Undelete

Martin `MJ' Mares mj na ucw.cz
Čtvrtek Srpen 10 01:03:58 CEST 2000


Zdravim!

> A undelete zabudovany v  Midnight Commanderu nezabralo?
> mě už pomohlo 2x

Ono sice z principu funguje, ale MC neni staveny na adresare majici
desitky tisic polozek, coz je presne to, co mu undeleter vygeneruje,
takze kazda operace je pak nepouzitelne pomala.

Nasledujici programek (nutno slinkovat s libext2fs) je pomerne dobrou
nahradou. Necekejte od nej ani zazraky ani zadny pekny user interface,
napsal jsem si jej jednou pred lety, kdyz jsem potreboval obnovit spousty
smazanych souboru. Pouziva se tak, ze oscanuje disk, nabidne vam
k oeditovani seznam smazanych souboru a ty, ktere v seznamu zustaly,
obnovi.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj na ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"How an engineer writes a program: Start by debugging an empty file..."

/*
 *  Ext2fs undelete. Experimental. GPL'ed.
 *  (c) 1998 Martin Mares <mj na ucw.cz>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <linux/ext2_fs.h>
#include <ext2fs/ext2fs.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>

static ext2_filsys fs = NULL;

struct fi {
  struct fi *next;
  ino_t inode;
  int size, nblk, perm;
  int num_blocks, bad_blocks, alloc_blocks;
  time_t mtime;
  int *blocks;
};

void
die(char *msg, ...)
{
  va_list args;

  va_start(args, msg);
  fprintf(stderr, "undel: ");
  vfprintf(stderr, msg, args);
  fputs("\n", stderr);
  exit(1);
}

void *
xmalloc(int z)
{
  void *x = malloc(z);
  if (!x)
    die("Hah. Out of memory. Need %d bytes more. Please add some SIVMMs and press enter.", z);
  return x;
}

static int verb;
static int anal;

static int
block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *priv)
{
  struct fi *t = priv;

//  printf("%d\n", *block_nr);
  if (*block_nr < fs->super->s_first_data_block ||
      *block_nr >= fs->super->s_blocks_count)
    {
      t->bad_blocks++;
      return BLOCK_ABORT;
    }
#ifdef BM_X
  if (ext2fs_test_block_bitmap(fs->block_map,*block_nr))
    t->alloc_blocks++;
#endif
  if (blockcnt < 0)
    return 0;
  if (t->num_blocks < t->nblk)
    t->blocks[t->num_blocks] = *block_nr;
  t->num_blocks++;
  return 0;
}

void
mkt(char *buf, time_t t)
{
  struct tm *tm;
  tm = localtime(&t);
  if (tm->tm_year == 98 && tm->tm_mon == 11 && tm->tm_mday == 18)
    sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
  else
    sprintf(buf, "%02d-%02d-%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday);
}

static int fd;

static void
md(char *z)
{
  char *c = strrchr(z, '/');
  if (c)
    {
      *c = 0;
      md(z);
      *c = '/';
      z = c+1;
    }
  mkdir(z, 0777);
}

static void
op(char *z)
{
  char *c;

  if ((fd = open(z, O_CREAT | O_TRUNC | O_RDWR, 0666)) >= 0)
    return;
  c = strrchr(z, '/');
  if (!c)
    die("open: %s", z);
  *c = 0;
  md(z);
  *c = '/';
  if ((fd = open(z, O_CREAT | O_TRUNC | O_RDWR, 0666)) >= 0)
    return;
  die("open: %s", z);
}

static char *
analyse(unsigned char *buf, int size)
{
  char *z, *x;
  int i, c;

  buf[size] = 0;
  if (!strncmp(buf, "\177ELF", 4))
    return "ELF";
  if (!memcmp(buf, "\x1f\x8b", 2))
    return "GZ";
  if (buf[0] == '#' && buf[1] == '!' && (z = strchr(buf, '\n')))
    {
      *z = 0;
      x = strrchr(buf, '/');
      if (x)
	{
	  *x++ = 0;
	  return x;
	}
      *z = '\n';
    }
  c=0;
  for(i=0; i<size; i++)
    if (buf[i] >= ' ' && buf[i] < 0x7f ||
	buf[i] >= 0xa0 ||
	buf[i] == '\r' || buf[i] == '\n' || buf[i] == '\t')
      c++;
  if (c == size)
    return "TXT";
  return "?";
}

int
main(int argc, char **argv)
{
  ino_t ino;
  struct ext2_inode inode;
  ext2_inode_scan scan;
  char *block;
  struct fi *first = NULL;
  struct fi **dir;
  FILE *f;
  int cnt=0;
  char dt[16], mt[16], at[16], buf[256], *xx;
  char *tf = "/tmp/undel.tmp";

  if (argc >= 3 && !strcmp(argv[1], "-v"))
    {
      argc--, argv++;
      verb=1;
    }
  if (argc >= 3 && !strcmp(argv[1], "-a"))
    {
      argc--, argv++;
      anal=1;
    }
  if (argc != 2)
    die("usage: undel [-v] [-a] <device>");
  if (!(f = fopen(tf, "w")))
    die("tmp file open");
  puts("Opening device...");
  if (ext2fs_open(argv[1], 0, 0, 0, unix_io_manager, &fs))
    die("ext2fs_open");
  if (ext2fs_read_inode_bitmap(fs))
    die("inode bitmap");
#ifdef BM_X
  if (ext2fs_read_block_bitmap(fs))
    die("block bitmap");
#endif
  if (ext2fs_open_inode_scan(fs, 0, &scan))
    die("scan init");
  if (ext2fs_get_next_inode(scan, &ino, &inode))
    die("scan 1");
  block = xmalloc(fs->blocksize * 16);
  while (ino)
    {
      if ((cnt % 1024) == 1023 && !verb) printf("%d\r", cnt);
      if (inode.i_dtime && S_ISREG(inode.i_mode) && inode.i_size > 0)
	{
	  struct fi *t = xmalloc(sizeof(struct fi));
	  t->num_blocks = t->bad_blocks = t->alloc_blocks = 0;
	  t->inode = ino;
	  t->size = inode.i_size;
	  t->nblk = (t->size + fs->blocksize - 1) / fs->blocksize;
	  if (verb) printf("%ld: size=%d[%d] ", t->inode, t->size, t->nblk);
	  fflush(stdout);
	  t->blocks = xmalloc(sizeof(int) * t->nblk);
	  if (ext2fs_block_iterate(fs, ino, 0, block, block_proc, t))
	    fprintf(stderr, "iterate error\n");
	  else if (t->bad_blocks)
	    {
	      if (verb) printf("BAD\n");
	    bad:
	      free(t->blocks);
	      free(t);
	    }
	  else if (t->num_blocks != t->nblk)
	    {
	      if (verb) printf("%d != %d\n", t->num_blocks, t->nblk);
	      goto bad;
	    }
	  else
	    {
	      if (verb) printf("alloc=%d ", t->alloc_blocks);
	      if (anal)
		{
		  if (io_channel_read_blk(fs->io, t->blocks[0], 1, block))
		    die("read error");
		  xx = analyse(block, (t->size > fs->blocksize) ? fs->blocksize : t->size);
		  if (verb) printf("%s ", xx);
		}
	      else
		xx = "";
	      if (verb) printf("OK\n");
	      t->next = first;
	      first = t;
	      mkt(dt, inode.i_dtime);
	      mkt(mt, inode.i_mtime);
	      mkt(at, inode.i_atime);
	      t->perm = inode.i_mode & 0777;
	      t->mtime = inode.i_mtime;
	      fprintf(f, "%d\t%ld\t%d\t%d:%d\t%03o\t%s\t%s\t%s\t%s\t%s\t%d/%ld\n",
		      cnt++,
		      t->inode,
		      t->size,
		      inode.i_uid,
		      inode.i_gid,
		      inode.i_mode & 07777,
		      (t->alloc_blocks) ? "C" : "",
		      dt,
		      mt,
		      at,
		      xx,
		      inode.i_uid,
		      t->inode);
	    }
	}
      if (ext2fs_get_next_inode(scan, &ino, &inode))
#if 0
	die("scan 2");
#else
	break;
#endif
    }
  fclose(f);

  sprintf(buf, "vi %s", tf);
  if (system(buf))
    die("aborted");

  if (!(f = fopen(tf, "r")))
    die("infile");
  dir = xmalloc(sizeof(struct fi *) * cnt);
  cnt=0;
  while (first)
    {
      dir[cnt++] = first;
      first = first->next;
    }
  while (fgets(buf, sizeof(buf)-1, f))
    {
      char *z = strchr(buf, '\n');
      int i, s;
      struct fi *t;
      struct utimbuf ut;

      if (!z)
	die("eol");
      if (buf[0] == '#')
	continue;
      *z = 0;
      sscanf(buf, "%d", &i);
      if (i < 0 || i >= cnt)
	die("out of range %d", i);
      t = dir[cnt-1-i];
      while (z[-1] != '\t')
	z--;
      printf("Undeleting %ld to %s\n", t->inode, z);
      op(z);
      s = t->size;
      for(i=0; i<t->nblk; i++)
	{
	  int z = fs->blocksize;
	  if (z > s)
	    z = s;
	  if (io_channel_read_blk(fs->io, t->blocks[i], 1, block))
	    die("read error");
	  if (write(fd, block, z) != z)
	    die("write error");
	  s -= z;
	}
      close(fd);
      chmod(z, t->perm);
      ut.actime = t->mtime;
      ut.modtime = t->mtime;
      utime(z, &ut);
    }

  return 0;
}


Další informace o konferenci Linux