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 3 of 5) Message-ID: <418@ubc-bdcvax.UUCP> Date: 3 Feb 89 01:30:35 GMT Lines: 982 --------------------------------------------------------------- X X /* Force closure of output file. */ X X fclose( s->file_f ); X s->file_name[ 0 ] = '\0'; X X return( REDRAW ); X } X X X case '/' : /* Search */ X case ESC_PLUS : X X { X off_t addr; X char *string; X X Draw_Prompt( "Search string?" ); X X string = Get_Line(); X X if ( string == NULL ) X return( ERROR ); X X if ( string[0] != '\0' ) X { X strcpy( s->search_string, string ); X Draw_Strings( s ); X } X X else if ( s->search_string[0] == '\0' ) X { X Warning( "No search string defined" ); X return( REDRAW ); X } X X Erase_Prompt(); 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 X s->mode = BLOCK; X s->address = addr; X X return( REDRAW ); X } X X X default: X return( ERROR ); X } X } X X X X X X X/****************************************************************/ X/* */ X/* Push( state ) */ X/* */ X/* Push current address and mode, used by the */ X/* commands B, F, g, G, i, I, n, x and /. This */ X/* information is popped by the 'p' command. */ X/* */ X/****************************************************************/ X X Xvoid Push( s ) X de_state *s; X X { X int i; X X for ( i = MAX_PREV - 1; i > 0; --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 s->prev_addr[ 0 ] = s->address; X s->prev_mode[ 0 ] = s->mode; X } X X X X X X X/****************************************************************/ X/* */ X/* Get_Filename( state ) */ X/* */ X/* Read and check a filename. */ X/* */ X/****************************************************************/ X X Xint Get_Filename( s ) X de_state *s; X X { X char *filename; X char *name; X FILE *f; X X Draw_Prompt( "File name?" ); X X filename = Get_Line(); X X if ( filename == NULL || filename[0] == '\0' ) X return( ERROR ); X X X for ( name = filename; *name != '\0'; ++name ) X if ( ! isgraph( *name ) ) X { X Warning( "File name contains non-graphic characters" ); X return( REDRAW ); X } X X X if ( access( filename, F_OK ) == 0 ) X { X Warning( "Will not overwrite file %s", filename ); X return( REDRAW ); X } X X if ( (f = fopen( filename, "w" )) == NULL ) X { X Warning( "Can not open file %s", filename ); X return( REDRAW ); X } X X /* If there is already an open output file then */ X /* close it. If it was never written to then */ X /* remove its directory entry. */ X X if ( s->file_name[0] != '\0' ) X { X if ( ! s->file_written ) X unlink( s->file_name ); X X fclose( s->file_f ); X } X X strcpy( s->file_name, filename ); X s->file_f = f; X s->file_written = 0; X X return( OK ); X } X X X X X X X/****************************************************************/ X/* */ X/* Get_Count() */ X/* */ X/* Read and check a number. Returns non-zero */ X/* if successful. */ X/* */ X/****************************************************************/ X X Xint Get_Count( units, result ) X char *units; X int *result; X X { X char *number; X X Draw_Prompt( units ); X X number = Get_Line(); X X if ( number == NULL || number[0] == '\0' ) X return( 0 ); X X return( Str_Int( number, result ) ); X } X X X X X X X/****************************************************************/ X/* */ X/* Str_Int( string, &result ) */ X/* */ X/* Convert "string" to an int. Returns non-zero */ X/* if successful. Format: [-][0][x]{0-9}* */ X/* */ X/****************************************************************/ X X Xint Str_Int( str, result ) X char *str; X int *result; X X { X int negative = 0; X int base = 10; X int total = 0; X char c; X X while ( *str == ' ' ) X ++str; X X if ( *str == '-' ) X { X ++str; X negative = 1; X } X X if ( *str == '0' ) X { X ++str; X base = 8; X } X X if ( *str == 'x' || *str == 'X' ) X { X ++str; X base = 16; X } X X if ( *str == '\0' && base != 8 ) X return( 0 ); X X while ( (c = *str++) != '\0' ) X { X if ( c >= '0' && c <= '7' ) X total = total * base + c - '0'; X else if ( isdigit( c ) && base >= 10 ) X total = total * base + c - '0'; X else if ( isxdigit( c ) && base == 16 ) X total = total * base + tolower( c ) - 'a' + 10; X else X return( 0 ); X } X X *result = negative ? -total : total; X return( 1 ); X } X X X X X X X/****************************************************************/ X/* */ X/* In_Use( bit, map ) */ X/* */ X/* Is the bit set in the map? */ X/* */ X/****************************************************************/ X X Xint In_Use( bit, map ) X int bit; X char *map; X X { X return( map[bit >> 3] & (1 << (bit & 07)) ); X } X X X X X X X/****************************************************************/ X/* */ X/* Find_Inode( state, filename ) */ X/* */ X/* Find the i-node for the given file name. */ X/* */ X/****************************************************************/ X X Xino_t Find_Inode( s, filename ) X de_state *s; X char *filename; X X { X struct stat device_stat; X struct stat file_stat; X ino_t inode; X X X if ( fstat( s->device_d, &device_stat ) == -1 ) X Error( "Can not fstat(2) file system device" ); X X if ( stat( filename, &file_stat ) == -1 ) X { X Warning( "Can not find file %s", filename ); X return( 0 ); X } X X if ( device_stat.st_rdev != file_stat.st_dev ) X { X Warning( "File is not on device %s", s->device_name ); X return( 0 ); X } X X X inode = file_stat.st_ino; 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 X X X X/****************************************************************/ X/* */ X/* Exec_Shell() */ X/* */ X/* Fork off a sub-process to exec() the shell. */ X/* */ X/****************************************************************/ X X Xvoid Exec_Shell() X X { X int pid = fork(); X X if ( pid == -1 ) X return; X X X if ( pid == 0 ) X { X /* The child process */ X X extern char **environ; X char *shell = getenv( "SHELL" ); X X if ( shell == NULL ) X shell = "/bin/sh"; X X execle( shell, shell, (char *) 0, environ ); X X perror( shell ); X exit( 127 ); X } X X X /* The parent process: ignore signals, wait for sub-process */ X X signal( SIGINT, SIG_IGN ); X signal( SIGQUIT, SIG_IGN ); X X { X int status; X int w; X X while ( (w=wait(&status)) != pid && w != -1 ); X } X X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X X return; X } X X X X X X X/****************************************************************/ X/* */ X/* Sigint() */ X/* */ X/* Terminate the program on an interrupt (^C) */ X/* or quit (^\) signal. */ X/* */ X/****************************************************************/ X X Xvoid Sigint() X X { X Reset_Term(); /* Restore terminal characteristics */ X X putchar( '\n' ); X X exit( 1 ); X } X X X X X X X/****************************************************************/ X/* */ X/* Error( message, arg1, arg2 ) */ X/* */ X/* Print an error message on stderr. */ X/* */ X/****************************************************************/ X X Xvoid Error( message, arg1, arg2 ) X char *message; X char *arg1; X char *arg2; X X { X Reset_Term(); X X fprintf( stderr, "\nde: " ); X fprintf( stderr, message, arg1, arg2 ); X fprintf( stderr, "\n" ); X X exit( 1 ); X } / echo x - de.h sed '/^X/s///' > de.h << '/' X/****************************************************************/ X/* */ X/* de.h */ X/* */ X/* Definitions for the "Disk editor". */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-15 Terrence W. Holm */ X/****************************************************************/ X X X/****************************************************************/ X/* */ X/* de(1) */ X/* */ X/* This is the MINIX disk editor. It allows the user to */ X/* observe and modify a file system. It can also be used */ X/* to recover unlink(2)'ed files */ X/* */ X/* See the de(1) man page. */ X/* */ X/****************************************************************/ X X X/****************************************************************/ X/* */ X/* de Copyright Terrence W. Holm 1989 */ X/* */ X/* This program was written for users of the Minix operating */ X/* system, and in the spirit of other public domain software */ X/* written for said system, this source code is made available */ X/* at no cost to everyone. I assume no responsibility for */ X/* damage to file systems caused by this program. */ X/* */ X/* This program (one .h, five .c's and a "man" page) may be */ X/* copied and/or modified subject to (1) no charge must be */ X/* made for distribution, other than for the medium, (2) all */ X/* modified sources must be clearly marked as such, (3) all */ X/* sources must carry this copyright. */ X/* */ X/****************************************************************/ X X X/****************************************************************/ X/* */ X/* files */ X/* */ X/* de.h Definitions */ X/* de.c The main loop */ X/* de_stdin.c Character input routines */ X/* de_stdout.c Output routines */ X/* de_diskio.c File system read/write */ X/* de_recover.c File restoration routines */ X/* */ X/* de.1 "Man" page */ X/* Makefile For "make" */ X/* README Installation help */ X/* */ X/* */ X/* fs/path.c was modified to support the 'x' command. */ X/* fs/link.c and fs/open.c were changed for 'X'. */ X/* */ X/****************************************************************/ X X X X/* General constants */ X X#define MAX_STRING 60 /* For all input lines */ X#define MAX_PREV 8 /* For 'p' command */ X#define SEARCH_BUFFER (4*K) /* For '/' and 'n' */ X X X/* Files */ X X#define TMP "/tmp" /* For "-r" output */ X#define DEV "/dev" /* Where devices are */ X X X/* a.out header constants (see a.out.h, if you have it) */ X X#ifdef i8088 X#define A_OUT 0x0301 X#define SPLIT 0x0420 X#endif X X#ifdef ATARI_ST X#define A_OUT 0x0301 X#define SPLIT 0x0B20 X#endif X X X/* Each buffer is 1k. In WORD mode 16 words (32 bytes) can be */ X/* displayed at once. In BLOCK mode 1K bytes can be displayed. */ X/* In MAP mode 2048 bits (256 bytes) are displayed. */ X X#define K 1024 /* STD_BLK */ X#define K_MASK (~(K-1)) /* Round to K boundary */ X#define K_SHIFT 10 /* Ie. 1<<10 = K */ X#define PAGE_MASK 0x1f /* Word mode: 32 bytes */ X#define PAGE_SHIFT 5 /* Ie. 1<<5 = 32 */ X#define MAP_BITS_PER_BLOCK (8 * K) /* 1k block, 8192 bits */ X#define MAP_MASK 0xff /* 256 bytes/screen */ X X X X/* Terminal i/o codes */ X X#define CTRL_D '\004' /* ASCII ^D */ X#define BELL '\007' /* ASCII bell code */ X#define BS '\010' /* ASCII back space */ X#define CTRL_U '\025' /* ASCII ^U */ X#define ESCAPE '\033' /* ASCII escape code */ X#define DEL '\177' /* ASCII delete code */ X X X/* Input escape codes generated by the Minix console. */ X/* Format: ESC [ X. */ X X#define ESC_HOME ('H' + 0x80) X#define ESC_UP ('A' + 0x80) X#define ESC_PGUP ('V' + 0x80) X#define ESC_LEFT ('D' + 0x80) X#define ESC_5 ('G' + 0x80) X#define ESC_RIGHT ('C' + 0x80) X#define ESC_END ('Y' + 0x80) X#define ESC_DOWN ('B' + 0x80) X#define ESC_PGDN ('U' + 0x80) X#define ESC_PLUS ('T' + 0x80) X#define ESC_MINUS ('S' + 0x80) X X X/* Graphic box codes - only applicable for console display */ X/* in visual mode "map". */ X X#ifdef i8088 X#define BOX_CLR ' ' /* Empty box */ X#define BOX_ALL '\333' /* Filled box */ X#define BOX_TOP '\337' /* Filled upper half */ X#define BOX_BOT '\334' /* Filled lower half */ X#endif X X#ifdef ATARI_ST X/* Please change these. */ X#define BOX_CLR ' ' /* Empty box */ X#define BOX_ALL '=' /* Filled box */ X#define BOX_TOP '-' /* Filled upper half */ X#define BOX_BOT '_' /* Filled lower half */ X#endif X X X/* Move positions for the output display. */ X X#define STATUS_COLUMN 2 X#define STATUS_LINE 0 X#define BLOCK_COLUMN 4 X#define BLOCK_LINE 4 X#define INFO_COLUMN 30 X#define INFO_LINE BLOCK_LINE X#define PROMPT_COLUMN 0 X#define PROMPT_LINE 23 X#define WARNING_COLUMN 10 X#define WARNING_LINE 10 X X X X/* Values returned by Process() and Get_Filename() */ X X#define OK 0 /* No update required */ X#define REDRAW 1 /* Redraw whole screen */ X#define REDRAW_POINTERS 2 /* Redraw just ptrs */ X#define ERROR 3 /* Beep */ X X X/* Visual modes */ X X#define WORD 1 X#define BLOCK 2 X#define MAP 3 X X Xtypedef struct de_state /* State of disk ed. */ X { X /* Information from super block */ X X unsigned inodes; /* Number of i-nodes */ X unsigned zones; /* Total # of blocks */ X unsigned inode_maps; /* I-node map blocks */ X unsigned zone_maps; /* Zone map blocks */ X unsigned inode_blocks; /* I-node blocks */ X unsigned first_data; /* Total non-data blks */ X X unsigned inodes_in_map; /* Bits in i-node map */ X unsigned zones_in_map; /* Bits in zone map */ X X /* Information from map blocks */ X X char inode_map[ I_MAP_SLOTS * K ]; X char zone_map[ ZMAP_SLOTS * K ]; X X /* Information for current block */ X X off_t address; /* Current address */ X off_t last_addr; /* For erasing ptrs */ X unsigned block; /* Current block (1K) */ X unsigned offset; /* Offset within block */ X X char buffer[ K ]; X X /* Display state */ X X int mode; /* WORD, BLOCK or MAP */ X int output_base; /* 2, 8, 10, or 16 */ X X /* Search information */ X X char search_string[ MAX_STRING + 1 ]; /* For '/' and 'n' */ X off_t prev_addr[ MAX_PREV ]; /* For 'p' command */ X int prev_mode[ MAX_PREV ]; X X /* File information */ X X char *device_name; /* From command line */ X int device_d; X int device_mode; /* O_RDONLY or O_RDWR */ X unsigned device_size; /* Number of blocks */ X X char file_name[ MAX_STRING + 1 ]; /* For 'w' and 'W' */ X FILE *file_f; X int file_written; /* Flag if written to */ X X } de_state; X X X X/* Forward references for external routines */ X X/* libc.a */ X Xstruct passwd *getpwuid(); Xstruct group *getgrgid(); Xchar *ctime(); Xchar *getenv(); Xchar *tgetstr(); Xchar *tgoto(); Xchar *ttyname(); XFILE *fopen(); Xoff_t lseek(); X X X/* de.c */ X Xvoid main(); Xint Process(); X Xvoid Push(); Xint Get_Filename(); Xint Get_Count(); Xint Str_Int(); Xint In_Use(); Xino_t Find_Inode(); Xvoid Exec_Shell(); Xvoid Sigint(); Xvoid Error(); X X X/* de_stdin.c */ X Xvoid Save_Term(); Xvoid Set_Term(); Xvoid Reset_Term(); Xint Get_Char(); Xchar *Get_Line(); Xint Arrow_Esc(); X X X/* de_stdout.c */ X Xint Init_Termcap(); Xvoid Goto(); Xvoid Draw_Help_Screen(); Xvoid Wait_For_Key(); Xvoid Draw_Prompt(); Xvoid Erase_Prompt(); X Xvoid Draw_Screen(); Xvoid Draw_Strings(); Xvoid Block_Type(); Xvoid Draw_Words(); Xvoid Draw_Info(); Xvoid Draw_Block(); Xvoid Draw_Map(); X Xvoid Draw_Pointers(); Xvoid Draw_Offset(); Xvoid Word_Pointers(); Xvoid Block_Pointers(); Xvoid Map_Pointers(); X Xvoid Print_Number(); Xvoid Print_Ascii(); Xvoid Warning(); X X X/* de_diskio.c */ X Xvoid Read_Disk(); Xvoid Read_Block(); Xvoid Read_Super_Block(); Xvoid Read_Bit_Maps(); Xoff_t Search(); Xvoid Write_Word(); X X X/* de_recover.c */ X Xint Path_Dir_File(); Xchar *File_Device(); Xino_t Find_Deleted_Entry(); Xoff_t Recover_Blocks(); X X X#undef printf /* Because fs/const.h */ X /* defines it. */ X / echo x - de_diskio.c sed '/^X/s///' > de_diskio.c << '/' X/****************************************************************/ X/* */ X/* de_diskio.c */ X/* */ X/* Reading and writing to a file system device. */ X/* */ X/****************************************************************/ X/* origination 1989-Jan-15 Terrence W. Holm */ X/****************************************************************/ X X X#include X#include X#include X X#include X#include X#include X#include X#include X X#include "de.h" X X X X X/****************************************************************/ X/* */ X/* Read_Disk( state, block_addr, buffer ) */ X/* */ X/* Reads a 1k block at "block_addr" into "buffer". */ X/* */ X/****************************************************************/ X X Xvoid Read_Disk( s, block_addr, buffer ) X de_state *s; X off_t block_addr; X char *buffer; X X { X if ( lseek( s->device_d, block_addr, SEEK_SET ) == -1 ) X Error( "Error seeking %s", s->device_name ); X X if ( read( s->device_d, buffer, K ) != K ) X Error( "Error reading %s", s->device_name ); X } X X X X X X X/****************************************************************/ X/* */ X/* Read_Block( state, buffer ) */ X/* */ X/* Reads a 1k block from "state->address" into */ X/* "buffer". Checks "address", and updates */ X/* "block" and "offset". */ X/* */ X/****************************************************************/ X X Xvoid Read_Block( s, buffer ) X de_state *s; X char *buffer; X X { X off_t end_addr = (long) s->device_size * K - 1; X off_t block_addr; X X if ( s->address < 0 ) X s->address = 0L; X X if ( s->address > end_addr ) X s->address = end_addr; X X /* The address must be rounded off for */ X /* certain visual display modes. */ X X if ( s->mode == WORD ) X s->address &= ~1L; X else if ( s->mode == MAP ) X s->address &= ~3L; X X X block_addr = s->address & K_MASK; X X s->block = (unsigned) (block_addr >> K_SHIFT); X s->offset = (unsigned) (s->address - block_addr); X X Read_Disk( s, block_addr, buffer ); X } X X X X X X X/****************************************************************/ X/* */ X/* Read_Super_Block( state ) */ X/* */ X/* Read and check the super block. */ X/* */ X/****************************************************************/ X X Xvoid Read_Super_Block( s ) X de_state *s; X X { X struct super_block *super = (struct super_block *) s->buffer; X X Read_Disk( s, (long) 1 * K, s->buffer ); X X s->inodes = super->s_ninodes; X s->zones = super->s_nzones; X X s->inode_maps = (s->inodes + MAP_BITS_PER_BLOCK) / MAP_BITS_PER_BLOCK; X s->zone_maps = (s->zones + MAP_BITS_PER_BLOCK - 1) / MAP_BITS_PER_BLOCK; X /* Note: zone_maps may be too large, but we must calculate */ X /* it this way, because this is the way mkfs(1) does it. */ X X s->inode_blocks = (s->inodes + INODES_PER_BLOCK - 1) / INODES_PER_BLOCK; X s->first_data = 2 + s->inode_maps + s->zone_maps + s->inode_blocks; X X s->inodes_in_map = s->inodes + 1; X s->zones_in_map = s->zones + 1 - s->first_data; X X /* X if ( s->zones != s->device_size ) X Warning( "Zone count does not equal device size" ); X */ X X s->device_size = s->zones; X X if ( s->inode_maps != super->s_imap_blocks ) X Warning( "Corrupted inode map count in super block" ); X X if ( s->zone_maps != super->s_zmap_blocks ) X Warning( "Corrupted zone map count in super block" ); X X if ( s->first_data != super->s_firstdatazone ) X Warning( "Corrupted first data zone count in super block" ); X X if ( super->s_log_zone_size != 0 ) X Warning( "Can not handle multiple blocks per zone" ); X X if ( super->s_magic != SUPER_MAGIC ) X Warning( "Corrupted magic number in super block" ); X } X X X X X X X/****************************************************************/ X/* */ X/* Read_Bit_Maps( state ) */ X/* */ X/* Read in the i-node and zone bit maps from the */ X/* specified file system device. */ X/* */ X/****************************************************************/ X X Xvoid Read_Bit_Maps( s ) X de_state *s; X X { X int i; X X if ( s->inode_maps > I_MAP_SLOTS || s->zone_maps > ZMAP_SLOTS ) X { X Warning( "Super block specifies too many bit map blocks" ); X return; X } X X for ( i = 0; i < s->inode_maps; ++i ) X { X Read_Disk( s, (long) (2 + i) * K, &s->inode_map[ i * K ] ); X } X X for ( i = 0; i < s->zone_maps; ++i ) X { X Read_Disk( s, (long) (2 + s->inode_maps + i) * K, &s->zone_map[ i * K ] ); X } X } X X X X X X X/****************************************************************/ X/* */ X/* Search( state, string ) */ X/* */ X/* Search from the current address for the ASCII */ X/* "string" on the device. */ X/* */ X/****************************************************************/ ---------------------------------------------------------------