Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!cornell!rochester!rutgers!att!alberta!ubc-cs!ubc-bdcvax!holm From: holm@ubc-bdcvax.UUCP (Terrence W. Holm) Newsgroups: comp.os.minix Subject: de(1) minix disk editor (part 2 of 5) Message-ID: <417@ubc-bdcvax.UUCP> Date: 3 Feb 89 01:28:36 GMT Lines: 982 --------------------------------------------------------------- X RIGHT r Move forward 32/1/4 bytes X X g Goto specified block X G Goto block indirectly X i Goto specified i-node X I Filename to i-node X X / Search X n Next occurrence X p Previous address X X h Help X EOF q Quit X m Minix shell X X v Visual mode (w b m) X o Output base (h d o b) X X c Change file name X w Write ASCII block X W Write block exactly X X x Extract lost directory entry X X Extract lost file blocks X X s Store word X X XNOTES X When entering a line in response to a prompt from de(1) X there are a couple of editing characters available. The X previous character may be erased by typing ^H and the X whole line may be erased by typing ^U. ENTER terminates X the input. If DELETE or a non-ASCII character is typed X then the command requesting the input is aborted. X X The commands 'G', 's' and 'X' will only function if X the current visual display mode is "word". The commands X 'i', 'I' and 'x' change the mode to "word" on X completion. The commands 'G' and '/' change the mode X to "block". These restrictions and automatic mode X conversions are intended to aid the user. X X The "map" mode uses special graphic characters, and X only functions if the user is at the console. X X De(1) generates warnings for illegal user input or if X erroneous data is found on the disk, for example a X corrupted magic number. Warnings appear in the middle X of the screen for two seconds, then the current page X is redrawn. Some minor errors, for example, setting X an unknown visual mode, simply ring the bell. Major X errors, for example i/o problems on the file system X device cause an immediate exit from de(1). X X The i-node and zone bit maps are read from the device X when de(1) starts up. These determine whether "in use" X or "not in use" is displayed in the status field at X the top of the screen. The bit maps are not re-read X while using de(1) and will become out-of-date if X observing a mounted file system. X X De(1) requires termcap definitions for "cm" and "cl". X "so" and "se" will also be used if available. The ANSI X strings generated by the keypad arrows are recognized, X as well as any single character codes defined by "ku", X "kd", "kl" and "kr". X X XSEE ALSO X dd(1), df(1), fdisk(1), fsck(1), mkfs(1), od(1), readfs(1), X unlink(2) X X Andrew S. Tanenbaum. "Operating Systems: Design and X Implementation", Section 5.6: "Overview of the Minix X File System". X XAUTHOR X Terrence W. Holm / echo x - de.c sed '/^X/s///' > de.c << '/' X/****************************************************************/ X/* */ X/* de.c */ X/* */ X/* Main loop of the "Disk editor". */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-15 Terrence W. Holm */ X/****************************************************************/ X X X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include X#include X#include X X#include "de.h" X Xstatic char copyright[] = { "de (c) Terrence W. Holm 1989" }; X X X X/****************************************************************/ X/* */ X/* main() */ X/* */ X/* Initialize. Handle the "-r" recovery option if */ X/* specified, else enter the main processing loop. */ X/* */ X/****************************************************************/ X X Xvoid main( argc, argv ) X int argc; X char *argv[]; X X { X de_state s; X char *command_name = argv[0]; X int recover = 0; X X X s.device_mode = O_RDONLY; X X X /* Parse arguments */ X X if ( argc == 3 && strcmp( argv[1], "-r" ) == 0 ) X { X recover = 1; X --argc; X ++argv; X } X else if ( argc == 3 && strcmp( argv[1], "-w" ) == 0 ) X { X s.device_mode = O_RDWR; X --argc; X ++argv; X } X X if ( argc != 2 || *argv[1] == '-' ) X { X fprintf( stderr, "Usage: %s [-w] /dev/device\n", command_name ); X fprintf( stderr, " %s -r lost_file_name\n", command_name ); X exit( 1 ); X } X X X /* Set the effective id to the real id. This eliminates */ X /* any increase in privilege done by a set-uid bit on the */ X /* executable file. We want to be "root" for recovering */ X /* files, because we must be able to read the device. */ X /* However, in normal usage, de(1) should not let just */ X /* anyone look at a file system, thus we drop the privilege. */ X /* */ X /* NOTE: There is a security hole when using "-r" with a */ X /* set-uid de(1). Do not use set-uid root if there is any */ X /* way to externally access your Minix system. */ X X if ( ! recover ) X { X setuid( getuid() ); X setgid( getgid() ); X } X X X /* Set terminal characteristics, and ^C interrupt handler */ X X Save_Term(); X X if ( signal( SIGINT, SIG_IGN ) != SIG_IGN ) X { X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X } X X Set_Term(); X X if ( ! Init_Termcap() ) X Error( "Requires a termcap entry" ); X X X X /* Get the device file name. If recovering, also open an output file. */ X X if ( recover ) X { X char *dir_name; X char *file_name; X struct stat device_stat; X struct stat tmp_stat; X X /* Split the path name into a directory and a file name. */ X X if ( strlen(argv[1]) > MAX_STRING ) X Error( "Path name too long" ); X X if ( ! Path_Dir_File( argv[1], &dir_name, &file_name ) ) X Error( "Recover aborted" ); X X /* Find the device holding the directory. */ X X if ( (s.device_name = File_Device( dir_name )) == NULL ) X Error( "Recover aborted" ); X X X /* The output file will be in /tmp with the same file name. */ X X strcpy( s.file_name, TMP ); X strcat( s.file_name, "/" ); X strcat( s.file_name, file_name ); X X X /* Make sure /tmp is not on the same device as the file we */ X /* are trying to recover (we don't want to use up the free */ X /* i-node and blocks before we get a chance to recover them). */ X X if ( stat( s.device_name, &device_stat ) == -1 ) X Error( "Can not stat(2) device %s", s.device_name ); X X if ( stat( TMP, &tmp_stat ) == -1 ) X Error( "Can not stat(2) directory %s", TMP ); X X if ( device_stat.st_rdev == tmp_stat.st_dev ) X Error( "Will not recover files on the same device as %s", TMP ); X X if ( access( s.file_name, F_OK ) == 0 ) X Error( "Will not overwrite file %s", s.file_name ); X X X /* Open the output file. */ X X if ( (s.file_f = fopen( s.file_name, "w" )) == NULL ) X Error( "Can not open file %s", s.file_name ); X X /* Don't let anyone else look at the recovered file */ X X chmod( s.file_name, 0700 ); X X /* If running as root then change the owner of the */ X /* restored file. If not running as root then the */ X /* chown(2) will fail. */ X X chown( s.file_name, getuid(), getgid() ); X } X else X { X s.device_name = argv[1]; X s.file_name[ 0 ] = '\0'; X } X X X /* Open the device file. */ X X { X struct stat device_stat; X off_t size; X X if ( stat( s.device_name, &device_stat ) == -1 ) X Error( "Can not find file %s", s.device_name ); X X if ( (device_stat.st_mode & S_IFMT) != S_IFBLK && X (device_stat.st_mode & S_IFMT) != S_IFREG ) X Error( "Can only edit block special or regular files" ); X X X if ( (s.device_d = open( s.device_name, s.device_mode )) == -1 ) X Error( "Can not open %s", s.device_name ); X X if ( (size = lseek( s.device_d, 0L, SEEK_END )) == -1 ) X Error( "Error seeking %s", s.device_name ); X X if ( size % K != 0 ) X Warning( "Device size is not a multiple of 1024" ); X } X X X /* Initialize the rest of the state record */ X X s.mode = WORD; X s.output_base = 10; X s.search_string[ 0 ] = '\0'; X X { X int i; X X for ( i = 0; i < MAX_PREV; ++i ) X { X s.prev_addr[ i ] = 0L; X s.prev_mode[ i ] = WORD; X } X } X X X sync(); X X Read_Super_Block( &s ); X X Read_Bit_Maps( &s ); X X s.address = 0L; X X X X /* Recover mode basically performs an 'x' and an 'X' */ X X if ( recover ) X { X ino_t inode = Find_Deleted_Entry( &s, argv[1] ); X off_t size; X X if ( inode == 0 ) X { X unlink( s.file_name ); X Error( "Recover aborted" ); X } X X s.address = ( (long) s.first_data - s.inode_blocks ) * K X + (long) (inode - 1) * INODE_SIZE; X X Read_Block( &s, s.buffer ); X X X /* Have found the lost i-node, now extract the blocks. */ X X if ( (size = Recover_Blocks( &s )) == -1L ) X { X unlink( s.file_name ); X Error( "Recover aborted" ); X } X X Reset_Term(); X X printf( "Recovered %ld bytes, written to file %s\n", size, s.file_name ); X X exit( 0 ); X } X X X /* Enter the main loop, first time redraw the screen */ X { X int rc = REDRAW; X X X do X { X if ( rc == REDRAW ) X { X Read_Block( &s, s.buffer ); X Draw_Screen( &s ); X s.last_addr = s.address; X Draw_Pointers( &s ); X } X X else if ( rc == REDRAW_POINTERS ) X { X s.offset = s.address & ~ K_MASK; X Draw_Pointers( &s ); X } X X else if ( rc == ERROR ) X { X Erase_Prompt(); X putchar( BELL ); X } X } while ( (rc = Process( &s, Arrow_Esc(Get_Char()) )) != EOF ); X } X X X /* If there is an open output file that was never written to */ X /* then remove its directory entry. This occurs when no 'w' */ X /* or 'W' command occurred between a 'c' command and exiting */ X /* the program. */ X X if ( s.file_name[0] != '\0' && ! s.file_written ) X unlink( s.file_name ); X X X Reset_Term(); /* Restore terminal characteristics */ X X exit( 0 ); X } X X X X X X X/****************************************************************/ X/* */ X/* Process( state, input_char ) */ X/* */ X/* Determine the function requested by the */ X/* input character. Returns OK, REDRAW, */ X/* REDRAW_POINTERS, ERROR or EOF. */ X/* */ X/****************************************************************/ X X Xint Process( s, c ) X de_state *s; X int c; X X { X switch ( c ) X { X case 'b' : /* Back up one block */ X case ESC_PGUP : X X if ( s->address == 0 ) X return( ERROR ); X X s->address = (s->address - K) & K_MASK; X X return( REDRAW ); X X X case 'B' : /* Back up to home */ X case ESC_HOME : X X if ( s->address == 0 ) X return( OK ); X X Push( s ); X X s->address = 0L; X X return( REDRAW ); X X X case 'c' : /* Change file name */ X X { X int rc = Get_Filename( s ); X X return( rc == OK ? REDRAW : rc ); X } X X X case 'd' : /* Down */ X case ESC_DOWN : X X { X s->last_addr = s->address; X X switch ( s->mode ) X { X case WORD : s->address += 2; X X if ( (s->address & PAGE_MASK) == 0 ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case BLOCK : s->address += 64; X X if ( (s->last_addr & K_MASK) != X (s->address & K_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case MAP : s->address += 256; X X return( REDRAW ); X X default : Error( "Internal fault (mode)" ); X } X } X X X case 'f' : /* Forward one block */ X case ' ' : X case ESC_PGDN : X X if ( s->block == s->device_size - 1 ) X return( ERROR ); X X s->address = (s->address + K) & K_MASK; X X return( REDRAW ); X X X case 'F' : /* Forward to end */ X case ESC_END : X X { X off_t last_block = ( (long) s->device_size - 1 ) * K; X X if ( s->address == last_block ) X return( OK ); X X Push( s ); X X s->address = last_block; X X return( REDRAW ); X } X X X case 'g' : /* Goto block */ X X { X unsigned block; X X if ( Get_Count( "Block?", &block ) ) X { X if ( block >= s->zones ) X { X Warning( "Block number too large" ); X return( REDRAW ); X } X X Push( s ); X X s->address = (long) block * K; X X return( REDRAW ); X } X else X return( ERROR ); X } X X X case 'G' : /* Goto block indirect */ X X { X unsigned block = *( (unsigned *) &s->buffer[ s->offset ] ); X X if ( s->mode != WORD ) X { X Warning( "Must be in visual mode \"word\"" ); X return( REDRAW ); X } X X if ( block >= s->zones ) X { X Warning( "Block number too large" ); X return( REDRAW ); X } X X Push( s ); X X s->mode = BLOCK; X s->address = (long) block * K; X X return( REDRAW ); X } X X X case 'h' : /* Help */ X case '?' : X X Draw_Help_Screen( s ); X X Wait_For_Key(); X X return( REDRAW ); X X X case 'i' : /* Goto i-node */ X X { X ino_t inode; X X if ( Get_Count( "I-node?", &inode ) ) X { X if ( inode < 1 || inode > s->inodes ) X { X Warning( "Illegal i-node number" ); X return( REDRAW ); X } X X Push( s ); X X s->mode = WORD; X s->address = ( (long) s->first_data - s->inode_blocks ) * K X + (long) (inode - 1) * INODE_SIZE; X X return( REDRAW ); X } X else X return( ERROR ); X } X X X case 'I' : /* Filename to i-node */ X X { X ino_t inode; X char *filename; X X Draw_Prompt( "File name?" ); X X filename = Get_Line(); X X if ( filename == NULL || filename[0] == '\0' ) X return( ERROR ); X X inode = Find_Inode( s, filename ); X X if ( inode ) X { X Push( s ); X X s->mode = WORD; X s->address = ( (long) s->first_data - s->inode_blocks ) * K X + (long) (inode - 1) * INODE_SIZE; X } X X return( REDRAW ); X } X X X case 'l' : /* Left */ X case ESC_LEFT : X X { X s->last_addr = s->address; X X switch ( s->mode ) X { X case WORD : s->address = s->address - 32; X X return( REDRAW ); X X case BLOCK : s->address -= 1; X X if ( (s->last_addr & K_MASK) != X (s->address & K_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case MAP : s->address -= 4; X X if ( (s->last_addr & ~ MAP_MASK) != X (s->address & ~ MAP_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X default : Error( "Internal fault (mode)" ); X } X } X X X case 'm' : /* Invoke a Minix shell */ X X Reset_Term(); X X Exec_Shell(); X X Set_Term(); X X return( REDRAW ); X X X case 'n' : /* Search for next */ X X { X off_t addr; X X if ( s->search_string[0] == '\0' ) X { X Warning( "No search string defined" ); X return( REDRAW ); X } X X Draw_Prompt( "Searching..." ); X X if ( (addr = Search( s, s->search_string )) == -1L ) X { X Warning( "Search string not found" ); X X Wait_For_Key(); X X return( REDRAW ); X } X X Push( s ); X s->address = addr; X X return( REDRAW ); X } X X X case 'o' : /* Set output base */ X X Draw_Prompt( "Output base?" ); X X switch ( Get_Char() ) X { X case 'h' : s->output_base = 16; X break; X X case 'd' : s->output_base = 10; X break; X X case 'o' : s->output_base = 8; X break; X X case 'b' : s->output_base = 2; X break; X X default : return( ERROR ); X } X X return( REDRAW ); X X X case 'p' : /* Previous address */ X X { X int i; X X s->address = s->prev_addr[ 0 ]; X s->mode = s->prev_mode[ 0 ]; X X for ( i = 0; i < MAX_PREV - 1; ++i ) X { X s->prev_addr[ i ] = s->prev_addr[ i + 1 ]; X s->prev_mode[ i ] = s->prev_mode[ i + 1 ]; X } X X return( REDRAW ); X } X X X case 'q' : /* Quit */ X case EOF : X case CTRL_D : X X return( EOF ); X X X case 'r' : /* Right */ X case ESC_RIGHT : X X { X s->last_addr = s->address; X X switch ( s->mode ) X { X case WORD : s->address += 32; X X return( REDRAW ); X X case BLOCK : s->address += 1; X X if ( (s->last_addr & K_MASK) != X (s->address & K_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case MAP : s->address += 4; X X if ( (s->last_addr & ~ MAP_MASK) != X (s->address & ~ MAP_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X default : Error( "Internal fault (mode)" ); X } X } X X X case 's' : /* Store word */ X X { X unsigned word; X X if ( s->mode != WORD ) X { X Warning( "Must be in visual mode \"word\"" ); X return( REDRAW ); X } X X if ( s->device_mode == O_RDONLY ) X { X Warning( "Use -w option to open device for writing" ); X return( REDRAW ); X } X X if ( Get_Count( "Store word?", &word ) ) X { X Write_Word( s, word ); X X return( REDRAW ); X } X else X return( ERROR ); X } X X X case 'u' : /* Up */ X case ESC_UP : X X { X s->last_addr = s->address; X X switch ( s->mode ) X { X case WORD : s->address -= 2; X X if ( (s->last_addr & PAGE_MASK) == 0 ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case BLOCK : s->address -= 64; X X if ( (s->last_addr & K_MASK) != X (s->address & K_MASK) ) X return( REDRAW ); X X return( REDRAW_POINTERS ); X X case MAP : s->address -= 256; X X return( REDRAW ); X X default : Error( "Internal fault (mode)" ); X } X } X X X case 'v' : /* Visual mode */ X X Draw_Prompt( "Visual mode?" ); X X switch ( Get_Char() ) X { X case 'w' : s->mode = WORD; X break; X X case 'b' : s->mode = BLOCK; X break; X X case 'm' : { X char *tty = ttyname( 0 ); X X if ( tty == NULL || X strcmp( tty, "/dev/tty0" ) != 0 ) X Warning( "Must be at console" ); X else X s->mode = MAP; X X break; X } X X default : return( ERROR ); X } X X return( REDRAW ); X X X case 'w' : /* Write ASCII block */ X X if ( s->file_name[0] == '\0' ) X { X int rc = Get_Filename( s ); X X if ( rc != OK ) X return( rc ); X } X X /* We have a successfully opened file */ X X /* Eliminate non-ASCII characters */ X { X int i; X char buf[ K ]; X char *from = s->buffer; X char *to = buf; X X for ( i = 0; i < K; ++i, ++from ) X { X *to = *from & 0x7f; X X if ( *to != '\0' && *to != '\177' ) X ++to; X } X X if ( fwrite( buf, 1, to - buf, s->file_f ) != to - buf ) X Warning( "Problem writing out buffer" ); X X s->file_written = 1; X X return( REDRAW ); X } X X X case 'W' : /* Write block exactly */ X X if ( s->file_name[0] == '\0' ) X { X int rc = Get_Filename( s ); X X if ( rc != OK ) X return( rc ); X } X X /* We have a successfully opened file */ X X if ( fwrite( s->buffer, 1, K, s->file_f ) != K ) X Warning( "Problem writing out buffer" ); X X s->file_written = 1; X X return( REDRAW ); X X X case 'x' : /* eXtract lost entry */ X X { X ino_t inode; X char *filename; X X Draw_Prompt( "Lost file name?" ); X X filename = Get_Line(); X X if ( filename == NULL || filename[0] == '\0' ) X return( ERROR ); X X inode = Find_Deleted_Entry( s, filename ); X X if ( inode ) X { X Push( s ); X X s->mode = WORD; X s->address = ( (long) s->first_data - s->inode_blocks ) * K X + (long) (inode - 1) * INODE_SIZE; X } X X return( REDRAW ); X } X X X case 'X' : /* eXtract lost blocks */ X X { X int rc; X off_t size; X X if ( s->mode != WORD ) X { X Warning( "Must be in visual mode \"word\"" ); X return( REDRAW ); X } X X X /* Force a new output file name. */ X X if ( (rc = Get_Filename( s )) != OK ) X return( rc ); X X X Draw_Strings( s ); X X Erase_Prompt(); X Draw_Prompt( "Recovering..." ); X X if ( (size = Recover_Blocks( s )) == -1L ) X unlink( s->file_name ); ---------------------------------------------------------------