2 UnDel File System: Midnight Commander file system.
4 This file system is intended to be used together with the
5 ext2fs library to recover files from ext2fs file systems.
7 Parts of this program were taken from the lsdel.c and dump.c files
8 written by Ted Ts'o (tytso@mit.edu) for the ext2fs package.
10 Copyright (C) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
12 The Free Software Foundation, Inc.
19 This file is part of the Midnight Commander.
21 The Midnight Commander is free software: you can redistribute it
22 and/or modify it under the terms of the GNU General Public License as
23 published by the Free Software Foundation, either version 3 of the License,
24 or (at your option) any later version.
26 The Midnight Commander is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program. If not, see <http://www.gnu.org/licenses/>.
37 * \brief Source: UnDel File System
41 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
42 * 2. Files are on the local file system (we do not support vfs files
43 * because we would have to provide an io_manager for the ext2fs tools,
44 * and I don't think it would be too useful to undelete files
54 #ifdef HAVE_EXT2FS_EXT2_FS_H
55 #include <ext2fs/ext2_fs.h>
57 /* asm/types.h defines its own umode_t */
59 #include <linux/ext2_fs.h>
62 #include <ext2fs/ext2fs.h>
65 #include "lib/global.h"
67 #include "lib/widget.h" /* message() */
68 #include "lib/vfs/utilvfs.h"
69 #include "lib/vfs/vfs.h"
73 /*** global variables ****************************************************************************/
75 /*** file scope macro definitions ****************************************************************/
77 /* To generate the . and .. entries use -2 */
78 #define READDIR_PTR_INIT 0
80 #define undelfs_stat undelfs_lstat
82 /*** file scope type declarations ****************************************************************/
106 int f_index
; /* file index into delarray */
108 int error_code
; /* */
109 off_t pos
; /* file position */
110 off_t current
; /* used to determine current position in itereate */
116 /* Used by undelfs_read: */
117 char *dest_buffer
; /* destination buffer */
118 size_t count
; /* bytes to read */
121 /*** file scope variables ************************************************************************/
123 /* We only allow one opened ext2fs */
124 static char *ext2_fname
;
125 static ext2_filsys fs
= NULL
;
126 static struct lsdel_struct lsd
;
127 static struct deleted_info
*delarray
;
128 static int num_delarray
, max_delarray
;
129 static char *block_buf
;
130 static const char *undelfserr
= N_("undelfs: error");
131 static int readdir_ptr
;
132 static int undelfs_usage
;
133 static struct vfs_class vfs_undelfs_ops
;
135 /*** file scope functions ************************************************************************/
136 /* --------------------------------------------------------------------------------------------- */
139 undelfs_shutdown (void)
152 /* --------------------------------------------------------------------------------------------- */
155 undelfs_get_path (const vfs_path_t
* vpath
, char **fsname
, char **file
)
157 const char *p
, *dirname
;
158 vfs_path_element_t
*path_element
= vfs_path_get_by_index (vpath
, -1);
161 /* To look like filesystem, we have virtual directories
162 /#undel:XXX, which have no subdirectories. XXX is replaced with
163 hda5, sdb8 etc, which is assumed to live under /dev.
166 dirname
= path_element
->path
;
170 if (strncmp (dirname
, "/#undel", 7))
175 /* Since we don't allow subdirectories, it's easy to get a filename,
176 * just scan backwards for a slash */
180 p
= dirname
+ strlen (dirname
);
184 if (p
- dirname
> 2 && *(p
- 1) == '/' && *(p
- 2) == '.')
194 *file
= g_strdup (p
+ 1);
195 tmp
= g_strndup (dirname
, p
- dirname
);
196 *fsname
= g_strconcat ("/dev/", tmp
, (char *) NULL
);
202 *file
= g_strdup ("");
203 *fsname
= g_strconcat ("/dev/", dirname
, (char *) NULL
);
207 /* --------------------------------------------------------------------------------------------- */
210 undelfs_lsdel_proc (ext2_filsys _fs
, blk_t
* block_nr
, int blockcnt
, void *private)
212 struct lsdel_struct
*_lsd
= (struct lsdel_struct
*) private;
216 if (*block_nr
< _fs
->super
->s_first_data_block
|| *block_nr
>= _fs
->super
->s_blocks_count
)
222 if (!ext2fs_test_block_bitmap (_fs
->block_map
, *block_nr
))
228 /* --------------------------------------------------------------------------------------------- */
230 * Load information about deleted files.
231 * Don't abort if there is not enough memory - load as much as we can.
235 undelfs_loaddel (void)
239 struct ext2_inode inode
;
240 ext2_inode_scan scan
;
244 delarray
= g_try_malloc (sizeof (struct deleted_info
) * max_delarray
);
247 message (D_ERROR
, undelfserr
, _("not enough memory"));
250 block_buf
= g_try_malloc (fs
->blocksize
* 3);
253 message (D_ERROR
, undelfserr
, _("while allocating block buffer"));
256 retval
= ext2fs_open_inode_scan (fs
, 0, &scan
);
259 message (D_ERROR
, undelfserr
, _("open_inode_scan: %d"), retval
);
262 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
265 message (D_ERROR
, undelfserr
, _("while starting inode scan %d"), retval
);
271 if ((count
++ % 1024) == 0)
272 vfs_print_message (_("undelfs: loading deleted files information %d inodes"), count
);
273 if (inode
.i_dtime
== 0)
276 if (S_ISDIR (inode
.i_mode
))
284 retval
= ext2fs_block_iterate (fs
, ino
, 0, block_buf
, undelfs_lsdel_proc
, &lsd
);
287 message (D_ERROR
, undelfserr
, _("while calling ext2_block_iterate %d"), retval
);
290 if (lsd
.free_blocks
&& !lsd
.bad_blocks
)
292 if (num_delarray
>= max_delarray
)
294 struct deleted_info
*delarray_new
= g_try_realloc (delarray
,
295 sizeof (struct deleted_info
) *
296 (max_delarray
+ 50));
299 message (D_ERROR
, undelfserr
, _("no more memory while reallocating array"));
302 delarray
= delarray_new
;
306 delarray
[num_delarray
].ino
= ino
;
307 delarray
[num_delarray
].mode
= inode
.i_mode
;
308 delarray
[num_delarray
].uid
= inode
.i_uid
;
309 delarray
[num_delarray
].gid
= inode
.i_gid
;
310 delarray
[num_delarray
].size
= inode
.i_size
;
311 delarray
[num_delarray
].dtime
= inode
.i_dtime
;
312 delarray
[num_delarray
].num_blocks
= lsd
.num_blocks
;
313 delarray
[num_delarray
].free_blocks
= lsd
.free_blocks
;
318 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
321 message (D_ERROR
, undelfserr
, _("while doing inode scan %d"), retval
);
325 readdir_ptr
= READDIR_PTR_INIT
;
326 ext2fs_close_inode_scan (scan
);
330 ext2fs_close_inode_scan (scan
);
340 /* --------------------------------------------------------------------------------------------- */
343 undelfs_opendir (const vfs_path_t
* vpath
)
346 vfs_path_element_t
*path_element
;
348 path_element
= vfs_path_get_by_index (vpath
, -1);
349 undelfs_get_path (vpath
, &file
, &f
);
353 /* We don't use the file name */
356 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
363 /* To avoid expensive re-scannings */
364 readdir_ptr
= READDIR_PTR_INIT
;
369 if (ext2fs_open (ext2_fname
, 0, 0, 0, unix_io_manager
, &fs
))
371 message (D_ERROR
, undelfserr
, _("Cannot open file %s"), ext2_fname
);
374 vfs_print_message (_("undelfs: reading inode bitmap..."));
375 if (ext2fs_read_inode_bitmap (fs
))
377 message (D_ERROR
, undelfserr
, _("Cannot load inode bitmap from:\n%s"), ext2_fname
);
380 vfs_print_message (_("undelfs: reading block bitmap..."));
381 if (ext2fs_read_block_bitmap (fs
))
383 message (D_ERROR
, undelfserr
, _("Cannot load block bitmap from:\n%s"), ext2_fname
);
386 /* Now load the deleted information */
387 if (!undelfs_loaddel ())
389 vfs_print_message (_("%s: done."), path_element
->class->name
);
392 vfs_print_message (_("%s: failure"), path_element
->class->name
);
398 /* --------------------------------------------------------------------------------------------- */
401 undelfs_readdir (void *vfs_info
)
403 static union vfs_dirent undelfs_readdir_data
;
404 static char *const dirent_dest
= undelfs_readdir_data
.dent
.d_name
;
408 message (D_ERROR
, undelfserr
, _("vfs_info is not fs!"));
411 if (readdir_ptr
== num_delarray
)
414 strcpy (dirent_dest
, readdir_ptr
== -2 ? "." : "..");
416 g_snprintf (dirent_dest
, MC_MAXPATHLEN
, "%ld:%d",
417 (long) delarray
[readdir_ptr
].ino
, delarray
[readdir_ptr
].num_blocks
);
420 compute_namelen (&undelfs_readdir_data
.dent
);
422 return &undelfs_readdir_data
;
425 /* --------------------------------------------------------------------------------------------- */
428 undelfs_closedir (void *vfs_info
)
434 /* --------------------------------------------------------------------------------------------- */
435 /* We do not support lseek */
438 undelfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
442 undelfs_file
*p
= NULL
;
446 /* Only allow reads on this file system */
447 undelfs_get_path (vpath
, &file
, &f
);
451 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
453 message (D_ERROR
, undelfserr
, _("You have to chdir to extract files first"));
460 /* Search the file into delarray */
461 for (i
= 0; i
< (ext2_ino_t
) num_delarray
; i
++)
463 if (inode
!= delarray
[i
].ino
)
466 /* Found: setup all the structures needed by read */
467 p
= (undelfs_file
*) g_try_malloc (((gsize
) sizeof (undelfs_file
)));
474 p
->buf
= g_try_malloc (fs
->blocksize
);
487 p
->size
= delarray
[i
].size
;
495 /* --------------------------------------------------------------------------------------------- */
498 undelfs_close (void *vfs_info
)
500 undelfs_file
*p
= vfs_info
;
507 /* --------------------------------------------------------------------------------------------- */
510 undelfs_dump_read (ext2_filsys param_fs
, blk_t
* blocknr
, int blockcnt
, void *private)
513 undelfs_file
*p
= (undelfs_file
*) private;
520 p
->error_code
= io_channel_read_blk (param_fs
->io
, *blocknr
, 1, p
->buf
);
525 memset (p
->buf
, 0, param_fs
->blocksize
);
527 if (p
->pos
+ (off_t
) p
->count
< p
->current
)
532 if (p
->pos
> p
->current
+ param_fs
->blocksize
)
534 p
->current
+= param_fs
->blocksize
;
535 return 0; /* we have not arrived yet */
538 /* Now, we know we have to extract some data */
539 if (p
->pos
>= p
->current
)
542 /* First case: starting pointer inside this block */
543 if (p
->pos
+ (off_t
) p
->count
<= p
->current
+ param_fs
->blocksize
)
545 /* Fully contained */
546 copy_count
= p
->count
;
547 p
->finished
= (p
->count
!= 0);
551 /* Still some more data */
552 copy_count
= param_fs
->blocksize
- (p
->pos
- p
->current
);
554 memcpy (p
->dest_buffer
, p
->buf
+ (p
->pos
- p
->current
), copy_count
);
558 /* Second case: we already have passed p->pos */
559 if (p
->pos
+ (off_t
) p
->count
< p
->current
+ param_fs
->blocksize
)
561 copy_count
= (p
->pos
+ p
->count
) - p
->current
;
562 p
->finished
= (p
->count
!= 0);
566 copy_count
= param_fs
->blocksize
;
568 memcpy (p
->dest_buffer
, p
->buf
, copy_count
);
570 p
->dest_buffer
+= copy_count
;
571 p
->current
+= param_fs
->blocksize
;
579 /* --------------------------------------------------------------------------------------------- */
582 undelfs_read (void *vfs_info
, char *buffer
, size_t count
)
584 undelfs_file
*p
= vfs_info
;
587 p
->dest_buffer
= buffer
;
592 if (p
->pos
+ (off_t
) p
->count
> p
->size
)
594 p
->count
= p
->size
- p
->pos
;
596 retval
= ext2fs_block_iterate (fs
, p
->inode
, 0, NULL
, undelfs_dump_read
, p
);
599 message (D_ERROR
, undelfserr
, _("while iterating over blocks"));
602 if (p
->error_code
&& !p
->finished
)
604 p
->pos
= p
->pos
+ (p
->dest_buffer
- buffer
);
605 return p
->dest_buffer
- buffer
;
608 /* --------------------------------------------------------------------------------------------- */
611 undelfs_getindex (char *path
)
613 ext2_ino_t inode
= atol (path
);
616 for (i
= 0; i
< num_delarray
; i
++)
618 if (delarray
[i
].ino
== inode
)
624 /* --------------------------------------------------------------------------------------------- */
627 undelfs_stat_int (int inode_index
, struct stat
*buf
)
630 buf
->st_ino
= delarray
[inode_index
].ino
;
631 buf
->st_mode
= delarray
[inode_index
].mode
;
633 buf
->st_uid
= delarray
[inode_index
].uid
;
634 buf
->st_gid
= delarray
[inode_index
].gid
;
635 buf
->st_size
= delarray
[inode_index
].size
;
636 buf
->st_atime
= delarray
[inode_index
].dtime
;
637 buf
->st_ctime
= delarray
[inode_index
].dtime
;
638 buf
->st_mtime
= delarray
[inode_index
].dtime
;
642 /* --------------------------------------------------------------------------------------------- */
645 undelfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
650 undelfs_get_path (vpath
, &file
, &f
);
654 /* When called from save_cwd_stats we get an incorrect file and f here:
655 e.g. incorrect correct
656 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
657 file = "/dev" file="/dev/sda1"
658 f = "sda1" f ="401:1"
659 If the first char in f is no digit -> return error */
667 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
669 message (D_ERROR
, undelfserr
, _("You have to chdir to extract files first"));
674 inode_index
= undelfs_getindex (f
);
678 if (inode_index
== -1)
681 return undelfs_stat_int (inode_index
, buf
);
684 /* --------------------------------------------------------------------------------------------- */
687 undelfs_fstat (void *vfs_info
, struct stat
*buf
)
689 undelfs_file
*p
= vfs_info
;
691 return undelfs_stat_int (p
->f_index
, buf
);
694 /* --------------------------------------------------------------------------------------------- */
697 undelfs_chdir (const vfs_path_t
* vpath
)
702 undelfs_get_path (vpath
, &file
, &f
);
706 /* We may use access because ext2 file systems are local */
707 /* this could be fixed by making an ext2fs io manager to use */
708 /* our vfs, but that is left as an excercise for the reader */
709 fd
= open (file
, O_RDONLY
);
712 message (D_ERROR
, undelfserr
, _("Cannot open file \"%s\""), file
);
723 /* --------------------------------------------------------------------------------------------- */
725 /* this has to stay here for now: vfs layer does not know how to emulate it */
727 undelfs_lseek (void *vfs_info
, off_t offset
, int whence
)
736 /* --------------------------------------------------------------------------------------------- */
739 undelfs_getid (const vfs_path_t
* vpath
)
741 char *fname
, *fsname
;
743 undelfs_get_path (vpath
, &fsname
, &fname
);
752 /* --------------------------------------------------------------------------------------------- */
755 undelfs_nothingisopen (vfsid id
)
759 return !undelfs_usage
;
762 /* --------------------------------------------------------------------------------------------- */
765 undelfs_free (vfsid id
)
772 /* --------------------------------------------------------------------------------------------- */
776 undelfs_init (struct vfs_class
*me
)
780 undelfserr
= _(undelfserr
);
784 #define undelfs_init NULL
787 /* --------------------------------------------------------------------------------------------- */
788 /*** public functions ****************************************************************************/
789 /* --------------------------------------------------------------------------------------------- */
791 * This function overrides com_err() from libcom_err library.
792 * It is used in libext2fs to report errors.
796 com_err (const char *whoami
, long err_code
, const char *fmt
, ...)
802 str
= g_strdup_vprintf (fmt
, ap
);
805 message (D_ERROR
, _("Ext2lib error"), "%s (%s: %ld)", str
, whoami
, err_code
);
809 /* --------------------------------------------------------------------------------------------- */
814 vfs_undelfs_ops
.name
= "undelfs";
815 vfs_undelfs_ops
.prefix
= "undel";
816 vfs_undelfs_ops
.init
= undelfs_init
;
817 vfs_undelfs_ops
.open
= undelfs_open
;
818 vfs_undelfs_ops
.close
= undelfs_close
;
819 vfs_undelfs_ops
.read
= undelfs_read
;
820 vfs_undelfs_ops
.opendir
= undelfs_opendir
;
821 vfs_undelfs_ops
.readdir
= undelfs_readdir
;
822 vfs_undelfs_ops
.closedir
= undelfs_closedir
;
823 vfs_undelfs_ops
.stat
= undelfs_stat
;
824 vfs_undelfs_ops
.lstat
= undelfs_lstat
;
825 vfs_undelfs_ops
.fstat
= undelfs_fstat
;
826 vfs_undelfs_ops
.chdir
= undelfs_chdir
;
827 vfs_undelfs_ops
.lseek
= undelfs_lseek
;
828 vfs_undelfs_ops
.getid
= undelfs_getid
;
829 vfs_undelfs_ops
.nothingisopen
= undelfs_nothingisopen
;
830 vfs_undelfs_ops
.free
= undelfs_free
;
831 vfs_register_class (&vfs_undelfs_ops
);
834 /* --------------------------------------------------------------------------------------------- */