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-2018
11 Free Software Foundation, Inc.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 * \brief Source: UnDel File System
40 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
41 * 2. Files are on the local file system (we do not support vfs files
42 * because we would have to provide an io_manager for the ext2fs tools,
43 * and I don't think it would be too useful to undelete files
52 #ifdef HAVE_EXT2FS_EXT2_FS_H
53 #include <ext2fs/ext2_fs.h>
55 /* asm/types.h defines its own umode_t */
57 #include <linux/ext2_fs.h>
60 #include <ext2fs/ext2fs.h>
63 #include "lib/global.h"
66 #include "lib/widget.h" /* message() */
67 #include "lib/vfs/utilvfs.h"
68 #include "lib/vfs/vfs.h"
72 /*** global variables ****************************************************************************/
74 /*** file scope macro definitions ****************************************************************/
76 /* To generate the . and .. entries use -2 */
77 #define READDIR_PTR_INIT 0
79 #define undelfs_stat undelfs_lstat
81 /*** file scope type declarations ****************************************************************/
105 int f_index
; /* file index into delarray */
107 int error_code
; /* */
108 off_t pos
; /* file position */
109 off_t current
; /* used to determine current position in itereate */
115 /* Used by undelfs_read: */
116 char *dest_buffer
; /* destination buffer */
117 size_t count
; /* bytes to read */
120 /*** file scope variables ************************************************************************/
122 /* We only allow one opened ext2fs */
123 static char *ext2_fname
;
124 static ext2_filsys fs
= NULL
;
125 static struct lsdel_struct lsd
;
126 static struct deleted_info
*delarray
;
127 static int num_delarray
, max_delarray
;
128 static char *block_buf
;
129 static const char *undelfserr
= N_("undelfs: error");
130 static int readdir_ptr
;
131 static int undelfs_usage
;
132 static struct vfs_class vfs_undelfs_ops
;
134 /*** file scope functions ************************************************************************/
135 /* --------------------------------------------------------------------------------------------- */
138 undelfs_shutdown (void)
143 MC_PTR_FREE (ext2_fname
);
144 MC_PTR_FREE (delarray
);
145 MC_PTR_FREE (block_buf
);
148 /* --------------------------------------------------------------------------------------------- */
151 undelfs_get_path (const vfs_path_t
* vpath
, char **fsname
, char **file
)
153 const char *p
, *dirname
;
154 const vfs_path_element_t
*path_element
;
156 path_element
= vfs_path_get_by_index (vpath
, -1);
158 /* To look like filesystem, we have virtual directories
159 undel://XXX, which have no subdirectories. XXX is replaced with
160 hda5, sdb8 etc, which is assumed to live under /dev.
163 dirname
= path_element
->path
;
167 if (strncmp (dirname
, "undel://", 8) != 0)
172 /* Since we don't allow subdirectories, it's easy to get a filename,
173 * just scan backwards for a slash */
177 p
= dirname
+ strlen (dirname
);
181 if (p
- dirname
> 2 && IS_PATH_SEP (p
[-1]) && p
[-2] == '.')
187 if (IS_PATH_SEP (*p
))
191 *file
= g_strdup (p
+ 1);
192 tmp
= g_strndup (dirname
, p
- dirname
);
193 *fsname
= g_strconcat ("/dev/", tmp
, (char *) NULL
);
199 *file
= g_strdup ("");
200 *fsname
= g_strconcat ("/dev/", dirname
, (char *) NULL
);
203 /* --------------------------------------------------------------------------------------------- */
206 undelfs_lsdel_proc (ext2_filsys _fs
, blk_t
* block_nr
, int blockcnt
, void *private)
208 struct lsdel_struct
*_lsd
= (struct lsdel_struct
*) private;
212 if (*block_nr
< _fs
->super
->s_first_data_block
|| *block_nr
>= _fs
->super
->s_blocks_count
)
218 if (!ext2fs_test_block_bitmap (_fs
->block_map
, *block_nr
))
224 /* --------------------------------------------------------------------------------------------- */
226 * Load information about deleted files.
227 * Don't abort if there is not enough memory - load as much as we can.
231 undelfs_loaddel (void)
235 struct ext2_inode inode
;
236 ext2_inode_scan scan
;
240 delarray
= g_try_malloc (sizeof (struct deleted_info
) * max_delarray
);
243 message (D_ERROR
, undelfserr
, "%s", _("not enough memory"));
246 block_buf
= g_try_malloc (fs
->blocksize
* 3);
249 message (D_ERROR
, undelfserr
, "%s", _("while allocating block buffer"));
252 retval
= ext2fs_open_inode_scan (fs
, 0, &scan
);
255 message (D_ERROR
, undelfserr
, _("open_inode_scan: %d"), retval
);
258 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
261 message (D_ERROR
, undelfserr
, _("while starting inode scan %d"), retval
);
267 if ((count
++ % 1024) == 0)
268 vfs_print_message (_("undelfs: loading deleted files information %d inodes"), count
);
269 if (inode
.i_dtime
== 0)
272 if (S_ISDIR (inode
.i_mode
))
280 retval
= ext2fs_block_iterate (fs
, ino
, 0, block_buf
, undelfs_lsdel_proc
, &lsd
);
283 message (D_ERROR
, undelfserr
, _("while calling ext2_block_iterate %d"), retval
);
286 if (lsd
.free_blocks
&& !lsd
.bad_blocks
)
288 if (num_delarray
>= max_delarray
)
290 struct deleted_info
*delarray_new
= g_try_realloc (delarray
,
291 sizeof (struct deleted_info
) *
292 (max_delarray
+ 50));
295 message (D_ERROR
, undelfserr
, "%s",
296 _("no more memory while reallocating array"));
299 delarray
= delarray_new
;
303 delarray
[num_delarray
].ino
= ino
;
304 delarray
[num_delarray
].mode
= inode
.i_mode
;
305 delarray
[num_delarray
].uid
= inode
.i_uid
;
306 delarray
[num_delarray
].gid
= inode
.i_gid
;
307 delarray
[num_delarray
].size
= inode
.i_size
;
308 delarray
[num_delarray
].dtime
= inode
.i_dtime
;
309 delarray
[num_delarray
].num_blocks
= lsd
.num_blocks
;
310 delarray
[num_delarray
].free_blocks
= lsd
.free_blocks
;
315 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
318 message (D_ERROR
, undelfserr
, _("while doing inode scan %d"), retval
);
322 readdir_ptr
= READDIR_PTR_INIT
;
323 ext2fs_close_inode_scan (scan
);
327 ext2fs_close_inode_scan (scan
);
329 MC_PTR_FREE (block_buf
);
331 MC_PTR_FREE (delarray
);
335 /* --------------------------------------------------------------------------------------------- */
338 undelfs_opendir (const vfs_path_t
* vpath
)
340 char *file
, *f
= NULL
;
341 const vfs_path_element_t
*path_element
;
343 path_element
= vfs_path_get_by_index (vpath
, -1);
344 undelfs_get_path (vpath
, &file
, &f
);
351 /* We don't use the file name */
354 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
361 /* To avoid expensive re-scannings */
362 readdir_ptr
= READDIR_PTR_INIT
;
367 if (ext2fs_open (ext2_fname
, 0, 0, 0, unix_io_manager
, &fs
))
369 message (D_ERROR
, undelfserr
, _("Cannot open file %s"), ext2_fname
);
372 vfs_print_message ("%s", _("undelfs: reading inode bitmap..."));
373 if (ext2fs_read_inode_bitmap (fs
))
375 message (D_ERROR
, undelfserr
, _("Cannot load inode bitmap from:\n%s"), ext2_fname
);
378 vfs_print_message ("%s", _("undelfs: reading block bitmap..."));
379 if (ext2fs_read_block_bitmap (fs
))
381 message (D_ERROR
, undelfserr
, _("Cannot load block bitmap from:\n%s"), ext2_fname
);
384 /* Now load the deleted information */
385 if (!undelfs_loaddel ())
387 vfs_print_message (_("%s: done."), path_element
->class->name
);
390 vfs_print_message (_("%s: failure"), path_element
->class->name
);
396 /* --------------------------------------------------------------------------------------------- */
399 undelfs_readdir (void *vfs_info
)
401 static union vfs_dirent undelfs_readdir_data
;
402 static char *const dirent_dest
= undelfs_readdir_data
.dent
.d_name
;
406 message (D_ERROR
, undelfserr
, "%s", _("vfs_info is not fs!"));
409 if (readdir_ptr
== num_delarray
)
412 strcpy (dirent_dest
, readdir_ptr
== -2 ? "." : "..");
414 g_snprintf (dirent_dest
, MC_MAXPATHLEN
, "%ld:%d",
415 (long) delarray
[readdir_ptr
].ino
, delarray
[readdir_ptr
].num_blocks
);
418 return &undelfs_readdir_data
;
421 /* --------------------------------------------------------------------------------------------- */
424 undelfs_closedir (void *vfs_info
)
430 /* --------------------------------------------------------------------------------------------- */
431 /* We do not support lseek */
434 undelfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
436 char *file
, *f
= NULL
;
438 undelfs_file
*p
= NULL
;
442 /* Only allow reads on this file system */
443 undelfs_get_path (vpath
, &file
, &f
);
450 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
452 message (D_ERROR
, undelfserr
, "%s", _("You have to chdir to extract files first"));
459 /* Search the file into delarray */
460 for (i
= 0; i
< (ext2_ino_t
) num_delarray
; i
++)
462 if (inode
!= delarray
[i
].ino
)
465 /* Found: setup all the structures needed by read */
466 p
= (undelfs_file
*) g_try_malloc (((gsize
) sizeof (undelfs_file
)));
473 p
->buf
= g_try_malloc (fs
->blocksize
);
486 p
->size
= delarray
[i
].size
;
494 /* --------------------------------------------------------------------------------------------- */
497 undelfs_close (void *vfs_info
)
499 undelfs_file
*p
= vfs_info
;
506 /* --------------------------------------------------------------------------------------------- */
509 undelfs_dump_read (ext2_filsys param_fs
, blk_t
* blocknr
, int blockcnt
, void *private)
512 undelfs_file
*p
= (undelfs_file
*) private;
519 p
->error_code
= io_channel_read_blk (param_fs
->io
, *blocknr
, 1, p
->buf
);
524 memset (p
->buf
, 0, param_fs
->blocksize
);
526 if (p
->pos
+ (off_t
) p
->count
< p
->current
)
531 if (p
->pos
> p
->current
+ param_fs
->blocksize
)
533 p
->current
+= param_fs
->blocksize
;
534 return 0; /* we have not arrived yet */
537 /* Now, we know we have to extract some data */
538 if (p
->pos
>= p
->current
)
541 /* First case: starting pointer inside this block */
542 if (p
->pos
+ (off_t
) p
->count
<= p
->current
+ param_fs
->blocksize
)
544 /* Fully contained */
545 copy_count
= p
->count
;
546 p
->finished
= (p
->count
!= 0);
550 /* Still some more data */
551 copy_count
= param_fs
->blocksize
- (p
->pos
- p
->current
);
553 memcpy (p
->dest_buffer
, p
->buf
+ (p
->pos
- p
->current
), copy_count
);
557 /* Second case: we already have passed p->pos */
558 if (p
->pos
+ (off_t
) p
->count
< p
->current
+ param_fs
->blocksize
)
560 copy_count
= (p
->pos
+ p
->count
) - p
->current
;
561 p
->finished
= (p
->count
!= 0);
565 copy_count
= param_fs
->blocksize
;
567 memcpy (p
->dest_buffer
, p
->buf
, copy_count
);
569 p
->dest_buffer
+= copy_count
;
570 p
->current
+= param_fs
->blocksize
;
578 /* --------------------------------------------------------------------------------------------- */
581 undelfs_read (void *vfs_info
, char *buffer
, size_t count
)
583 undelfs_file
*p
= vfs_info
;
586 p
->dest_buffer
= buffer
;
591 if (p
->pos
+ (off_t
) p
->count
> p
->size
)
593 p
->count
= p
->size
- p
->pos
;
595 retval
= ext2fs_block_iterate (fs
, p
->inode
, 0, NULL
, undelfs_dump_read
, p
);
598 message (D_ERROR
, undelfserr
, "%s", _("while iterating over blocks"));
601 if (p
->error_code
&& !p
->finished
)
603 p
->pos
= p
->pos
+ (p
->dest_buffer
- buffer
);
604 return p
->dest_buffer
- buffer
;
607 /* --------------------------------------------------------------------------------------------- */
610 undelfs_getindex (char *path
)
612 ext2_ino_t inode
= atol (path
);
615 for (i
= 0; i
< num_delarray
; i
++)
617 if (delarray
[i
].ino
== inode
)
623 /* --------------------------------------------------------------------------------------------- */
626 undelfs_stat_int (int inode_index
, struct stat
*buf
)
629 buf
->st_ino
= delarray
[inode_index
].ino
;
630 buf
->st_mode
= delarray
[inode_index
].mode
;
632 buf
->st_uid
= delarray
[inode_index
].uid
;
633 buf
->st_gid
= delarray
[inode_index
].gid
;
634 buf
->st_size
= delarray
[inode_index
].size
;
635 buf
->st_atime
= delarray
[inode_index
].dtime
;
636 buf
->st_ctime
= delarray
[inode_index
].dtime
;
637 buf
->st_mtime
= delarray
[inode_index
].dtime
;
638 #ifdef HAVE_STRUCT_STAT_ST_MTIM
639 buf
->st_atim
.tv_nsec
= buf
->st_mtim
.tv_nsec
= buf
->st_ctim
.tv_nsec
= 0;
644 /* --------------------------------------------------------------------------------------------- */
647 undelfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
650 char *file
, *f
= NULL
;
652 undelfs_get_path (vpath
, &file
, &f
);
659 /* When called from save_cwd_stats we get an incorrect file and f here:
660 e.g. incorrect correct
661 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
662 file = "/dev" file="/dev/sda1"
663 f = "sda1" f ="401:1"
664 If the first char in f is no digit -> return error */
672 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
676 message (D_ERROR
, undelfserr
, "%s", _("You have to chdir to extract files first"));
679 inode_index
= undelfs_getindex (f
);
683 if (inode_index
== -1)
686 return undelfs_stat_int (inode_index
, buf
);
689 /* --------------------------------------------------------------------------------------------- */
692 undelfs_fstat (void *vfs_info
, struct stat
*buf
)
694 undelfs_file
*p
= vfs_info
;
696 return undelfs_stat_int (p
->f_index
, buf
);
699 /* --------------------------------------------------------------------------------------------- */
702 undelfs_chdir (const vfs_path_t
* vpath
)
704 char *file
, *f
= NULL
;
707 undelfs_get_path (vpath
, &file
, &f
);
714 /* We may use access because ext2 file systems are local */
715 /* this could be fixed by making an ext2fs io manager to use */
716 /* our vfs, but that is left as an exercise for the reader */
717 fd
= open (file
, O_RDONLY
);
720 message (D_ERROR
, undelfserr
, _("Cannot open file \"%s\""), file
);
731 /* --------------------------------------------------------------------------------------------- */
733 /* this has to stay here for now: vfs layer does not know how to emulate it */
735 undelfs_lseek (void *vfs_info
, off_t offset
, int whence
)
744 /* --------------------------------------------------------------------------------------------- */
747 undelfs_getid (const vfs_path_t
* vpath
)
749 char *fname
= NULL
, *fsname
;
752 undelfs_get_path (vpath
, &fsname
, &fname
);
758 return ok
? (vfsid
) fs
: NULL
;
761 /* --------------------------------------------------------------------------------------------- */
764 undelfs_nothingisopen (vfsid id
)
768 return !undelfs_usage
;
771 /* --------------------------------------------------------------------------------------------- */
774 undelfs_free (vfsid id
)
781 /* --------------------------------------------------------------------------------------------- */
785 undelfs_init (struct vfs_class
*me
)
789 undelfserr
= _(undelfserr
);
793 #define undelfs_init NULL
796 /* --------------------------------------------------------------------------------------------- */
797 /*** public functions ****************************************************************************/
798 /* --------------------------------------------------------------------------------------------- */
800 * This function overrides com_err() from libcom_err library.
801 * It is used in libext2fs to report errors.
805 com_err (const char *whoami
, long err_code
, const char *fmt
, ...)
811 str
= g_strdup_vprintf (fmt
, ap
);
814 message (D_ERROR
, _("Ext2lib error"), "%s (%s: %ld)", str
, whoami
, err_code
);
818 /* --------------------------------------------------------------------------------------------- */
823 vfs_undelfs_ops
.name
= "undelfs";
824 vfs_undelfs_ops
.prefix
= "undel";
825 vfs_undelfs_ops
.init
= undelfs_init
;
826 vfs_undelfs_ops
.open
= undelfs_open
;
827 vfs_undelfs_ops
.close
= undelfs_close
;
828 vfs_undelfs_ops
.read
= undelfs_read
;
829 vfs_undelfs_ops
.opendir
= undelfs_opendir
;
830 vfs_undelfs_ops
.readdir
= undelfs_readdir
;
831 vfs_undelfs_ops
.closedir
= undelfs_closedir
;
832 vfs_undelfs_ops
.stat
= undelfs_stat
;
833 vfs_undelfs_ops
.lstat
= undelfs_lstat
;
834 vfs_undelfs_ops
.fstat
= undelfs_fstat
;
835 vfs_undelfs_ops
.chdir
= undelfs_chdir
;
836 vfs_undelfs_ops
.lseek
= undelfs_lseek
;
837 vfs_undelfs_ops
.getid
= undelfs_getid
;
838 vfs_undelfs_ops
.nothingisopen
= undelfs_nothingisopen
;
839 vfs_undelfs_ops
.free
= undelfs_free
;
840 vfs_register_class (&vfs_undelfs_ops
);
843 /* --------------------------------------------------------------------------------------------- */