add directory study

This commit is contained in:
gohigh
2024-02-19 00:25:23 -05:00
parent b1306b38b1
commit f3774e2f8c
4001 changed files with 2285787 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
CFLAGS = -F -D_MINIX -D_POSIX_SOURCE
DEOBJ = de.s de_stdin.s de_stdout.s de_diskio.s de_recover.s
de: $(DEOBJ)
cc -i -T. $(DEOBJ) -o de
chmem =30000 de
echo "chmod 755 de; chown bin de; chgrp bin de"
$(DEOBJ): de.h
clean:
@rm -f *.bak *.s de

View File

@@ -0,0 +1,11 @@
CFLAGS = -O -DATARI_ST
DEOBJ = de.o de_stdin.o de_stdout.o de_diskio.o de_recover.o
de: $(DEOBJ)
cc $(DEOBJ) -o de
chmem =20000 de
chmod 4755 de
chown root de
chgrp bin de
$(DEOBJ): de.h

View File

@@ -0,0 +1,156 @@
de - A Minix Disk Editor
Terrence W. Holm, Jan. 1989
INTRODUCTION
The de(1) disk editor allows a system administrator to
look at and modify a Minix file system device. Commands
allow movement throughout a file system device, displaying
information in a couple of formats, writing blocks from
the device onto another file, and rewriting words on the
disk.
A few changes to the Minix file system aid recovering files.
I-node numbers are retained in directory entries now (they
get moved to the end). And all the i-node information is not
zeroed-out when a file is unlinked. So, after a file is
accidently rm(1)'ed, you can find the old i-node, and then
manually (or automatically) go to each of the freed blocks
and write them to a new file.
USES FOR THE DISK EDITOR
1) EDUCATION. Students can look at a file system in
a painless manner. For example you don't have to
use od(1) to look at the zone numbers in i-nodes.
A simple assignment is to change the size of an un-mounted
floppy disk file system from 360 to 300 blocks. (A more
difficult assignment is to explain why this works, even
though fsck(1) and df(1) do not report the correct number
of free blocks. :-)
2) ADMINISTRATION. You can visually check inconsistencies
reported by fsck(1) before letting fsck(1) fix them.
You can change any word on the disk, this greatly simplifies
editing file system information. For example, changing the
size of a block special device is actually fun, no more
"blind" writing to your partitions.
Bit maps can be displayed with 2048 "bits" per screen,
(on the IBM/PC console), see how your zones are allocated!
3) RECOVERING LOST FILES. You can search a disk for an ASCII
string, once found, the block can be written out to a file.
A one line change to fs/path.c allows users to get the i-node
number for a file after it has been removed from a directory.
Another couple lines changed in the file system keep the
i-node information available until the i-node is reused
(normally this information is zeroed out when an i-node is
released.) This allows a de(1) user to go to a released
i-node, get all the block numbers, go to these blocks and
write them back to a new file.
The whole recovery process is automated by running "de -r file".
So, IF a file is unlink(2)'ed (eg. "rm file"), AND IF no one
allocates a new i-node or block in the mean-time, THEN you
can recover the file.
RECOVERY SECURITY
Normally Minix hard disk partitions are r/w only by the super-user,
and floppy disks are r/w by anyone. This means that only "root"
can look at hard disk partitions, but others can use de(1) to play
with their floppy disks.
When recovering files ("de -r file"), a user requires access to
the major file system partitions. This can be done by:
(a) Give everyone access to the hard disks. DON'T DO THIS, it
defeats all the file system protection we already have.
(b) Make de(1) set-uid "root". This is the way to go, IF you
are running a Minix system that has NO ACCESS from the
outside. This allows anyone to execute "de -r file", but only
root to use "de /dev/hd3". De(1) does some checking when
retrieving lost blocks, eg. making sure they really are
free blocks and making sure the user owned the i-node.
BUT, file system information has been lost when the file
was unlink(2)'ed, so de(1) can not be 100% sure that a
recovered block really belonged to the user. THIS IS A
SECURITY HOLE. [Since the only access to my machine is from
observable terminals and their associated humans, I run
de(1) as set-uid root.]
(c) Keep the disks rw-------, and don't set-uid de(1). This
means that only the super-user can recover lost files.
So, if you accidently "rm", you must tell the system
administrator to "su" and recover your file, (be sure to
inform the other users to stop whatever they are doing
until the file is restored).
INSTALLATION
- Install de.1 in /usr/man/cat1.
- Install the files: Makefile, README, de.h, de.c, de_stdin.c,
de_stdout.c, de_diskio.c and de_recover.c in commands/de.
Add -F and -T. to the Makefile, if necessary.
- "make" de(1). If a header file is not found, don't worry:
You probably have it somewhere, just link it to what de(1)
is looking for. This program also requires the subroutine
tolower(3), see EFTH MINIX report #50, if you don't have it.
- Do you really want set-uid root on de?
- Patch the files fs/path.c, fs/link.c and fs/open.c. If
you don't patch the file system then the recover option
"-r" and associated commands ('x' and 'X') will not work,
but de(1) is still functional and useful.
- "make" a new fs, using -DRECOVER. Rebuild a boot diskette.
USING DE(1) FOR THE FIRST TIME
De(1) starts up in "word" mode at block 0 of the specified
device. Hit the PGDN (or space bar) a few times, observing
all the information on the screen. Each PGUP/PGDN moves to
the next 1024 byte block, (de(1) only knows about 1 block per
zone file systems). Note that "word" mode only displays 32
bytes at a time, so you are only observing the first 32 bytes
in the first few blocks when you skip using PGDN.
Now go back to block 3, (zone bit map), using "g 3 ENTER".
Change to "map" mode "v m", and then use the down arrow key
to check each 2 Megs in the zone bit map.
Now change to "block" mode using "v b". And go to some data
block, eg. "g 1000 ENTER". Use PGUP/PGDN to see what data
is in each nearby block.
Remember 'h' gives you a help page.
Try some more commands, for example: 'END', 'I', '/'.
(Note: searching through a whole disk under Minix takes a
long time: 30-60 seconds per megabyte, depending on your
machine, drive and controller, [Minix is embarrassingly slow].)
Don't worry about looking at a mounted device, you must specify
the "-w" option before the 's' command is operational, and
this command is the only one which will try to modify the
contents of the device.
MINIX-ST
Please contact me if you are interesting in attempting a port
to MINIX-ST.

