Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!bloom-beacon!apple!voder!pyramid!prls!philabs!micomvax!ncc!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 3) Message-ID: <408@ubc-bdcvax.UUCP> Date: 26 Jan 89 22:34:46 GMT Lines: 1502 -------------------------------------------------------------- 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 gres '^X' '' > 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 gres '^X' '' > 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/****************************************************************/ 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 gres '^X' '' > 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/****************************************************************/ 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/* On any error -1L is returned, otherwise the */ X/* size of the recovered file is returned. */ 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 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 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 * The file size is decremented accordingly. X */ X X Xint Data_Block( s, block, file_size ) X de_state *s; X zone_nr block; ----------------------------------------------------------