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 4 of 5) Message-ID: <419@ubc-bdcvax.UUCP> Date: 3 Feb 89 01:32:05 GMT Lines: 982 --------------------------------------------------------------- X X Xoff_t Search( s, string ) X de_state *s; X char *string; X X { X off_t address = s->address + 1; X off_t last_addr = address; X char buffer[ SEARCH_BUFFER ]; X int offset; X int tail_length = strlen( string ) - 1; X int count = SEARCH_BUFFER; X int last_offset; X X X for ( ; count == SEARCH_BUFFER; address += SEARCH_BUFFER - tail_length ) X { X if ( lseek( s->device_d, address, SEEK_SET ) == -1 ) X Error( "Error seeking %s", s->device_name ); X X if ( (count = read( s->device_d, buffer, SEARCH_BUFFER)) == -1 ) X Error( "Error reading %s", s->device_name ); X X X if ( address - last_addr >= 500L * K ) X { X putchar( '.' ); X fflush( stdout ); X X last_addr += 500L * K; X } X X X last_offset = count - tail_length; X X for ( offset = 0; offset < last_offset; ++offset ) X { X register char c = buffer[ offset ]; X X if ( c == *string ) X { X char *tail_buffer = &buffer[ offset + 1 ]; X char *tail_string = string + 1; X X do X { X if ( *tail_string == '\0' ) X return( address + offset ); X } X while ( *tail_buffer++ == *tail_string++ ); X } X } /* end for ( offset ) */ X } /* end for ( address ) */ X X return( -1L ); X } X X X X X X X/****************************************************************/ X/* */ X/* Write_Word( state, word ) */ X/* */ X/* Write a word at address. */ X/* */ X/****************************************************************/ X X Xvoid Write_Word( s, word ) X de_state *s; X unsigned word; X X { X if ( s->address & 01 ) X Error( "Internal fault (unaligned address)" ); X X if ( lseek( s->device_d, s->address, SEEK_SET ) == -1 ) X Error( "Error seeking %s", s->device_name ); X X if ( write( s->device_d, &word, 2 ) != 2 ) X Error( "Error writing %s", s->device_name ); X } / echo x - de_recover.c sed '/^X/s///' > de_recover.c << '/' X/****************************************************************/ X/* */ X/* de_recover.c */ X/* */ X/* File restoration routines. */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-21 Terrence W. Holm */ X/* handle "holes" 1989-Jan-28 Terrence W. Holm */ X/****************************************************************/ X X X#include X#include X#include X#include X#include X#include X#include X#include X X#include X#include X#include X#include X X#include "de.h" X X X X X/****************************************************************/ X/* */ X/* Path_Dir_File( path_name, dir_name, file_name ) */ X/* */ X/* Split "path_name" into a directory name and */ X/* a file name. */ X/* */ X/* Zero is returned on error conditions. */ X/* */ X/****************************************************************/ X X Xint Path_Dir_File( path_name, dir_name, file_name ) X char *path_name; X char **dir_name; X char **file_name; X X { X char *p; X static char directory[ MAX_STRING + 1 ]; X static char filename[ MAX_STRING + 1 ]; X X X if ( (p = strrchr( path_name, '/' )) == NULL ) X { X strcpy( directory, "." ); X strcpy( filename, path_name ); X } X else X { X *directory = '\0'; X strncat( directory, path_name, p - path_name ); X strcpy( filename, p + 1 ); X } X X if ( *directory == '\0' ) X strcpy( directory, "/" ); X X if ( *filename == '\0' ) X { X Warning( "A file name must follow the directory name" ); X return( 0 ); X } X X *dir_name = directory; X *file_name = filename; X X return( 1 ); X } X X X X X X X/****************************************************************/ X/* */ X/* File_Device( file_name ) */ X/* */ X/* Return the name of the file system device */ X/* containing the file "file_name". */ X/* */ X/* This is used if the "-r" option was specified. */ X/* In this case we have only been given a file */ X/* name, and must determine which file system */ X/* device to open. */ X/* */ X/* NULL is returned on error conditions. */ X/* */ X/****************************************************************/ X X X Xchar *File_Device( file_name ) X char *file_name; X X { X struct stat file_stat; X struct stat device_stat; X int dev_d; X struct direct entry; X static char device_name[ DIRSIZ + 1 ]; X X X if ( access( file_name, R_OK ) != 0 ) X { X Warning( "Can not find %s", file_name ); X return( NULL ); X } X X X if ( stat( file_name, &file_stat ) == -1 ) X { X Warning( "Can not stat(2) %s", file_name ); X return( NULL ); X } X X X /* Open /dev for reading */ X X if ( (dev_d = open( DEV, O_RDONLY )) == -1 ) X { X Warning( "Can not read %s", DEV ); X return( NULL ); X } X X X while ( read( dev_d, (char *) &entry, sizeof(struct direct) ) X == sizeof(struct direct) ) X { X if ( entry.d_ino == 0 ) X continue; X X strcpy( device_name, DEV ); X strcat( device_name, "/" ); X strncat( device_name, entry.d_name, DIRSIZ ); X X if ( stat( device_name, &device_stat ) == -1 ) X continue; X X if ( (device_stat.st_mode & S_IFMT) != S_IFBLK ) X continue; X X if ( file_stat.st_dev == device_stat.st_rdev ) X { X close( dev_d ); X return( device_name ); X } X } X X close( dev_d ); X X Warning( "The device containing file %s is not in %s", file_name, DEV ); X X return( NULL ); X } X X X X X X X/****************************************************************/ X/* */ X/* Find_Deleted_Entry( state, path_name ) */ X/* */ X/* Split "path_name" into a directory name and */ X/* a file name. Then search the directory for */ X/* an entry that would match the deleted file */ X/* name. (Deleted entries have a zero i-node */ X/* number, but the original i-node number is */ X/* placed at the end of the file name.) */ X/* */ X/* If successful an i-node number is returned, */ X/* else zero is returned. */ X/* */ X/****************************************************************/ X X Xino_t Find_Deleted_Entry( s, path_name ) X de_state *s; X char *path_name; X X { X char *dir_name; X char *file_name; X X X /* Check if the file exists */ X X if ( access( path_name, F_OK ) == 0 ) X { X Warning( "File has not been deleted" ); X return( 0 ); X } X X X /* Split the path name into a directory and a file name */ X X if ( ! Path_Dir_File( path_name, &dir_name, &file_name ) ) X return( 0 ); X X X /* Check to make sure the user has read permission on */ X /* the directory. */ X X if ( access( dir_name, R_OK ) != 0 ) X { X Warning( "Can not find %s", dir_name ); X return( 0 ); X } X X X /* Make sure "dir_name" is really a directory. */ X { X struct stat dir_stat; X X if ( stat( dir_name, &dir_stat ) == -1 || X (dir_stat.st_mode & S_IFMT) != S_IFDIR ) X { X Warning( "Can not find directory %s", dir_name ); X return( 0 ); X } X } X X X /* Make sure the directory is on the current */ X /* file system device. */ X X if ( Find_Inode( s, dir_name ) == 0 ) X return( 0 ); X X X /* Open the directory and search for the lost file name. */ X { X int dir_d; X int count; X struct direct entry; X X if ( (dir_d = open( dir_name, O_RDONLY )) == -1 ) X { X Warning( "Can not read directory %s", dir_name ); X return( 0 ); X } X X while ( (count = read( dir_d, (char *) &entry, sizeof(struct direct) )) X == sizeof(struct direct) ) X { X if ( entry.d_ino == 0 && X strncmp( file_name, entry.d_name, DIRSIZ - sizeof(ino_t) ) == 0 ) X { X ino_t inode = *( (ino_t *) &entry.d_name[ DIRSIZ - sizeof(ino_t) ] ); X X close( dir_d ); X X if ( inode < 1 || inode > s->inodes ) X { X Warning( "Illegal i-node number" ); X return( 0 ); X } X X return( inode ); X } X } X X close( dir_d ); X X if ( count == 0 ) X Warning( "Can not find a deleted entry for %s", file_name ); X else X Warning( "Problem reading directory %s", dir_name ); X X return( 0 ); X } X } X X X X X X X/****************************************************************/ X/* */ X/* Recover_Blocks( state ) */ X/* */ X/* Try to recover all the blocks for the i-node */ X/* currently pointed to by "s->address". The */ X/* i-node and all of the blocks must be marked */ X/* as FREE in the bit maps. The owner of the */ X/* i-node must match the current real user name. */ X/* */ X/* "Holes" in the original file are maintained. */ X/* This allows moving sparse files from one device */ X/* to another. */ X/* */ X/* On any error -1L is returned, otherwise the */ X/* size of the recovered file is returned. */ X/* */ X/* */ X/* NOTE: Once a user has read access to a device, */ X/* there is a security hole, as we lose the */ X/* normal file system protection. For convenience, */ X/* de(1) is sometimes set-uid root, this allows */ X/* anyone to use the "-r" option. When recovering, */ X/* Recover_Blocks() can only superficially check */ X/* the validity of a request. */ X/* */ X/****************************************************************/ X X Xoff_t Recover_Blocks( s ) X de_state *s; X X { X d_inode *inode = (d_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ]; X int node = (s->address - (s->first_data - s->inode_blocks) * K) / X INODE_SIZE + 1; X X X if ( s->block < s->first_data - s->inode_blocks || X s->block >= s->first_data ) X { X Warning( "Not in an inode block" ); X return( -1L ); X } X X X /* Is this a valid, but free i-node? */ X X if ( node > s->inodes ) X { X Warning( "Not an inode" ); X return( -1L ); X } X X if ( In_Use(node, s->inode_map) ) X { X Warning( "I-node is in use" ); X return( -1L ); X } X X X /* Only recover files that belonged to the real user. */ X X { X uid real_uid = getuid(); X struct passwd *user = getpwuid( real_uid ); X X if ( real_uid != SU_UID && real_uid != inode->i_uid ) X { X Warning( "I-node did not belong to user %s", user ? user->pw_name : "" ); X return( -1L ); X } X } X X X /* Recover all the blocks of the file. */ X X { X off_t file_size = inode->i_size; X int i; X X X /* Up to 7 block pointers are stored in the i-node. */ X X for ( i = 0; i < NR_DZONE_NUM; ++i ) X { X if ( file_size == 0 ) X return( inode->i_size ); X X if ( ! Data_Block( s, inode->i_zone[ i ], &file_size ) ) X return( -1L ); X } X X if ( file_size == 0 ) X return( inode->i_size ); X X X /* An indirect block can contain up to 512 more block pointers. */ X X if ( ! Indirect( s, inode->i_zone[ NR_DZONE_NUM ], &file_size, 0 ) ) X return( -1L ); X X if ( file_size == 0 ) X return( inode->i_size ); X X X /* A double indirect block can contain up to 512 indirect blocks. */ X X if ( ! Indirect( s, inode->i_zone[ NR_DZONE_NUM+1 ], &file_size, 1 ) ) X return( -1L ); X X if ( file_size == 0 ) X return( inode->i_size ); X X Error( "Internal fault (file_size != 0)" ); X } X } X X X X X X X/* Indirect( state, block, &file_size, double ) X * X * Recover all the blocks pointed to by the indirect block X * "block", up to "file_size" bytes. If "double" is true, X * then "block" is a double-indirect block pointing to 512 X * indirect blocks. X * X * If a "hole" is encountered, then just seek ahead in the X * output file. X */ X X Xint Indirect( s, block, file_size, double ) X de_state *s; X zone_nr block; X off_t *file_size; X int double; X X { X zone_nr indirect[ NR_INDIRECTS ]; X int i; X X /* Check for a "hole". */ X X if ( block == NO_ZONE ) X { X off_t skip = (off_t) NR_INDIRECTS * K; X X if ( *file_size < skip || double ) X { X Warning( "File has a hole at the end" ); X return( 0 ); X } X X if ( fseek( s->file_f, skip, SEEK_CUR ) == -1 ) X { X Warning( "Problem seeking %s", s->file_name ); X return( 0 ); X } X X *file_size -= skip; X return( 1 ); X } X X X /* Not a "hole". Recover indirect block, if not in use. */ X X if ( ! Free_Block( s, block ) ) X return( 0 ); X X X Read_Disk( s, (long) block << K_SHIFT, indirect ); X X for ( i = 0; i < NR_INDIRECTS; ++i ) X { X if ( *file_size == 0 ) X return( 1 ); X X if ( double ) X { X if ( ! Indirect( s, indirect[ i ], file_size, 0 ) ) X return( 0 ); X } X else X { X if ( ! Data_Block( s, indirect[ i ], file_size ) ) X return( 0 ); X } X } X X return( 1 ); X } X X X X X X X/* Data_Block( state, block, &file_size ) X * X * If "block" is free then write Min(file_size, k) X * bytes from it onto the current output file. X * X * If "block" is zero, this means that a 1k "hole" X * is in the file. The recovered file maintains X * the reduced size by not allocating the block. X * X * The file size is decremented accordingly. X */ X X Xint Data_Block( s, block, file_size ) X de_state *s; X zone_nr block; X off_t *file_size; X X { X char buffer[ K ]; X off_t block_size = *file_size > K ? K : *file_size; X X X /* Check for a "hole". */ X X if ( block == NO_ZONE ) X { X if ( block_size < K ) X { X Warning( "File has a hole at the end" ); X return( 0 ); X } X X if ( fseek( s->file_f, block_size, SEEK_CUR ) == -1 ) X { X Warning( "Problem seeking %s", s->file_name ); X return( 0 ); X } X X *file_size -= block_size; X return( 1 ); X } X X X /* Block is not a "hole". Copy it to output file, if not in use. */ X X if ( ! Free_Block( s, block ) ) X return( 0 ); X X Read_Disk( s, (long) block << K_SHIFT, buffer ); X X X if ( fwrite( buffer, 1, (int) block_size, s->file_f ) != (int) block_size ) X { X Warning( "Problem writing %s", s->file_name ); X return( 0 ); X } X X *file_size -= block_size; X return( 1 ); X } X X X X X X X/* Free_Block( state, block ) X * X * Make sure "block" is a valid data block number, and it X * has not been allocated to another file. X */ X X Xint Free_Block( s, block ) X de_state *s; X zone_nr block; X X { X if ( block < s->first_data || block >= s->zones ) X { X Warning( "Illegal block number" ); X return( 0 ); X } X X if ( In_Use( block - s->first_data + 1, s->zone_map ) ) X { X Warning( "Encountered an \"in use\" data block" ); X return( 0 ); X } X X return( 1 ); X } X / echo x - de_stdin.c sed '/^X/s///' > de_stdin.c << '/' X/****************************************************************/ X/* */ X/* de_stdin.c */ X/* */ X/* Processing input from the "de" user. */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-15 Terrence W. Holm */ X/****************************************************************/ X X X#include X#include X#include X#include X X#include X X#include "de.h" X X X X/****************************************************************/ X/* */ X/* Save_Term() */ X/* */ X/* Save the current terminal characteristics. */ X/* */ X/* */ X/* Set_Term() */ X/* */ X/* Set up the terminal characteristics. */ X/* */ X/* */ X/* Reset_Term() */ X/* */ X/* Restore the terminal characteristics. */ X/* */ X/****************************************************************/ X X Xstatic struct sgttyb saved_mode; Xstatic struct tchars saved_chars; X X X Xvoid Save_Term() X X { X ioctl( 0, TIOCGETP, &saved_mode ); X ioctl( 0, TIOCGETC, &saved_chars ); X } X X X X Xvoid Set_Term() X X { X struct sgttyb mode; X struct tchars chars; X X mode = saved_mode; X chars = saved_chars; X X X /* No tab expansion, no echo, don't map ^M to ^J, cbreak mode */ X X mode.sg_flags = mode.sg_flags & ~XTABS & ~ECHO & ~CRMOD | CBREAK; X X X /* Change the interrupt character to ^C */ X X chars.t_intrc = '\003'; X X ioctl( 0, TIOCSETP, &mode ); X ioctl( 0, TIOCSETC, &chars ); X } X X X X Xvoid Reset_Term() X X { X ioctl( 0, TIOCSETP, &saved_mode ); X ioctl( 0, TIOCSETC, &saved_chars ); X } X X X X X X X/****************************************************************/ X/* */ X/* Get_Char() */ X/* */ X/* Return the next input character. Escape */ X/* sequences are mapped to special codes. */ X/* */ X/****************************************************************/ X X Xint Get_Char() X { X int c; X static int unget_char = EOF; X X X /* Flush the output to the screen before waiting */ X /* for input from the user. */ X X fflush( stdout ); X X if ( unget_char == EOF ) X { X while ( (c = Timed_Get_Char( 60 * 60 )) < EOF ) X printf( "%c", BELL ); X } X else X { X c = unget_char; X unget_char = EOF; X } X X if ( c == EOF ) X return( EOF ); X X if ( c != ESCAPE ) X return( c ); X X if ( (c = Timed_Get_Char( 1 )) <= EOF ) X return( ESCAPE ); X X if ( c != '[' ) X { X unget_char = c; X return( ESCAPE ); X } X X if ( (c = Timed_Get_Char( 1 )) <= EOF ) X { X unget_char = '['; X return( ESCAPE ); X } X X return( c | 0x80 ); /* Flag ESC [ x */ X } X X X X XTimed_Out() X {} X X X X Xint Timed_Get_Char( time ) X int time; X X { X char c; X int count; X X signal( SIGALRM, Timed_Out ); X X alarm( time ); X count = read( 0, &c, 1 ); X alarm( 0 ); X X if ( count <= 0 ) X return( EOF + count ); X X return( c & 0x7f ); X } X X X X X X X/****************************************************************/ X/* */ X/* Get_Line() */ X/* */ X/* Read a line from the user. Returns a pointer */ X/* to a local buffer, or NULL if DEL or a non- */ X/* ASCII character was typed. Processes ^H and */ X/* ^U. ^M terminates the input. */ X/* */ X/****************************************************************/ X X Xchar *Get_Line() X X { X int c; X int i; X static char line[ MAX_STRING + 1 ]; X X for ( i = 0; i <= MAX_STRING; ++i ) X { X c = Get_Char(); X X if ( c == EOF || c == DEL || (c & 0x80) ) X return( NULL ); X X if ( c == BS ) X { X if ( --i >= 0 ) X { X printf( "\b \b" ); X --i; X } X } X X else if ( c == CTRL_U ) X { X for ( --i; i >= 0; --i ) X printf( "\b \b" ); X } X X else if ( c == '\r' ) X { X line[ i ] = '\0'; X return( line ); X } X X else if ( i < MAX_STRING ) X { X line[ i ] = c; X Print_Ascii( c ); X } X X else /* Line buffer is full, don't add any more to it. */ X { X putchar( BELL ); X --i; X } X } X X Error( "Internal fault (line buffer overflow)" ); X } X X X X X X X/****************************************************************/ X/* */ X/* Arrow_Esc( char ) */ X/* */ X/* If the keyboard does not generate Ansi escape */ X/* codes for the arrow keys, but does generate */ X/* single byte control codes, then map these */ X/* codes to the special characters we are using */ X/* to denote the Ansi escape codes. */ X/* */ X/****************************************************************/ X X Xextern char Kup; /* (ku) - Up arrow key */ Xextern char Kdown; /* (kd) - Down arrow key */ Xextern char Kleft; /* (kl) - Left arrow key */ Xextern char Kright; /* (kr) - Right arrow key */ X X Xint Arrow_Esc( c ) X int c; X X { X if ( c == Kup ) X return( ESC_UP ); X X if ( c == Kdown ) X return( ESC_DOWN ); X X if ( c == Kleft ) X return( ESC_LEFT ); X X if ( c == Kright ) X return( ESC_RIGHT ); X X return( c ); X } / echo x - de_stdout.c sed '/^X/s///' > de_stdout.c << '/' X/****************************************************************/ X/* */ X/* de_stdout.c */ X/* */ X/* Displaying information from the "Disk editor". */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-15 Terrence W. Holm */ X/****************************************************************/ X ---------------------------------------------------------------