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.
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. */
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
43 #ifdef HAVE_EXT2FS_EXT2_FS_H
44 #include <ext2fs/ext2_fs.h>
46 /* asm/types.h defines its own umode_t */
48 #include <linux/ext2_fs.h>
51 #include <ext2fs/ext2fs.h>
54 #include <mhl/memory.h>
55 #include <mhl/string.h>
57 #include "../src/global.h"
58 #include "../src/tty.h" /* enable/disable interrupt key */
59 #include "../src/wtools.h" /* message() */
60 #include "../src/main.h" /* print_vfs_message */
83 /* We only allow one opened ext2fs */
84 static char *ext2_fname
;
85 static ext2_filsys fs
= NULL
;
86 static struct lsdel_struct lsd
;
87 static struct deleted_info
*delarray
;
88 static int num_delarray
, max_delarray
;
89 static char *block_buf
;
90 static const char *undelfserr
= N_(" undelfs: error ");
91 static int readdir_ptr
;
92 static int undelfs_usage
;
93 static struct vfs_class vfs_undelfs_ops
;
95 /* To generate the . and .. entries use -2 */
96 #define READDIR_PTR_INIT 0
99 undelfs_shutdown (void)
113 undelfs_get_path (const char *dirname
, char **fsname
, char **file
)
117 /* To look like filesystem, we have virtual directories
118 /#undel:XXX, which have no subdirectories. XXX is replaced with
119 hda5, sdb8 etc, which is assumed to live under /dev.
124 if (strncmp (dirname
, "/#undel:", 8))
129 /* Since we don't allow subdirectories, it's easy to get a filename,
130 * just scan backwards for a slash */
134 p
= dirname
+ strlen (dirname
);
138 if (p
- dirname
> 2 && *(p
-1) == '/' && *(p
-2) == '.')
146 *file
= mhl_str_dup (p
+1);
147 tmp
= g_strndup (dirname
, p
- dirname
);
148 *fsname
= g_strconcat ("/dev/", tmp
, (char *) NULL
);
154 *file
= mhl_str_dup ("");
155 *fsname
= g_strconcat ("/dev/", dirname
, (char *) NULL
);
160 undelfs_lsdel_proc(ext2_filsys fs
, blk_t
*block_nr
, int blockcnt
, void *private)
162 struct lsdel_struct
*lsd
= (struct lsdel_struct
*) private;
166 if (*block_nr
< fs
->super
->s_first_data_block
||
167 *block_nr
>= fs
->super
->s_blocks_count
) {
172 if (!ext2fs_test_block_bitmap(fs
->block_map
,*block_nr
))
179 * Load information about deleted files.
180 * Don't abort if there is not enough memory - load as much as we can.
183 undelfs_loaddel (void)
187 struct ext2_inode inode
;
188 ext2_inode_scan scan
;
192 delarray
= g_try_malloc (sizeof (struct deleted_info
) * max_delarray
);
194 message (D_ERROR
, undelfserr
, _(" not enough memory "));
197 block_buf
= g_try_malloc (fs
->blocksize
* 3);
199 message (D_ERROR
, undelfserr
, _(" while allocating block buffer "));
202 if ((retval
= ext2fs_open_inode_scan (fs
, 0, &scan
))) {
203 message (D_ERROR
, undelfserr
, _(" open_inode_scan: %d "), retval
);
206 if ((retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
))) {
207 message (D_ERROR
, undelfserr
, _(" while starting inode scan %d "),
214 if ((count
++ % 1024) == 0)
216 ("undelfs: loading deleted files information %d inodes"),
218 if (inode
.i_dtime
== 0)
221 if (S_ISDIR (inode
.i_mode
))
230 ext2fs_block_iterate (fs
, ino
, 0, block_buf
,
231 undelfs_lsdel_proc
, &lsd
);
233 message (D_ERROR
, undelfserr
,
234 _(" while calling ext2_block_iterate %d "), retval
);
237 if (lsd
.free_blocks
&& !lsd
.bad_blocks
) {
238 if (num_delarray
>= max_delarray
) {
239 struct deleted_info
*delarray_new
=
240 g_try_realloc (delarray
,
241 sizeof (struct deleted_info
) *
242 (max_delarray
+ 50));
244 message (D_ERROR
, undelfserr
,
246 (" no more memory while reallocating array "));
249 delarray
= delarray_new
;
253 delarray
[num_delarray
].ino
= ino
;
254 delarray
[num_delarray
].mode
= inode
.i_mode
;
255 delarray
[num_delarray
].uid
= inode
.i_uid
;
256 delarray
[num_delarray
].gid
= inode
.i_gid
;
257 delarray
[num_delarray
].size
= inode
.i_size
;
258 delarray
[num_delarray
].dtime
= inode
.i_dtime
;
259 delarray
[num_delarray
].num_blocks
= lsd
.num_blocks
;
260 delarray
[num_delarray
].free_blocks
= lsd
.free_blocks
;
265 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
267 message (D_ERROR
, undelfserr
, _(" while doing inode scan %d "),
272 readdir_ptr
= READDIR_PTR_INIT
;
273 ext2fs_close_inode_scan (scan
);
277 ext2fs_close_inode_scan (scan
);
289 * This function overrides com_err() from libcom_err library.
290 * It is used in libext2fs to report errors.
293 com_err (const char *whoami
, long err_code
, const char *fmt
, ...)
299 str
= g_strdup_vprintf (fmt
, ap
);
302 message (D_ERROR
, _(" Ext2lib error "), " %s (%s: %ld) ", str
, whoami
,
308 undelfs_opendir (struct vfs_class
*me
, const char *dirname
)
312 undelfs_get_path (dirname
, &file
, &f
);
316 /* We don't use the file name */
319 if (!ext2_fname
|| strcmp (ext2_fname
, file
)){
323 /* To avoid expensive re-scannings */
324 readdir_ptr
= READDIR_PTR_INIT
;
329 if (ext2fs_open (ext2_fname
, 0, 0, 0, unix_io_manager
, &fs
)){
330 message (D_ERROR
, undelfserr
, _(" Cannot open file %s "), ext2_fname
);
333 print_vfs_message (_("undelfs: reading inode bitmap..."));
334 if (ext2fs_read_inode_bitmap (fs
)){
335 message (D_ERROR
, undelfserr
,
336 _(" Cannot load inode bitmap from: \n %s \n"), ext2_fname
);
339 print_vfs_message (_("undelfs: reading block bitmap..."));
340 if (ext2fs_read_block_bitmap (fs
)){
341 message (D_ERROR
, undelfserr
,
342 _(" Cannot load block bitmap from: \n %s \n"), ext2_fname
);
345 /* Now load the deleted information */
346 if (!undelfs_loaddel ())
348 print_vfs_message (_("%s: done."), me
->name
);
351 print_vfs_message (_("%s: failure"), me
->name
);
359 undelfs_readdir(void *vfs_info
)
361 static union vfs_dirent undelfs_readdir_data
;
362 static char *const dirent_dest
= undelfs_readdir_data
.dent
.d_name
;
364 if (vfs_info
!= fs
) {
365 message (D_ERROR
, undelfserr
, _(" vfs_info is not fs! "));
368 if (readdir_ptr
== num_delarray
)
371 strcpy (dirent_dest
, readdir_ptr
== -2 ? "." : "..");
373 snprintf(dirent_dest
, MC_MAXPATHLEN
, "%ld:%d",
374 (long) delarray
[readdir_ptr
].ino
,
375 delarray
[readdir_ptr
].num_blocks
);
378 compute_namelen(&undelfs_readdir_data
.dent
);
380 return &undelfs_readdir_data
;
384 undelfs_closedir (void *vfs_info
)
390 int f_index
; /* file index into delarray */
392 int error_code
; /* */
393 int pos
; /* file position */
394 int current
; /* used to determine current position in itereate */
400 /* Used by undelfs_read: */
401 char *dest_buffer
; /* destination buffer */
402 int count
; /* bytes to read */
405 /* We do not support lseek */
407 undelfs_open (struct vfs_class
*me
, const char *fname
, int flags
, int mode
)
411 undelfs_file
*p
= NULL
;
413 /* Only allow reads on this file system */
414 undelfs_get_path (fname
, &file
, &f
);
418 if (!ext2_fname
|| strcmp (ext2_fname
, file
)) {
419 message (D_ERROR
, undelfserr
,
420 _(" You have to chdir to extract files first "));
427 /* Search the file into delarray */
428 for (i
= 0; i
< num_delarray
; i
++) {
429 if (inode
!= delarray
[i
].ino
)
432 /* Found: setup all the structures needed by read */
433 p
= (undelfs_file
*) g_try_malloc (((gsize
) sizeof (undelfs_file
)));
439 p
->buf
= g_try_malloc (fs
->blocksize
);
451 p
->size
= delarray
[i
].size
;
460 undelfs_close (void *vfs_info
)
462 undelfs_file
*p
= vfs_info
;
470 undelfs_dump_read(ext2_filsys fs
, blk_t
*blocknr
, int blockcnt
, void *private)
473 undelfs_file
*p
= (undelfs_file
*) private;
479 p
->error_code
= io_channel_read_blk(fs
->io
, *blocknr
,
484 memset(p
->buf
, 0, fs
->blocksize
);
486 if (p
->pos
+ p
->count
< p
->current
){
490 if (p
->pos
> p
->current
+ fs
->blocksize
){
491 p
->current
+= fs
->blocksize
;
492 return 0; /* we have not arrived yet */
495 /* Now, we know we have to extract some data */
496 if (p
->pos
>= p
->current
){
498 /* First case: starting pointer inside this block */
499 if (p
->pos
+ p
->count
<= p
->current
+ fs
->blocksize
){
500 /* Fully contained */
501 copy_count
= p
->count
;
502 p
->finished
= p
->count
;
504 /* Still some more data */
505 copy_count
= fs
->blocksize
-(p
->pos
-p
->current
);
507 memcpy (p
->dest_buffer
, p
->buf
+ (p
->pos
-p
->current
), copy_count
);
509 /* Second case: we already have passed p->pos */
510 if (p
->pos
+p
->count
< p
->current
+fs
->blocksize
){
511 copy_count
= (p
->pos
+ p
->count
) - p
->current
;
512 p
->finished
= p
->count
;
514 copy_count
= fs
->blocksize
;
516 memcpy (p
->dest_buffer
, p
->buf
, copy_count
);
518 p
->dest_buffer
+= copy_count
;
519 p
->current
+= fs
->blocksize
;
527 undelfs_read (void *vfs_info
, char *buffer
, int count
)
529 undelfs_file
*p
= vfs_info
;
532 p
->dest_buffer
= buffer
;
537 if (p
->pos
+ p
->count
> p
->size
){
538 p
->count
= p
->size
- p
->pos
;
540 retval
= ext2fs_block_iterate(fs
, p
->inode
, 0, NULL
,
541 undelfs_dump_read
, p
);
543 message (D_ERROR
, undelfserr
, _(" while iterating over blocks "));
546 if (p
->error_code
&& !p
->finished
)
548 p
->pos
= p
->pos
+ (p
->dest_buffer
- buffer
);
549 return p
->dest_buffer
- buffer
;
553 undelfs_getindex (char *path
)
555 ext2_ino_t inode
= atol (path
);
558 for (i
= 0; i
< num_delarray
; i
++){
559 if (delarray
[i
].ino
== inode
)
566 undelfs_stat_int (int inode_index
, struct stat
*buf
)
569 buf
->st_ino
= delarray
[inode_index
].ino
;
570 buf
->st_mode
= delarray
[inode_index
].mode
;
572 buf
->st_uid
= delarray
[inode_index
].uid
;
573 buf
->st_gid
= delarray
[inode_index
].gid
;
574 buf
->st_size
= delarray
[inode_index
].size
;
575 buf
->st_atime
= delarray
[inode_index
].dtime
;
576 buf
->st_ctime
= delarray
[inode_index
].dtime
;
577 buf
->st_mtime
= delarray
[inode_index
].dtime
;
582 undelfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
587 undelfs_get_path (path
, &file
, &f
);
591 /* When called from save_cwd_stats we get an incorrect file and f here:
592 e.g. incorrect correct
593 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
594 file = "/dev" file="/dev/sda1"
595 f = "sda1" f ="401:1"
596 If the first char in f is no digit -> return error */
603 if (!ext2_fname
|| strcmp (ext2_fname
, file
)){
604 message (D_ERROR
, undelfserr
, _(" You have to chdir to extract files first "));
609 inode_index
= undelfs_getindex (f
);
613 if (inode_index
== -1)
616 return undelfs_stat_int (inode_index
, buf
);
619 #define undelfs_stat undelfs_lstat
622 undelfs_fstat (void *vfs_info
, struct stat
*buf
)
624 undelfs_file
*p
= vfs_info
;
626 return undelfs_stat_int (p
->f_index
, buf
);
630 undelfs_chdir(struct vfs_class
*me
, const char *path
)
635 undelfs_get_path (path
, &file
, &f
);
639 /* We may use access because ext2 file systems are local */
640 /* this could be fixed by making an ext2fs io manager to use */
641 /* our vfs, but that is left as an excercise for the reader */
642 if ((fd
= open (file
, O_RDONLY
)) == -1){
643 message (D_ERROR
, undelfserr
, _(" Cannot open file %s "), file
);
654 /* this has to stay here for now: vfs layer does not know how to emulate it */
656 undelfs_lseek(void *vfs_info
, off_t offset
, int whence
)
662 undelfs_getid (struct vfs_class
*me
, const char *path
)
664 char *fname
, *fsname
;
666 undelfs_get_path (path
, &fsname
, &fname
);
676 undelfs_nothingisopen(vfsid id
)
678 return !undelfs_usage
;
682 undelfs_free(vfsid id
)
689 undelfs_init(struct vfs_class
*me
) {
690 undelfserr
= _(undelfserr
);
694 #define undelfs_init NULL
700 vfs_undelfs_ops
.name
= "undelfs";
701 vfs_undelfs_ops
.prefix
= "undel:";
702 vfs_undelfs_ops
.init
= undelfs_init
;
703 vfs_undelfs_ops
.open
= undelfs_open
;
704 vfs_undelfs_ops
.close
= undelfs_close
;
705 vfs_undelfs_ops
.read
= undelfs_read
;
706 vfs_undelfs_ops
.opendir
= undelfs_opendir
;
707 vfs_undelfs_ops
.readdir
= undelfs_readdir
;
708 vfs_undelfs_ops
.closedir
= undelfs_closedir
;
709 vfs_undelfs_ops
.stat
= undelfs_stat
;
710 vfs_undelfs_ops
.lstat
= undelfs_lstat
;
711 vfs_undelfs_ops
.fstat
= undelfs_fstat
;
712 vfs_undelfs_ops
.chdir
= undelfs_chdir
;
713 vfs_undelfs_ops
.lseek
= undelfs_lseek
;
714 vfs_undelfs_ops
.getid
= undelfs_getid
;
715 vfs_undelfs_ops
.nothingisopen
= undelfs_nothingisopen
;
716 vfs_undelfs_ops
.free
= undelfs_free
;
717 vfs_register_class (&vfs_undelfs_ops
);