2007-10-13 Marco Ciampa <ciampix@libero.it>
[midnight-commander.git] / vfs / undelfs.c
blobcea0e7991570d9969d3787431686aafdaf0f5e9d
1 /* UnDel File System: Midnight Commander file system.
3 This file system is intended to be used together with the
4 ext2fs library to recover files from ext2fs file systems.
6 Parts of this program were taken from the lsdel.c and dump.c files
7 written by Ted Ts'o (tytso@mit.edu) for the ext2fs package.
9 Copyright (C) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
10 2005, 2007 Free Software Foundation, Inc.
11 Written by: 1995 Miguel de Icaza.
12 1997 Norbert Warmuth.
13 2000 Pavel Machek
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public License
17 as published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Library General Public License for more details.
25 You should have received a copy of the GNU Library General Public
26 License along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
29 /* Assumptions:
31 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
32 * 2. Files are on the local file system (we do not support vfs files
33 * because we would have to provide an io_manager for the ext2fs tools,
34 * and I don't think it would be too useful to undelete files
37 #include <config.h>
38 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
43 #ifdef HAVE_EXT2FS_EXT2_FS_H
44 #include <ext2fs/ext2_fs.h>
45 #else
46 /* asm/types.h defines its own umode_t */
47 #undef umode_t
48 #include <linux/ext2_fs.h>
49 #endif
51 #include <ext2fs/ext2fs.h>
52 #include <ctype.h>
54 #include "../src/global.h"
55 #include "../src/tty.h" /* enable/disable interrupt key */
56 #include "../src/wtools.h" /* message() */
57 #include "../src/main.h" /* print_vfs_message */
58 #include "utilvfs.h"
59 #include "vfs.h"
60 #include "vfs-impl.h"
62 struct deleted_info {
63 ext2_ino_t ino;
64 unsigned short mode;
65 unsigned short uid;
66 unsigned short gid;
67 unsigned long size;
68 time_t dtime;
69 int num_blocks;
70 int free_blocks;
73 struct lsdel_struct {
74 ext2_ino_t inode;
75 int num_blocks;
76 int free_blocks;
77 int bad_blocks;
80 /* We only allow one opened ext2fs */
81 static char *ext2_fname;
82 static ext2_filsys fs = NULL;
83 static struct lsdel_struct lsd;
84 static struct deleted_info *delarray;
85 static int num_delarray, max_delarray;
86 static char *block_buf;
87 static char *undelfserr = N_(" undelfs: error ");
88 static int readdir_ptr;
89 static int undelfs_usage;
90 static struct vfs_class vfs_undelfs_ops;
92 /* To generate the . and .. entries use -2 */
93 #define READDIR_PTR_INIT 0
95 static void
96 undelfs_shutdown (void)
98 if (fs)
99 ext2fs_close (fs);
100 fs = NULL;
101 g_free (ext2_fname);
102 ext2_fname = NULL;
103 g_free (delarray);
104 delarray = NULL;
105 g_free (block_buf);
106 block_buf = NULL;
109 static void
110 undelfs_get_path (const char *dirname, char **fsname, char **file)
112 const char *p;
114 /* To look like filesystem, we have virtual directories
115 /#undel:XXX, which have no subdirectories. XXX is replaced with
116 hda5, sdb8 etc, which is assumed to live under /dev.
117 -- pavel@ucw.cz */
119 *fsname = NULL;
121 if (strncmp (dirname, "/#undel:", 8))
122 return;
123 else
124 dirname += 8;
126 /* Since we don't allow subdirectories, it's easy to get a filename,
127 * just scan backwards for a slash */
128 if (*dirname == 0)
129 return;
131 p = dirname + strlen (dirname);
132 #if 0
133 /* Strip trailing ./
135 if (p - dirname > 2 && *(p-1) == '/' && *(p-2) == '.')
136 *(p = p-2) = 0;
137 #endif
139 while (p > dirname){
140 if (*p == '/'){
141 char *tmp;
143 *file = g_strdup (p+1);
144 tmp = g_strndup (dirname, p - dirname);
145 *fsname = g_strconcat ("/dev/", tmp, (char *) NULL);
146 g_free (tmp);
147 return;
149 p--;
151 *file = g_strdup ("");
152 *fsname = g_strconcat ("/dev/", dirname, (char *) NULL);
153 return;
156 static int
157 undelfs_lsdel_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *private)
159 struct lsdel_struct *lsd = (struct lsdel_struct *) private;
161 lsd->num_blocks++;
163 if (*block_nr < fs->super->s_first_data_block ||
164 *block_nr >= fs->super->s_blocks_count) {
165 lsd->bad_blocks++;
166 return BLOCK_ABORT;
169 if (!ext2fs_test_block_bitmap(fs->block_map,*block_nr))
170 lsd->free_blocks++;
172 return 0;
176 * Load information about deleted files.
177 * Don't abort if there is not enough memory - load as much as we can.
179 static int
180 undelfs_loaddel (void)
182 int retval, count;
183 ext2_ino_t ino;
184 struct ext2_inode inode;
185 ext2_inode_scan scan;
187 max_delarray = 100;
188 num_delarray = 0;
189 delarray = g_try_malloc (sizeof (struct deleted_info) * max_delarray);
190 if (!delarray) {
191 message (1, undelfserr, _(" not enough memory "));
192 return 0;
194 block_buf = g_try_malloc (fs->blocksize * 3);
195 if (!block_buf) {
196 message (1, undelfserr, _(" while allocating block buffer "));
197 goto free_delarray;
199 if ((retval = ext2fs_open_inode_scan (fs, 0, &scan))) {
200 message (1, undelfserr, _(" open_inode_scan: %d "), retval);
201 goto free_block_buf;
203 if ((retval = ext2fs_get_next_inode (scan, &ino, &inode))) {
204 message (1, undelfserr, _(" while starting inode scan %d "),
205 retval);
206 goto error_out;
209 count = 0;
210 while (ino) {
211 if ((count++ % 1024) == 0)
212 print_vfs_message (_
213 ("undelfs: loading deleted files information %d inodes"),
214 count);
215 if (inode.i_dtime == 0)
216 goto next;
218 if (S_ISDIR (inode.i_mode))
219 goto next;
221 lsd.inode = ino;
222 lsd.num_blocks = 0;
223 lsd.free_blocks = 0;
224 lsd.bad_blocks = 0;
226 retval =
227 ext2fs_block_iterate (fs, ino, 0, block_buf,
228 undelfs_lsdel_proc, &lsd);
229 if (retval) {
230 message (1, undelfserr,
231 _(" while calling ext2_block_iterate %d "), retval);
232 goto next;
234 if (lsd.free_blocks && !lsd.bad_blocks) {
235 if (num_delarray >= max_delarray) {
236 struct deleted_info *delarray_new =
237 g_try_realloc (delarray,
238 sizeof (struct deleted_info) *
239 (max_delarray + 50));
240 if (!delarray_new) {
241 message (1, undelfserr,
243 (" no more memory while reallocating array "));
244 goto error_out;
246 delarray = delarray_new;
247 max_delarray += 50;
250 delarray[num_delarray].ino = ino;
251 delarray[num_delarray].mode = inode.i_mode;
252 delarray[num_delarray].uid = inode.i_uid;
253 delarray[num_delarray].gid = inode.i_gid;
254 delarray[num_delarray].size = inode.i_size;
255 delarray[num_delarray].dtime = inode.i_dtime;
256 delarray[num_delarray].num_blocks = lsd.num_blocks;
257 delarray[num_delarray].free_blocks = lsd.free_blocks;
258 num_delarray++;
261 next:
262 retval = ext2fs_get_next_inode (scan, &ino, &inode);
263 if (retval) {
264 message (1, undelfserr, _(" while doing inode scan %d "),
265 retval);
266 goto error_out;
269 readdir_ptr = READDIR_PTR_INIT;
270 ext2fs_close_inode_scan (scan);
271 return 1;
273 error_out:
274 ext2fs_close_inode_scan (scan);
275 free_block_buf:
276 g_free (block_buf);
277 block_buf = NULL;
278 free_delarray:
279 g_free (delarray);
280 delarray = NULL;
281 return 0;
286 * This function overrides com_err() from libcom_err library.
287 * It is used in libext2fs to report errors.
289 void
290 com_err (const char *whoami, long err_code, const char *fmt, ...)
292 va_list ap;
293 char *str;
295 va_start (ap, fmt);
296 str = g_strdup_vprintf (fmt, ap);
297 va_end (ap);
299 message (1, _(" Ext2lib error "), " %s (%s: %ld) ", str, whoami,
300 err_code);
301 g_free (str);
304 static void *
305 undelfs_opendir (struct vfs_class *me, const char *dirname)
307 char *file, *f;
309 undelfs_get_path (dirname, &file, &f);
310 if (!file)
311 return 0;
313 /* We don't use the file name */
314 g_free (f);
316 if (!ext2_fname || strcmp (ext2_fname, file)){
317 undelfs_shutdown ();
318 ext2_fname = file;
319 } else {
320 /* To avoid expensive re-scannings */
321 readdir_ptr = READDIR_PTR_INIT;
322 g_free (file);
323 return fs;
326 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)){
327 message (1, undelfserr, _(" Cannot open file %s "), ext2_fname);
328 return 0;
330 print_vfs_message (_("undelfs: reading inode bitmap..."));
331 if (ext2fs_read_inode_bitmap (fs)){
332 message (1, undelfserr,
333 _(" Cannot load inode bitmap from: \n %s \n"), ext2_fname);
334 goto quit_opendir;
336 print_vfs_message (_("undelfs: reading block bitmap..."));
337 if (ext2fs_read_block_bitmap (fs)){
338 message (1, undelfserr,
339 _(" Cannot load block bitmap from: \n %s \n"), ext2_fname);
340 goto quit_opendir;
342 /* Now load the deleted information */
343 if (!undelfs_loaddel ())
344 goto quit_opendir;
345 print_vfs_message (_("%s: done."), me->name);
346 return fs;
347 quit_opendir:
348 print_vfs_message (_("%s: failure"), me->name);
349 ext2fs_close (fs);
350 fs = NULL;
351 return 0;
355 static void *
356 undelfs_readdir(void *vfs_info)
358 static union vfs_dirent undelfs_readdir_data;
359 static char *const dirent_dest = undelfs_readdir_data.dent.d_name;
361 if (vfs_info != fs) {
362 message (1, undelfserr, _(" vfs_info is not fs! "));
363 return NULL;
365 if (readdir_ptr == num_delarray)
366 return NULL;
367 if (readdir_ptr < 0)
368 strcpy (dirent_dest, readdir_ptr == -2 ? "." : "..");
369 else
370 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
371 (long) delarray[readdir_ptr].ino,
372 delarray[readdir_ptr].num_blocks);
373 readdir_ptr++;
375 compute_namelen(&undelfs_readdir_data.dent);
377 return &undelfs_readdir_data;
380 static int
381 undelfs_closedir (void *vfs_info)
383 return 0;
386 typedef struct {
387 int f_index; /* file index into delarray */
388 char *buf;
389 int error_code; /* */
390 int pos; /* file position */
391 int current; /* used to determine current position in itereate */
392 int finished;
393 ext2_ino_t inode;
394 int bytes_read;
395 long size;
397 /* Used by undelfs_read: */
398 char *dest_buffer; /* destination buffer */
399 int count; /* bytes to read */
400 } undelfs_file;
402 /* We do not support lseek */
403 static void *
404 undelfs_open (struct vfs_class *me, const char *fname, int flags, int mode)
406 char *file, *f;
407 ext2_ino_t inode, i;
408 undelfs_file *p = NULL;
410 /* Only allow reads on this file system */
411 undelfs_get_path (fname, &file, &f);
412 if (!file)
413 return 0;
415 if (!ext2_fname || strcmp (ext2_fname, file)) {
416 message (1, undelfserr,
417 _(" You have to chdir to extract files first "));
418 g_free (file);
419 g_free (f);
420 return 0;
422 inode = atol (f);
424 /* Search the file into delarray */
425 for (i = 0; i < num_delarray; i++) {
426 if (inode != delarray[i].ino)
427 continue;
429 /* Found: setup all the structures needed by read */
430 p = (undelfs_file *) g_try_malloc (((gsize) sizeof (undelfs_file)));
431 if (!p) {
432 g_free (file);
433 g_free (f);
434 return 0;
436 p->buf = g_try_malloc (fs->blocksize);
437 if (!p->buf) {
438 g_free (p);
439 g_free (file);
440 g_free (f);
441 return 0;
443 p->inode = inode;
444 p->finished = 0;
445 p->f_index = i;
446 p->error_code = 0;
447 p->pos = 0;
448 p->size = delarray[i].size;
450 g_free (file);
451 g_free (f);
452 undelfs_usage++;
453 return p;
456 static int
457 undelfs_close (void *vfs_info)
459 undelfs_file *p = vfs_info;
460 g_free (p->buf);
461 g_free (p);
462 undelfs_usage--;
463 return 0;
466 static int
467 undelfs_dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
469 int copy_count;
470 undelfs_file *p = (undelfs_file *) private;
472 if (blockcnt < 0)
473 return 0;
475 if (*blocknr) {
476 p->error_code = io_channel_read_blk(fs->io, *blocknr,
477 1, p->buf);
478 if (p->error_code)
479 return BLOCK_ABORT;
480 } else
481 memset(p->buf, 0, fs->blocksize);
483 if (p->pos + p->count < p->current){
484 p->finished = 1;
485 return BLOCK_ABORT;
487 if (p->pos > p->current + fs->blocksize){
488 p->current += fs->blocksize;
489 return 0; /* we have not arrived yet */
492 /* Now, we know we have to extract some data */
493 if (p->pos >= p->current){
495 /* First case: starting pointer inside this block */
496 if (p->pos + p->count <= p->current + fs->blocksize){
497 /* Fully contained */
498 copy_count = p->count;
499 p->finished = p->count;
500 } else {
501 /* Still some more data */
502 copy_count = fs->blocksize-(p->pos-p->current);
504 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
505 } else {
506 /* Second case: we already have passed p->pos */
507 if (p->pos+p->count < p->current+fs->blocksize){
508 copy_count = (p->pos + p->count) - p->current;
509 p->finished = p->count;
510 } else {
511 copy_count = fs->blocksize;
513 memcpy (p->dest_buffer, p->buf, copy_count);
515 p->dest_buffer += copy_count;
516 p->current += fs->blocksize;
517 if (p->finished){
518 return BLOCK_ABORT;
520 return 0;
523 static int
524 undelfs_read (void *vfs_info, char *buffer, int count)
526 undelfs_file *p = vfs_info;
527 int retval;
529 p->dest_buffer = buffer;
530 p->current = 0;
531 p->finished = 0;
532 p->count = count;
534 if (p->pos + p->count > p->size){
535 p->count = p->size - p->pos;
537 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
538 undelfs_dump_read, p);
539 if (retval){
540 message (1, undelfserr, _(" while iterating over blocks "));
541 return -1;
543 if (p->error_code && !p->finished)
544 return 0;
545 p->pos = p->pos + (p->dest_buffer - buffer);
546 return p->dest_buffer - buffer;
549 static long
550 undelfs_getindex (char *path)
552 ext2_ino_t inode = atol (path);
553 int i;
555 for (i = 0; i < num_delarray; i++){
556 if (delarray [i].ino == inode)
557 return i;
559 return -1;
562 static int
563 undelfs_stat_int (int inode_index, struct stat *buf)
565 buf->st_dev = 0;
566 buf->st_ino = delarray [inode_index].ino;
567 buf->st_mode = delarray [inode_index].mode;
568 buf->st_nlink = 1;
569 buf->st_uid = delarray [inode_index].uid;
570 buf->st_gid = delarray [inode_index].gid;
571 buf->st_size = delarray [inode_index].size;
572 buf->st_atime = delarray [inode_index].dtime;
573 buf->st_ctime = delarray [inode_index].dtime;
574 buf->st_mtime = delarray [inode_index].dtime;
575 return 0;
578 static int
579 undelfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
581 int inode_index;
582 char *file, *f;
584 undelfs_get_path (path, &file, &f);
585 if (!file)
586 return 0;
588 /* When called from save_cwd_stats we get an incorrect file and f here:
589 e.g. incorrect correct
590 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
591 file = "/dev" file="/dev/sda1"
592 f = "sda1" f ="401:1"
593 If the first char in f is no digit -> return error */
594 if (!isdigit (*f)) {
595 g_free (file);
596 g_free (f);
597 return -1;
600 if (!ext2_fname || strcmp (ext2_fname, file)){
601 message (1, undelfserr, _(" You have to chdir to extract files first "));
602 g_free (file);
603 g_free (f);
604 return 0;
606 inode_index = undelfs_getindex (f);
607 g_free (file);
608 g_free (f);
610 if (inode_index == -1)
611 return -1;
613 return undelfs_stat_int (inode_index, buf);
616 #define undelfs_stat undelfs_lstat
618 static int
619 undelfs_fstat (void *vfs_info, struct stat *buf)
621 undelfs_file *p = vfs_info;
623 return undelfs_stat_int (p->f_index, buf);
626 static int
627 undelfs_chdir(struct vfs_class *me, const char *path)
629 char *file, *f;
630 int fd;
632 undelfs_get_path (path, &file, &f);
633 if (!file)
634 return -1;
636 /* We may use access because ext2 file systems are local */
637 /* this could be fixed by making an ext2fs io manager to use */
638 /* our vfs, but that is left as an excercise for the reader */
639 if ((fd = open (file, O_RDONLY)) == -1){
640 message (1, undelfserr, _(" Cannot open file %s "), file);
641 g_free (f);
642 g_free (file);
643 return -1;
645 close (fd);
646 g_free (f);
647 g_free (file);
648 return 0;
651 /* this has to stay here for now: vfs layer does not know how to emulate it */
652 static int
653 undelfs_lseek(void *vfs_info, off_t offset, int whence)
655 return -1;
658 static vfsid
659 undelfs_getid (struct vfs_class *me, const char *path)
661 char *fname, *fsname;
663 undelfs_get_path (path, &fsname, &fname);
665 if (!fsname)
666 return NULL;
667 g_free (fname);
668 g_free (fsname);
669 return (vfsid) fs;
672 static int
673 undelfs_nothingisopen(vfsid id)
675 return !undelfs_usage;
678 static void
679 undelfs_free(vfsid id)
681 undelfs_shutdown ();
684 #ifdef ENABLE_NLS
685 static int
686 undelfs_init(struct vfs_class *me) {
687 undelfserr = _(undelfserr);
688 return 1;
690 #else
691 #define undelfs_init NULL
692 #endif
694 void
695 init_undelfs (void)
697 vfs_undelfs_ops.name = "undelfs";
698 vfs_undelfs_ops.prefix = "undel:";
699 vfs_undelfs_ops.init = undelfs_init;
700 vfs_undelfs_ops.open = undelfs_open;
701 vfs_undelfs_ops.close = undelfs_close;
702 vfs_undelfs_ops.read = undelfs_read;
703 vfs_undelfs_ops.opendir = undelfs_opendir;
704 vfs_undelfs_ops.readdir = undelfs_readdir;
705 vfs_undelfs_ops.closedir = undelfs_closedir;
706 vfs_undelfs_ops.stat = undelfs_stat;
707 vfs_undelfs_ops.lstat = undelfs_lstat;
708 vfs_undelfs_ops.fstat = undelfs_fstat;
709 vfs_undelfs_ops.chdir = undelfs_chdir;
710 vfs_undelfs_ops.lseek = undelfs_lseek;
711 vfs_undelfs_ops.getid = undelfs_getid;
712 vfs_undelfs_ops.nothingisopen = undelfs_nothingisopen;
713 vfs_undelfs_ops.free = undelfs_free;
714 vfs_register_class (&vfs_undelfs_ops);