View File

@@ -0,0 +1,323 @@
/****************************************************************/
/* */
/* de.h */
/* */
/* Definitions for the "Disk editor". */
/* */
/****************************************************************/
/* origination 1989-Jan-15 Terrence W. Holm */
/****************************************************************/
/****************************************************************/
/* */
/* de(1) */
/* */
/* This is the MINIX disk editor. It allows the user to */
/* observe and modify a file system. It can also be used */
/* to recover unlink(2)'ed files */
/* */
/* See the de(1) man page. */
/* */
/****************************************************************/
/****************************************************************/
/* */
/* de Copyright Terrence W. Holm 1989 */
/* */
/* This program was written for users of the Minix operating */
/* system, and in the spirit of other public domain software */
/* written for said system, this source code is made available */
/* at no cost to everyone. I assume no responsibility for */
/* damage to file systems caused by this program. */
/* */
/* This program (one .h, five .c's and a "man" page) may be */
/* copied and/or modified subject to (1) no charge must be */
/* made for distribution, other than for the medium, (2) all */
/* modified sources must be clearly marked as such, (3) all */
/* sources must carry this copyright. */
/* */
/****************************************************************/
/****************************************************************/
/* */
/* files */
/* */
/* de.h Definitions */
/* de.c The main loop */
/* de_stdin.c Character input routines */
/* de_stdout.c Output routines */
/* de_diskio.c File system read/write */
/* de_recover.c File restoration routines */
/* */
/* de.1 "Man" page */
/* Makefile For "make" */
/* README Installation help */
/* */
/* */
/* fs/path.c was modified to support the 'x' command. */
/* fs/link.c and fs/open.c were changed for 'X'. */
/* */
/****************************************************************/
/* General constants */
#define MAX_STRING 60 /* For all input lines */
#define MAX_PREV 8 /* For 'p' command */
#define SEARCH_BUFFER (4*K) /* For '/' and 'n' */
/* Files */
#define TMP "/tmp" /* For "-r" output */
#define DEV "/dev" /* Where devices are */
/* a.out header constants (see a.out.h, if you have it) */
#if (CHIP == INTEL)
#define A_OUT 0x0301
#define SPLIT 0x0420
#endif
#if (CHIP == M68000)
#define A_OUT 0x0301
#define SPLIT 0x0B20
#endif
/* Each buffer is 1k. In WORD mode 16 words (32 bytes) can be */
/* displayed at once. In BLOCK mode 1K bytes can be displayed. */
/* In MAP mode 2048 bits (256 bytes) are displayed. */
#define K 1024 /* STD_BLK */
#define K_MASK (~(K-1)) /* Round to K boundary */
#define K_SHIFT 10 /* Ie. 1<<10 = K */
#define PAGE_MASK 0x1f /* Word mode: 32 bytes */
#define PAGE_SHIFT 5 /* Ie. 1<<5 = 32 */
#define MAP_BITS_PER_BLOCK (8 * K) /* 1k block, 8192 bits */
#define MAP_MASK 0xff /* 256 bytes/screen */
/* Terminal i/o codes */
#define CTRL_D '\004' /* ASCII ^D */
#define BELL '\007' /* ASCII bell code */
#define BS '\010' /* ASCII back space */
#define CTRL_U '\025' /* ASCII ^U */
#define ESCAPE '\033' /* ASCII escape code */
#define DEL '\177' /* ASCII delete code */
/* Input escape codes generated by the Minix console. */
/* Format: ESC [ X. */
#define ESC_HOME ('H' + 0x80)
#define ESC_UP ('A' + 0x80)
#define ESC_PGUP ('V' + 0x80)
#define ESC_LEFT ('D' + 0x80)
#define ESC_5 ('G' + 0x80)
#define ESC_RIGHT ('C' + 0x80)
#define ESC_END ('Y' + 0x80)
#define ESC_DOWN ('B' + 0x80)
#define ESC_PGDN ('U' + 0x80)
#define ESC_PLUS ('T' + 0x80)
#define ESC_MINUS ('S' + 0x80)
/* Graphic box codes - only applicable for console display */
/* in visual mode "map". */
#if (CHIP == INTEL)
#define BOX_CLR ' ' /* Empty box */
#define BOX_ALL '\333' /* Filled box */
#define BOX_TOP '\337' /* Filled upper half */
#define BOX_BOT '\334' /* Filled lower half */
#endif
#if (CHIP == M68000)
/* Please change these. */
#define BOX_CLR ' ' /* Empty box */
#define BOX_ALL '=' /* Filled box */
#define BOX_TOP '-' /* Filled upper half */
#define BOX_BOT '_' /* Filled lower half */
#endif
/* Move positions for the output display. */
#define STATUS_COLUMN 2
#define STATUS_LINE 0
#define BLOCK_COLUMN 4
#define BLOCK_LINE 4
#define INFO_COLUMN 30
#define INFO_LINE BLOCK_LINE
#define PROMPT_COLUMN 0
#define PROMPT_LINE 23
#define WARNING_COLUMN 10
#define WARNING_LINE 10
/* Values returned by Process() and Get_Filename() */
#define OK 0 /* No update required */
#define REDRAW 1 /* Redraw whole screen */
#define REDRAW_POINTERS 2 /* Redraw just ptrs */
#define ERROR 3 /* Beep */
/* Visual modes */
#define WORD 1
#define BLOCK 2
#define MAP 3
typedef struct de_state /* State of disk ed. */
{
/* Information from super block */
unsigned inodes; /* Number of i-nodes */
unsigned zones; /* Total # of blocks */
unsigned inode_maps; /* I-node map blocks */
unsigned zone_maps; /* Zone map blocks */
unsigned inode_blocks; /* I-node blocks */
unsigned first_data; /* Total non-data blks */
unsigned inodes_in_map; /* Bits in i-node map */
unsigned zones_in_map; /* Bits in zone map */
/* Information from map blocks */
char inode_map[ I_MAP_SLOTS * K ];
char zone_map[ ZMAP_SLOTS * K ];
/* Information for current block */
off_t address; /* Current address */
off_t last_addr; /* For erasing ptrs */
unsigned block; /* Current block (1K) */
unsigned offset; /* Offset within block */
char buffer[ K ];
/* Display state */
int mode; /* WORD, BLOCK or MAP */
int output_base; /* 2, 8, 10, or 16 */
/* Search information */
char search_string[ MAX_STRING + 1 ]; /* For '/' and 'n' */
off_t prev_addr[ MAX_PREV ]; /* For 'p' command */
int prev_mode[ MAX_PREV ];
/* File information */
char *device_name; /* From command line */
int device_d;
int device_mode; /* O_RDONLY or O_RDWR */
unsigned device_size; /* Number of blocks */
char file_name[ MAX_STRING + 1 ]; /* For 'w' and 'W' */
FILE *file_f;
int file_written; /* Flag if written to */
} de_state;
/* Forward references for external routines */
/* libc.a */
struct passwd *getpwuid();
struct group *getgrgid();
char *ctime();
char *getenv();
char *tgetstr();
char *tgoto();
char *ttyname();
FILE *fopen();
off_t lseek();
/* de.c */
void main();
int Process();
void Push();
int Get_Filename();
int Get_Count();
int Str_Int();
int In_Use();
ino_t Find_Inode();
void Exec_Shell();
void Sigint();
void Error();
/* de_stdin.c */
void Save_Term();
void Set_Term();
void Reset_Term();
int Get_Char();
char *Get_Line();
int Arrow_Esc();
/* de_stdout.c */
int Init_Termcap();
void Goto();
void Draw_Help_Screen();
void Wait_For_Key();
void Draw_Prompt();
void Erase_Prompt();
void Draw_Screen();
void Draw_Strings();
void Block_Type();
void Draw_Words();
void Draw_Info();
void Draw_Block();
void Draw_Map();
void Draw_Pointers();
void Draw_Offset();
void Word_Pointers();
void Block_Pointers();
void Map_Pointers();
void Print_Number();
void Print_Ascii();
void Warning();
/* de_diskio.c */
void Read_Disk();
void Read_Block();
void Read_Super_Block();
void Read_Bit_Maps();
off_t Search();
void Write_Word();
/* de_recover.c */
int Path_Dir_File();
char *File_Device();
ino_t Find_Deleted_Entry();
off_t Recover_Blocks();
#undef printf /* Because fs/const.h */
/* defines it. */

