Initial revision
This commit is contained in:
Executable
+22
@@ -0,0 +1,22 @@
|
||||
# Makefile for de
|
||||
|
||||
CC = cc
|
||||
CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2
|
||||
|
||||
all: de
|
||||
|
||||
OBJS = de.o de_stdin.o de_stdout.o de_diskio.o de_recover.o
|
||||
|
||||
de: $(OBJS)
|
||||
$(CC) -i $(OBJS) -o de
|
||||
install -S 4kw de
|
||||
|
||||
install: /usr/bin/de
|
||||
|
||||
/usr/bin/de: de
|
||||
install -cs -o bin de $@
|
||||
|
||||
$(OBJS): de.h
|
||||
|
||||
clean:
|
||||
rm -f *.bak *.o de
|
||||
Executable
+156
@@ -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.
|
||||
Executable
+1335
File diff suppressed because it is too large
Load Diff
Executable
+361
@@ -0,0 +1,361 @@
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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'. */
|
||||
/* */
|
||||
/****************************************************************/
|
||||
#undef printf
|
||||
#include <stdio.h>
|
||||
|
||||
/* 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
|
||||
|
||||
#if (CHIP == SPARC)
|
||||
#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
|
||||
|
||||
#if (CHIP == SPARC)
|
||||
/* 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 5
|
||||
#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 unsigned short word_t; /* For most user i/o */
|
||||
#if _WORD_SIZE == 2
|
||||
typedef unsigned int Word_t; /* What it should always be */
|
||||
#else
|
||||
typedef int Word_t; /* Unsigned promotion under ANSI C */
|
||||
#endif
|
||||
|
||||
#ifndef I_MAP_SLOTS
|
||||
/* Max number of inode and zone map blocks we can handle. */
|
||||
#define I_MAP_SLOTS 8
|
||||
#define Z_MAP_SLOTS (sizeof(char *) == 2 ? 16 : 128)
|
||||
#endif
|
||||
|
||||
typedef struct de_state /* State of disk ed. */
|
||||
{
|
||||
/* Information from super block */
|
||||
/* The types here are mostly promoted types for simplicity */
|
||||
/* and efficiency. */
|
||||
|
||||
unsigned inodes; /* Number of i-nodes */
|
||||
zone_t 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 */
|
||||
int magic; /* Magic number */
|
||||
|
||||
/* Numbers derived from the magic number */
|
||||
|
||||
unsigned char is_fs; /* Nonzero for good fs */
|
||||
unsigned char v1; /* Nonzero for V1 fs */
|
||||
unsigned inode_size; /* Size of disk inode */
|
||||
unsigned nr_indirects; /* # indirect blocks */
|
||||
unsigned zone_num_size; /* Size of disk z num */
|
||||
|
||||
/* Other derived numbers */
|
||||
|
||||
bit_t inodes_in_map; /* Bits in i-node map */
|
||||
bit_t zones_in_map; /* Bits in zone map */
|
||||
int ndzones; /* # direct zones in an inode */
|
||||
|
||||
/* Information from map blocks */
|
||||
|
||||
bitchunk_t inode_map[ I_MAP_SLOTS * K / sizeof (bitchunk_t) ];
|
||||
bitchunk_t zone_map[ Z_MAP_SLOTS * K / sizeof (bitchunk_t) ];
|
||||
|
||||
/* Information for current block */
|
||||
|
||||
off_t address; /* Current address */
|
||||
off_t last_addr; /* For erasing ptrs */
|
||||
zone_t 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 */
|
||||
zone_t 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 */
|
||||
|
||||
/* de.c */
|
||||
|
||||
_PROTOTYPE(void main , (int argc , char *argv []));
|
||||
_PROTOTYPE(int Process , (de_state *s , int c ));
|
||||
|
||||
#if __STDC__
|
||||
void Error( const char *text, ... );
|
||||
#else
|
||||
void Error();
|
||||
#endif
|
||||
|
||||
_PROTOTYPE(int In_Use , (bit_t bit , bitchunk_t *map ));
|
||||
_PROTOTYPE(ino_t Find_Inode , (de_state *s , char *filename ));
|
||||
|
||||
|
||||
/* de_stdin.c */
|
||||
|
||||
_PROTOTYPE(void Save_Term , (void));
|
||||
_PROTOTYPE(void Set_Term , (void));
|
||||
_PROTOTYPE(void Reset_Term , (void));
|
||||
_PROTOTYPE(int Get_Char , (void));
|
||||
_PROTOTYPE(char *Get_Line , (void));
|
||||
_PROTOTYPE(int Arrow_Esc , (int c ));
|
||||
|
||||
/* de_stdout.c */
|
||||
|
||||
_PROTOTYPE(int Init_Termcap , (void));
|
||||
_PROTOTYPE(void Draw_Help_Screen , (de_state *s ));
|
||||
_PROTOTYPE(void Wait_For_Key , (void));
|
||||
_PROTOTYPE(void Draw_Prompt , (char *string ));
|
||||
_PROTOTYPE(void Erase_Prompt , (void));
|
||||
|
||||
_PROTOTYPE(void Draw_Screen , (de_state *s ));
|
||||
_PROTOTYPE(void Draw_Strings , (de_state *s ));
|
||||
_PROTOTYPE(void Draw_Pointers , (de_state *s ));
|
||||
_PROTOTYPE(void Print_Ascii , (int c ));
|
||||
|
||||
_PROTOTYPE(void Goto , (int column , int line ));
|
||||
_PROTOTYPE(void Block_Type , (de_state *s ));
|
||||
_PROTOTYPE(void Draw_Words , (de_state *s ));
|
||||
_PROTOTYPE(void Draw_Info , (de_state *s ));
|
||||
_PROTOTYPE(void Draw_Block , (char *block ));
|
||||
_PROTOTYPE(void Draw_Map , (char *block , int max_bits ));
|
||||
_PROTOTYPE(void Draw_Offset , (de_state *s ));
|
||||
_PROTOTYPE(void Word_Pointers , (off_t old_addr , off_t new_addr ));
|
||||
_PROTOTYPE(void Block_Pointers , (off_t old_addr , off_t new_addr ));
|
||||
_PROTOTYPE(void Map_Pointers , (off_t old_addr , off_t new_addr ));
|
||||
_PROTOTYPE(void Print_Number , (Word_t number , int output_base ));
|
||||
_PROTOTYPE(void Draw_Zone_Numbers , (de_state *s , struct inode *inode ,
|
||||
int zindex , int zrow ));
|
||||
|
||||
#if __STDC__
|
||||
void Warning( const char *text, ... );
|
||||
#else
|
||||
void Warning();
|
||||
#endif
|
||||
|
||||
|
||||
/* de_diskio.c */
|
||||
|
||||
_PROTOTYPE(void Read_Disk , (de_state *s , off_t block_addr , char *buffer ));
|
||||
_PROTOTYPE(void Read_Block , (de_state *s , char *buffer ));
|
||||
_PROTOTYPE(void Read_Super_Block , (de_state *s ));
|
||||
_PROTOTYPE(void Read_Bit_Maps , (de_state *s ));
|
||||
_PROTOTYPE(off_t Search , (de_state *s , char *string ));
|
||||
_PROTOTYPE(void Write_Word , (de_state *s , Word_t word ));
|
||||
|
||||
|
||||
/* de_recover.c */
|
||||
|
||||
_PROTOTYPE(int Path_Dir_File , (char *path_name , char **dir_name ,
|
||||
char **file_name ));
|
||||
_PROTOTYPE(char *File_Device , (char *file_name ));
|
||||
_PROTOTYPE(ino_t Find_Deleted_Entry , (de_state *s , char *path_name ));
|
||||
_PROTOTYPE(off_t Recover_Blocks , (de_state *s ));
|
||||
|
||||
|
||||
#undef printf /* Because fs/const.h */
|
||||
/* defines it. */
|
||||
|
||||
|
||||
/* Static functions are all pre-declared FORWARD but none are */
|
||||
/* declared static yet - this can wait until all functions are */
|
||||
/* declared with prototypes. */
|
||||
|
||||
#undef FORWARD
|
||||
#define FORWARD /* static */
|
||||
Executable
+345
@@ -0,0 +1,345 @@
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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 <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <minix/const.h>
|
||||
#include <minix/type.h>
|
||||
#include "../../servers/fs/const.h"
|
||||
#include "../../servers/fs/type.h"
|
||||
#include "../../servers/fs/super.h"
|
||||
#include "../../servers/fs/inode.h"
|
||||
#include <minix/fslib.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 = (zone_t) (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;
|
||||
unsigned inodes_per_block;
|
||||
off_t size;
|
||||
|
||||
Read_Disk( s, (long) 1 * K, s->buffer );
|
||||
|
||||
s->magic = super->s_magic;
|
||||
if ( s->magic == SUPER_MAGIC )
|
||||
{
|
||||
s->is_fs = TRUE;
|
||||
s->v1 = TRUE;
|
||||
s->inode_size = V1_INODE_SIZE;
|
||||
inodes_per_block = V1_INODES_PER_BLOCK;
|
||||
s->nr_indirects = V1_INDIRECTS;
|
||||
s->zone_num_size = V1_ZONE_NUM_SIZE;
|
||||
s->zones = super->s_nzones;
|
||||
s->ndzones = V1_NR_DZONES;
|
||||
}
|
||||
else if ( s->magic == SUPER_V2 )
|
||||
{
|
||||
s->is_fs = TRUE;
|
||||
s->v1 = FALSE;
|
||||
s->inode_size = V2_INODE_SIZE;
|
||||
inodes_per_block = V2_INODES_PER_BLOCK(STATIC_BLOCK_SIZE);
|
||||
s->nr_indirects = V2_INDIRECTS(STATIC_BLOCK_SIZE);
|
||||
s->zone_num_size = V2_ZONE_NUM_SIZE;
|
||||
s->zones = super->s_zones;
|
||||
s->ndzones = V2_NR_DZONES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( super->s_magic == SUPER_REV )
|
||||
Warning( "V1-bytes-swapped file system (?)" );
|
||||
else if ( super->s_magic == SUPER_V2_REV )
|
||||
Warning( "V2-bytes-swapped file system (?)" );
|
||||
else
|
||||
Warning( "Not a Minix file system" );
|
||||
Warning( "The file system features will not be available" );
|
||||
s->zones = 100000L;
|
||||
return;
|
||||
}
|
||||
|
||||
s->inodes = super->s_ninodes;
|
||||
s->inode_maps = bitmapsize( (bit_t) s->inodes + 1 , STATIC_BLOCK_SIZE);
|
||||
if ( s->inode_maps != super->s_imap_blocks )
|
||||
{
|
||||
if ( s->inode_maps > super->s_imap_blocks )
|
||||
Error( "Corrupted inode map count or inode count in super block" );
|
||||
else
|
||||
Warning( "Count of inode map blocks in super block suspiciously high" );
|
||||
s->inode_maps = super->s_imap_blocks;
|
||||
}
|
||||
|
||||
s->zone_maps = bitmapsize( (bit_t) s->zones , STATIC_BLOCK_SIZE);
|
||||
if ( s->zone_maps != super->s_zmap_blocks )
|
||||
{
|
||||
if ( s->zone_maps > super->s_zmap_blocks )
|
||||
Error( "Corrupted zone map count or zone count in super block" );
|
||||
else
|
||||
Warning( "Count of zone map blocks in super block suspiciously high" );
|
||||
s->zone_maps = super->s_zmap_blocks;
|
||||
}
|
||||
|
||||
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;
|
||||
if ( s->first_data != super->s_firstdatazone )
|
||||
{
|
||||
if ( s->first_data > super->s_firstdatazone )
|
||||
Error( "Corrupted first data zone offset or inode count in super block" );
|
||||
else
|
||||
Warning( "First data zone in super block suspiciously high" );
|
||||
s->first_data = super->s_firstdatazone;
|
||||
}
|
||||
|
||||
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 ( super->s_log_zone_size != 0 )
|
||||
Error( "Can not handle multiple blocks per zone" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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 > Z_MAP_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,
|
||||
(char *) &s->inode_map[ i * K / sizeof (bitchunk_t ) ] );
|
||||
}
|
||||
|
||||
for ( i = 0; i < s->zone_maps; ++i )
|
||||
{
|
||||
Read_Disk( s, (long) (2 + s->inode_maps + i) * K,
|
||||
(char *) &s->zone_map[ i * K / sizeof (bitchunk_t ) ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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;
|
||||
word_t 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, sizeof word ) != sizeof word )
|
||||
Error( "Error writing %s", s->device_name );
|
||||
}
|
||||
Executable
+612
@@ -0,0 +1,612 @@
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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 <dirent.h>
|
||||
|
||||
#include <minix/const.h>
|
||||
#include <minix/type.h>
|
||||
#include "../../servers/fs/const.h"
|
||||
#include "../../servers/fs/type.h"
|
||||
#include "../../servers/fs/inode.h"
|
||||
#include <minix/fslib.h>
|
||||
|
||||
#include "de.h"
|
||||
|
||||
_PROTOTYPE(int Indirect, (de_state *s, zone_t block, off_t *size, int dblind));
|
||||
_PROTOTYPE(int Data_Block, (de_state *s, zone_t block, off_t *file_size ));
|
||||
_PROTOTYPE(int Free_Block, (de_state *s, zone_t block ));
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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[ NAME_MAX + 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, NAME_MAX );
|
||||
|
||||
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, NAME_MAX - sizeof(ino_t) ) == 0 )
|
||||
{
|
||||
ino_t inode = *( (ino_t *) &entry.d_name[ NAME_MAX - 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;
|
||||
|
||||
{
|
||||
struct inode core_inode;
|
||||
d1_inode *dip1;
|
||||
d2_inode *dip2;
|
||||
struct inode *inode = &core_inode;
|
||||
bit_t node = (s->address - (s->first_data - s->inode_blocks) * K) /
|
||||
s->inode_size + 1;
|
||||
|
||||
dip1 = (d1_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ];
|
||||
dip2 = (d2_inode *) &s->buffer[ s->offset & ~ PAGE_MASK
|
||||
& ~ (V2_INODE_SIZE-1) ];
|
||||
conv_inode( inode, dip1, dip2, READING, s->magic );
|
||||
|
||||
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 s->ndzones pointers are stored in the i-node. */
|
||||
|
||||
for ( i = 0; i < s->ndzones; ++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 inode->i_indirects more blk ptrs. */
|
||||
|
||||
if ( ! Indirect( s, inode->i_zone[ s->ndzones ], &file_size, 0 ) )
|
||||
return( -1L );
|
||||
|
||||
if ( file_size == 0 )
|
||||
return( inode->i_size );
|
||||
|
||||
|
||||
/* A double indirect block can contain up to inode->i_indirects blk ptrs. */
|
||||
|
||||
if ( ! Indirect( s, inode->i_zone[ s->ndzones+1 ], &file_size, 1 ) )
|
||||
return( -1L );
|
||||
|
||||
if ( file_size == 0 )
|
||||
return( inode->i_size );
|
||||
|
||||
Error( "Internal fault (file_size != 0)" );
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
return( -1L );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* 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
|
||||
* V*_INDIRECTS 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_t block;
|
||||
off_t *file_size;
|
||||
int dblind;
|
||||
|
||||
{
|
||||
union
|
||||
{
|
||||
zone1_t ind1[ V1_INDIRECTS ];
|
||||
zone_t ind2[ V2_INDIRECTS(STATIC_BLOCK_SIZE) ];
|
||||
} indirect;
|
||||
int i;
|
||||
zone_t zone;
|
||||
|
||||
/* Check for a "hole". */
|
||||
|
||||
if ( block == NO_ZONE )
|
||||
{
|
||||
off_t skip = (off_t) s->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, (char *) &indirect );
|
||||
|
||||
for ( i = 0; i < s->nr_indirects; ++i )
|
||||
{
|
||||
if ( *file_size == 0 )
|
||||
return( 1 );
|
||||
|
||||
zone = (s->v1 ? indirect.ind1[ i ] : indirect.ind2[ i ]);
|
||||
if ( dblind )
|
||||
{
|
||||
if ( ! Indirect( s, zone, file_size, 0 ) )
|
||||
return( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! Data_Block( s, zone, 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_t 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, (size_t) block_size, s->file_f )
|
||||
!= (size_t) 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_t block;
|
||||
|
||||
{
|
||||
if ( block < s->first_data || block >= s->zones )
|
||||
{
|
||||
Warning( "Illegal block number" );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
if ( In_Use( (bit_t) (block - (s->first_data - 1)), s->zone_map ) )
|
||||
{
|
||||
Warning( "Encountered an \"in use\" data block" );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
Executable
+298
@@ -0,0 +1,298 @@
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* de_stdin.c */
|
||||
/* */
|
||||
/* Processing input from the "de" user. */
|
||||
/* */
|
||||
/****************************************************************/
|
||||
/* origination 1989-Jan-15 Terrence W. Holm */
|
||||
/****************************************************************/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <minix/config.h>
|
||||
#include <minix/const.h>
|
||||
#include "../../servers/fs/const.h"
|
||||
#include "../../servers/fs/inode.h"
|
||||
|
||||
#include "de.h"
|
||||
|
||||
FORWARD _PROTOTYPE(int Timed_Get_Char , (int time ));
|
||||
FORWARD _PROTOTYPE(void Timed_Out , (int sig));
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* Save_Term() */
|
||||
/* */
|
||||
/* Save the current terminal characteristics. */
|
||||
/* */
|
||||
/* */
|
||||
/* Set_Term() */
|
||||
/* */
|
||||
/* Set up the terminal characteristics. */
|
||||
/* */
|
||||
/* */
|
||||
/* Reset_Term() */
|
||||
/* */
|
||||
/* Restore the terminal characteristics. */
|
||||
/* */
|
||||
/****************************************************************/
|
||||
|
||||
|
||||
static struct termios saved_term;
|
||||
|
||||
|
||||
void Save_Term()
|
||||
|
||||
{
|
||||
tcgetattr( 0, &saved_term );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Set_Term()
|
||||
|
||||
{
|
||||
struct termios term;
|
||||
|
||||
term = saved_term;
|
||||
|
||||
|
||||
/* No tab expansion, no echo, don't map ^M to ^J, cbreak mode */
|
||||
|
||||
term.c_iflag &= ~ICRNL;
|
||||
term.c_oflag &= ~OPOST;
|
||||
term.c_lflag &= ~ICANON & ~ECHO;
|
||||
|
||||
|
||||
/* Change the interrupt character to ^C */
|
||||
|
||||
term.c_cc[VINTR] = '\003';
|
||||
|
||||
tcsetattr( 0, TCSANOW, &term );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Reset_Term()
|
||||
|
||||
{
|
||||
tcsetattr( 0, TCSANOW, &saved_term );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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)" );
|
||||
|
||||
/* NOTREACHED */
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* 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(sig)
|
||||
int sig;
|
||||
{}
|
||||
|
||||
/*
|
||||
* $PchHeader: /mount/hd2/minix/sys/cmd/de/RCS/de_stdin.c,v 1.3 1995/02/10 08:01:30 philip Exp $
|
||||
*/
|
||||
Executable
+1169
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user