View File

@@ -0,0 +1,326 @@
/****************************************************************/
/* */
/* de_diskio.c */
/* */
/* Reading and writing to a file system device. */
/* */
/****************************************************************/
/* origination 1989-Jan-15 Terrence W. Holm */
/****************************************************************/
#include <minix/config.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <minix/const.h>
#include <minix/type.h>
#include "../../fs/const.h"
#include "../../fs/type.h"
#include "../../fs/super.h"
#include "de.h"
/****************************************************************/
/* */
/* Read_Disk( state, block_addr, buffer ) */
/* */
/* Reads a 1k block at "block_addr" into "buffer". */
/* */
/****************************************************************/
void Read_Disk( s, block_addr, buffer )
de_state *s;
off_t block_addr;
char *buffer;
{
if ( lseek( s->device_d, block_addr, SEEK_SET ) == -1 )
Error( "Error seeking %s", s->device_name );
if ( read( s->device_d, buffer, K ) != K )
Error( "Error reading %s", s->device_name );
}
/****************************************************************/
/* */
/* Read_Block( state, buffer ) */
/* */
/* Reads a 1k block from "state->address" into */
/* "buffer". Checks "address", and updates */
/* "block" and "offset". */
/* */
/****************************************************************/
void Read_Block( s, buffer )
de_state *s;
char *buffer;
{
off_t end_addr = (long) s->device_size * K - 1;
off_t block_addr;
if ( s->address < 0 )
s->address = 0L;
if ( s->address > end_addr )
s->address = end_addr;
/* The address must be rounded off for */
/* certain visual display modes. */
if ( s->mode == WORD )
s->address &= ~1L;
else if ( s->mode == MAP )
s->address &= ~3L;
block_addr = s->address & K_MASK;
s->block = (unsigned) (block_addr >> K_SHIFT);
s->offset = (unsigned) (s->address - block_addr);
Read_Disk( s, block_addr, buffer );
}
/****************************************************************/
/* */
/* Read_Super_Block( state ) */
/* */
/* Read and check the super block. */
/* */
/****************************************************************/
void Read_Super_Block( s )
de_state *s;
{
struct super_block *super = (struct super_block *) s->buffer;
Read_Disk( s, (long) 1 * K, s->buffer );
s->inodes = super->s_ninodes;
s->zones = super->s_nzones;
s->inode_maps = bitmapsize(1 + s->inodes);
s->zone_maps = bitmapsize(s->zones);
s->inode_blocks = (s->inodes + INODES_PER_BLOCK - 1) / INODES_PER_BLOCK;
s->first_data = 2 + s->inode_maps + s->zone_maps + s->inode_blocks;
s->inodes_in_map = s->inodes + 1;
s->zones_in_map = s->zones + 1 - s->first_data;
/*
if ( s->zones != s->device_size )
Warning( "Zone count does not equal device size" );
*/
s->device_size = s->zones;
if ( s->inode_maps != super->s_imap_blocks )
Warning( "Corrupted inode map count in super block" );
if ( s->zone_maps != super->s_zmap_blocks )
Warning( "Corrupted zone map count in super block" );
if ( s->first_data != super->s_firstdatazone )
Warning( "Corrupted first data zone count in super block" );
if ( super->s_log_zone_size != 0 )
Warning( "Can not handle multiple blocks per zone" );
if ( super->s_magic != SUPER_MAGIC )
Warning( "Corrupted magic number in super block" );
}
/****************************************************************/
/* */
/* Read_Bit_Maps( state ) */
/* */
/* Read in the i-node and zone bit maps from the */
/* specified file system device. */
/* */
/****************************************************************/
void Read_Bit_Maps( s )
de_state *s;
{
int i;
if ( s->inode_maps > I_MAP_SLOTS || s->zone_maps > ZMAP_SLOTS )
{
Warning( "Super block specifies too many bit map blocks" );
return;
}
for ( i = 0; i < s->inode_maps; ++i )
{
Read_Disk( s, (long) (2 + i) * K, &s->inode_map[ i * K ] );
}
for ( i = 0; i < s->zone_maps; ++i )
{
Read_Disk( s, (long) (2 + s->inode_maps + i) * K, &s->zone_map[ i * K ] );
}
}
/****************************************************************/
/* */
/* Search( state, string ) */
/* */
/* Search from the current address for the ASCII */
/* "string" on the device. */
/* */
/****************************************************************/
off_t Search( s, string )
de_state *s;
char *string;
{
off_t address = s->address + 1;
off_t last_addr = address;
char buffer[ SEARCH_BUFFER ];
int offset;
int tail_length = strlen( string ) - 1;
int count = SEARCH_BUFFER;
int last_offset;
for ( ; count == SEARCH_BUFFER; address += SEARCH_BUFFER - tail_length )
{
if ( lseek( s->device_d, address, SEEK_SET ) == -1 )
Error( "Error seeking %s", s->device_name );
if ( (count = read( s->device_d, buffer, SEARCH_BUFFER)) == -1 )
Error( "Error reading %s", s->device_name );
if ( address - last_addr >= 500L * K )
{
putchar( '.' );
fflush( stdout );
last_addr += 500L * K;
}
last_offset = count - tail_length;
for ( offset = 0; offset < last_offset; ++offset )
{
register char c = buffer[ offset ];
if ( c == *string )
{
char *tail_buffer = &buffer[ offset + 1 ];
char *tail_string = string + 1;
do
{
if ( *tail_string == '\0' )
return( address + offset );
}
while ( *tail_buffer++ == *tail_string++ );
}
} /* end for ( offset ) */
} /* end for ( address ) */
return( -1L );
}
/****************************************************************/
/* */
/* Write_Word( state, word ) */
/* */
/* Write a word at address. */
/* */
/****************************************************************/
void Write_Word( s, word )
de_state *s;
unsigned word;
{
if ( s->address & 01 )
Error( "Internal fault (unaligned address)" );
if ( lseek( s->device_d, s->address, SEEK_SET ) == -1 )
Error( "Error seeking %s", s->device_name );
if ( write( s->device_d, (char *) &word, 2 ) != 2 )
Error( "Error writing %s", s->device_name );
}
/* The next routine is copied from fsck.c and mkfs.c... (Re)define some
* things for consistency. This sharing should be done better in a library
* library routine. In the library routine, the shifts should be replaced
* by multiplications and divisions by MAP_BITS_PER_BLOCK since log2 of
* this is too painful to get right.
*/
#define BITMAPSHIFT 13 /* = log2(MAP_BITS_PER_BLOCK) */
#define bit_nr unsigned
/* Convert from bit count to a block count. The usual expression
*
* (nr_bits + (1 << BITMAPSHIFT) - 1) >> BITMAPSHIFT
*
* doesn't work because of overflow.
*
* Other overflow bugs, such as the expression for N_ILIST overflowing when
* s_inodes is just over INODES_PER_BLOCK less than the maximum+1, are not
* fixed yet, because that number of inodes is silly.
*/
int bitmapsize(nr_bits)
bit_nr nr_bits;
{
int nr_blocks;
nr_blocks = nr_bits >> BITMAPSHIFT;
if ((nr_blocks << BITMAPSHIFT) < nr_bits)
++nr_blocks;
return(nr_blocks);
}

View File

@@ -0,0 +1,589 @@
/****************************************************************/
/* */
/* de_recover.c */
/* */
/* File restoration routines. */
/* */
/****************************************************************/
/* origination 1989-Jan-21 Terrence W. Holm */
/* handle "holes" 1989-Jan-28 Terrence W. Holm */
/****************************************************************/
#include <minix/config.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <minix/type.h>
#include <blocksize.h>
#include "../../fs/const.h"
#include "../../fs/type.h"
#include "de.h"
/****************************************************************/
/* */
/* Path_Dir_File( path_name, dir_name, file_name ) */
/* */
/* Split "path_name" into a directory name and */
/* a file name. */
/* */
/* Zero is returned on error conditions. */
/* */
/****************************************************************/
int Path_Dir_File( path_name, dir_name, file_name )
char *path_name;
char **dir_name;
char **file_name;
{
char *p;
static char directory[ MAX_STRING + 1 ];
static char filename[ MAX_STRING + 1 ];
if ( (p = strrchr( path_name, '/' )) == NULL )
{
strcpy( directory, "." );
strcpy( filename, path_name );
}
else
{
*directory = '\0';
strncat( directory, path_name, p - path_name );
strcpy( filename, p + 1 );
}
if ( *directory == '\0' )
strcpy( directory, "/" );
if ( *filename == '\0' )
{
Warning( "A file name must follow the directory name" );
return( 0 );
}
*dir_name = directory;
*file_name = filename;
return( 1 );
}
/****************************************************************/
/* */
/* File_Device( file_name ) */
/* */
/* Return the name of the file system device */
/* containing the file "file_name". */
/* */
/* This is used if the "-r" option was specified. */
/* In this case we have only been given a file */
/* name, and must determine which file system */
/* device to open. */
/* */
/* NULL is returned on error conditions. */
/* */
/****************************************************************/
char *File_Device( file_name )
char *file_name;
{
struct stat file_stat;
struct stat device_stat;
int dev_d;
struct direct entry;
static char device_name[ DIRSIZ + 1 ];
if ( access( file_name, R_OK ) != 0 )
{
Warning( "Can not find %s", file_name );
return( NULL );
}
if ( stat( file_name, &file_stat ) == -1 )
{
Warning( "Can not stat(2) %s", file_name );
return( NULL );
}
/* Open /dev for reading */
if ( (dev_d = open( DEV, O_RDONLY )) == -1 )
{
Warning( "Can not read %s", DEV );
return( NULL );
}
while ( read( dev_d, (char *) &entry, sizeof(struct direct) )
== sizeof(struct direct) )
{
if ( entry.d_ino == 0 )
continue;
strcpy( device_name, DEV );
strcat( device_name, "/" );
strncat( device_name, entry.d_name, DIRSIZ );
if ( stat( device_name, &device_stat ) == -1 )
continue;
if ( (device_stat.st_mode & S_IFMT) != S_IFBLK )
continue;
if ( file_stat.st_dev == device_stat.st_rdev )
{
close( dev_d );
return( device_name );
}
}
close( dev_d );
Warning( "The device containing file %s is not in %s", file_name, DEV );
return( NULL );
}
/****************************************************************/
/* */
/* Find_Deleted_Entry( state, path_name ) */
/* */
/* Split "path_name" into a directory name and */
/* a file name. Then search the directory for */
/* an entry that would match the deleted file */
/* name. (Deleted entries have a zero i-node */
/* number, but the original i-node number is */
/* placed at the end of the file name.) */
/* */
/* If successful an i-node number is returned, */
/* else zero is returned. */
/* */
/****************************************************************/
ino_t Find_Deleted_Entry( s, path_name )
de_state *s;
char *path_name;
{
char *dir_name;
char *file_name;
/* Check if the file exists */
if ( access( path_name, F_OK ) == 0 )
{
Warning( "File has not been deleted" );
return( 0 );
}
/* Split the path name into a directory and a file name */
if ( ! Path_Dir_File( path_name, &dir_name, &file_name ) )
return( 0 );
/* Check to make sure the user has read permission on */
/* the directory. */
if ( access( dir_name, R_OK ) != 0 )
{
Warning( "Can not find %s", dir_name );
return( 0 );
}
/* Make sure "dir_name" is really a directory. */
{
struct stat dir_stat;
if ( stat( dir_name, &dir_stat ) == -1 ||
(dir_stat.st_mode & S_IFMT) != S_IFDIR )
{
Warning( "Can not find directory %s", dir_name );
return( 0 );
}
}
/* Make sure the directory is on the current */
/* file system device. */
if ( Find_Inode( s, dir_name ) == 0 )
return( 0 );
/* Open the directory and search for the lost file name. */
{
int dir_d;
int count;
struct direct entry;
if ( (dir_d = open( dir_name, O_RDONLY )) == -1 )
{
Warning( "Can not read directory %s", dir_name );
return( 0 );
}
while ( (count = read( dir_d, (char *) &entry, sizeof(struct direct) ))
== sizeof(struct direct) )
{
if ( entry.d_ino == 0 &&
strncmp( file_name, entry.d_name, DIRSIZ - sizeof(ino_t) ) == 0 )
{
ino_t inode = *( (ino_t *) &entry.d_name[ DIRSIZ - sizeof(ino_t) ] );
close( dir_d );
if ( inode < 1 || inode > s->inodes )
{
Warning( "Illegal i-node number" );
return( 0 );
}
return( inode );
}
}
close( dir_d );
if ( count == 0 )
Warning( "Can not find a deleted entry for %s", file_name );
else
Warning( "Problem reading directory %s", dir_name );
return( 0 );
}
}
/****************************************************************/
/* */
/* Recover_Blocks( state ) */
/* */
/* Try to recover all the blocks for the i-node */
/* currently pointed to by "s->address". The */
/* i-node and all of the blocks must be marked */
/* as FREE in the bit maps. The owner of the */
/* i-node must match the current real user name. */
/* */
/* "Holes" in the original file are maintained. */
/* This allows moving sparse files from one device */
/* to another. */
/* */
/* On any error -1L is returned, otherwise the */
/* size of the recovered file is returned. */
/* */
/* */
/* NOTE: Once a user has read access to a device, */
/* there is a security hole, as we lose the */
/* normal file system protection. For convenience, */
/* de(1) is sometimes set-uid root, this allows */
/* anyone to use the "-r" option. When recovering, */
/* Recover_Blocks() can only superficially check */
/* the validity of a request. */
/* */
/****************************************************************/
off_t Recover_Blocks( s )
de_state *s;
{
d_inode *inode = (d_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ];
int node = (s->address - (s->first_data - s->inode_blocks) * K) /
INODE_SIZE + 1;
if ( s->block < s->first_data - s->inode_blocks ||
s->block >= s->first_data )
{
Warning( "Not in an inode block" );
return( -1L );
}
/* Is this a valid, but free i-node? */
if ( node > s->inodes )
{
Warning( "Not an inode" );
return( -1L );
}
if ( In_Use(node, s->inode_map) )
{
Warning( "I-node is in use" );
return( -1L );
}
/* Only recover files that belonged to the real user. */
{
uid_t real_uid = getuid();
struct passwd *user = getpwuid( real_uid );
if ( real_uid != SU_UID && real_uid != inode->i_uid )
{
Warning( "I-node did not belong to user %s", user ? user->pw_name : "" );
return( -1L );
}
}
/* Recover all the blocks of the file. */
{
off_t file_size = inode->i_size;
int i;
/* Up to 7 block pointers are stored in the i-node. */
for ( i = 0; i < NR_DZONE_NUM; ++i )
{
if ( file_size == 0 )
return( inode->i_size );
if ( ! Data_Block( s, inode->i_zone[ i ], &file_size ) )
return( -1L );
}
if ( file_size == 0 )
return( inode->i_size );
/* An indirect block can contain up to 512 more block pointers. */
if ( ! Indirect( s, inode->i_zone[ NR_DZONE_NUM ], &file_size, 0 ) )
return( -1L );
if ( file_size == 0 )
return( inode->i_size );
/* A double indirect block can contain up to 512 indirect blocks. */
if ( ! Indirect( s, inode->i_zone[ NR_DZONE_NUM+1 ], &file_size, 1 ) )
return( -1L );
if ( file_size == 0 )
return( inode->i_size );
Error( "Internal fault (file_size != 0)" );
}
}
/* Indirect( state, block, &file_size, double )
*
* Recover all the blocks pointed to by the indirect block
* "block", up to "file_size" bytes. If "double" is true,
* then "block" is a double-indirect block pointing to 512
* indirect blocks.
*
* If a "hole" is encountered, then just seek ahead in the
* output file.
*/
int Indirect( s, block, file_size, dblind )
de_state *s;
zone_nr block;
off_t *file_size;
int dblind;
{
zone_nr indirect[ NR_INDIRECTS ];
int i;
/* Check for a "hole". */
if ( block == NO_ZONE )
{
off_t skip = (off_t) NR_INDIRECTS * K;
if ( *file_size < skip || dblind )
{
Warning( "File has a hole at the end" );
return( 0 );
}
if ( fseek( s->file_f, skip, SEEK_CUR ) == -1 )
{
Warning( "Problem seeking %s", s->file_name );
return( 0 );
}
*file_size -= skip;
return( 1 );
}
/* Not a "hole". Recover indirect block, if not in use. */
if ( ! Free_Block( s, block ) )
return( 0 );
Read_Disk( s, (long) block << K_SHIFT, indirect );
for ( i = 0; i < NR_INDIRECTS; ++i )
{
if ( *file_size == 0 )
return( 1 );
if ( dblind )
{
if ( ! Indirect( s, indirect[ i ], file_size, 0 ) )
return( 0 );
}
else
{
if ( ! Data_Block( s, indirect[ i ], file_size ) )
return( 0 );
}
}
return( 1 );
}
/* Data_Block( state, block, &file_size )
*
* If "block" is free then write Min(file_size, k)
* bytes from it onto the current output file.
*
* If "block" is zero, this means that a 1k "hole"
* is in the file. The recovered file maintains
* the reduced size by not allocating the block.
*
* The file size is decremented accordingly.
*/
int Data_Block( s, block, file_size )
de_state *s;
zone_nr block;
off_t *file_size;
{
char buffer[ K ];
off_t block_size = *file_size > K ? K : *file_size;
/* Check for a "hole". */
if ( block == NO_ZONE )
{
if ( block_size < K )
{
Warning( "File has a hole at the end" );
return( 0 );
}
if ( fseek( s->file_f, block_size, SEEK_CUR ) == -1 )
{
Warning( "Problem seeking %s", s->file_name );
return( 0 );
}
*file_size -= block_size;
return( 1 );
}
/* Block is not a "hole". Copy it to output file, if not in use. */
if ( ! Free_Block( s, block ) )
return( 0 );
Read_Disk( s, (long) block << K_SHIFT, buffer );
if ( fwrite( buffer, 1, (int) block_size, s->file_f ) != (int) block_size )
{
Warning( "Problem writing %s", s->file_name );
return( 0 );
}
*file_size -= block_size;
return( 1 );
}
/* Free_Block( state, block )
*
* Make sure "block" is a valid data block number, and it
* has not been allocated to another file.
*/
int Free_Block( s, block )
de_state *s;
zone_nr block;
{
if ( block < s->first_data || block >= s->zones )
{
Warning( "Illegal block number" );
return( 0 );
}
if ( In_Use( block - s->first_data + 1, s->zone_map ) )
{
Warning( "Encountered an \"in use\" data block" );
return( 0 );
}
return( 1 );
}

View File

@@ -0,0 +1,290 @@
/****************************************************************/
/* */
/* de_stdin.c */
/* */
/* Processing input from the "de" user. */
/* */
/****************************************************************/
/* origination 1989-Jan-15 Terrence W. Holm */
/****************************************************************/
#include <minix/config.h>
#include <sys/types.h>
#include <sgtty.h>
#include <signal.h>
#include <stdio.h>
#include "../../fs/const.h"
#include "de.h"
/****************************************************************/
/* */
/* Save_Term() */
/* */
/* Save the current terminal characteristics. */
/* */
/* */
/* Set_Term() */
/* */
/* Set up the terminal characteristics. */
/* */
/* */
/* Reset_Term() */
/* */
/* Restore the terminal characteristics. */
/* */
/****************************************************************/
static struct sgttyb saved_mode;
static struct tchars saved_chars;
void Timed_Out();
void Save_Term()
{
ioctl( 0, TIOCGETP, &saved_mode );
ioctl( 0, TIOCGETC, (struct sgttyb *) &saved_chars );
}
void Set_Term()
{
struct sgttyb mode;
struct tchars chars;
mode = saved_mode;
chars = saved_chars;
/* No tab expansion, no echo, don't map ^M to ^J, cbreak mode */
mode.sg_flags = mode.sg_flags & ~XTABS & ~ECHO & ~CRMOD | CBREAK;
/* Change the interrupt character to ^C */
chars.t_intrc = '\003';
ioctl( 0, TIOCSETP, &mode );
ioctl( 0, TIOCSETC, (struct sgttyb *) &chars );
}
void Reset_Term()
{
ioctl( 0, TIOCSETP, &saved_mode );
ioctl( 0, TIOCSETC, (struct sgttyb *) &saved_chars );
}
/****************************************************************/
/* */
/* Get_Char() */
/* */
/* Return the next input character. Escape */
/* sequences are mapped to special codes. */
/* */
/****************************************************************/
int Get_Char()
{
int c;
static int unget_char = EOF;
/* Flush the output to the screen before waiting */
/* for input from the user. */
fflush( stdout );
if ( unget_char == EOF )
{
while ( (c = Timed_Get_Char( 60 * 60 )) < EOF )
printf( "%c", BELL );
}
else
{
c = unget_char;
unget_char = EOF;
}
if ( c == EOF )
return( EOF );
if ( c != ESCAPE )
return( c );
if ( (c = Timed_Get_Char( 1 )) <= EOF )
return( ESCAPE );
if ( c != '[' )
{
unget_char = c;
return( ESCAPE );
}
if ( (c = Timed_Get_Char( 1 )) <= EOF )
{
unget_char = '[';
return( ESCAPE );
}
return( c | 0x80 ); /* Flag ESC [ x */
}
int Timed_Get_Char( time )
int time;
{
char c;
int count;
signal( SIGALRM, Timed_Out );
alarm( time );
count = read( 0, &c, 1 );
alarm( 0 );
if ( count <= 0 )
return( EOF + count );
return( c & 0x7f );
}
/****************************************************************/
/* */
/* Get_Line() */
/* */
/* Read a line from the user. Returns a pointer */
/* to a local buffer, or NULL if DEL or a non- */
/* ASCII character was typed. Processes ^H and */
/* ^U. ^M terminates the input. */
/* */
/****************************************************************/
char *Get_Line()
{
int c;
int i;
static char line[ MAX_STRING + 1 ];
for ( i = 0; i <= MAX_STRING; ++i )
{
c = Get_Char();
if ( c == EOF || c == DEL || (c & 0x80) )
return( NULL );
if ( c == BS )
{
if ( --i >= 0 )
{
printf( "\b \b" );
--i;
}
}
else if ( c == CTRL_U )
{
for ( --i; i >= 0; --i )
printf( "\b \b" );
}
else if ( c == '\r' )
{
line[ i ] = '\0';
return( line );
}
else if ( i < MAX_STRING )
{
line[ i ] = c;
Print_Ascii( c );
}
else /* Line buffer is full, don't add any more to it. */
{
putchar( BELL );
--i;
}
}
Error( "Internal fault (line buffer overflow)" );
}
/****************************************************************/
/* */
/* Arrow_Esc( char ) */
/* */
/* If the keyboard does not generate Ansi escape */
/* codes for the arrow keys, but does generate */
/* single byte control codes, then map these */
/* codes to the special characters we are using */
/* to denote the Ansi escape codes. */
/* */
/****************************************************************/
extern char Kup; /* (ku) - Up arrow key */
extern char Kdown; /* (kd) - Down arrow key */
extern char Kleft; /* (kl) - Left arrow key */
extern char Kright; /* (kr) - Right arrow key */
int Arrow_Esc( c )
int c;
{
if ( c == Kup )
return( ESC_UP );
if ( c == Kdown )
return( ESC_DOWN );
if ( c == Kleft )
return( ESC_LEFT );
if ( c == Kright )
return( ESC_RIGHT );
return( c );
}
void Timed_Out()
{}