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-2023
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
50 #include <string.h> /* memset() */
51 #include <ext2fs/ext2_fs.h>
52 #include <ext2fs/ext2fs.h>
55 #include "lib/global.h"
58 #include "lib/widget.h" /* message() */
59 #include "lib/vfs/xdirentry.h"
60 #include "lib/vfs/utilvfs.h"
61 #include "lib/vfs/vfs.h"
65 /*** global variables ****************************************************************************/
67 /*** file scope macro definitions ****************************************************************/
69 /* To generate the . and .. entries use -2 */
70 #define READDIR_PTR_INIT 0
72 #define undelfs_stat undelfs_lstat
74 /*** file scope type declarations ****************************************************************/
98 int f_index
; /* file index into delarray */
100 int error_code
; /* */
101 off_t pos
; /* file position */
102 off_t current
; /* used to determine current position in itereate */
108 /* Used by undelfs_read: */
109 char *dest_buffer
; /* destination buffer */
110 size_t count
; /* bytes to read */
113 /*** forward declarations (file scope functions) *************************************************/
115 /*** file scope variables ************************************************************************/
117 /* We only allow one opened ext2fs */
118 static char *ext2_fname
;
119 static ext2_filsys fs
= NULL
;
120 static struct lsdel_struct lsd
;
121 static struct deleted_info
*delarray
;
122 static int num_delarray
, max_delarray
;
123 static char *block_buf
;
124 static const char *undelfserr
= N_("undelfs: error");
125 static int readdir_ptr
;
126 static int undelfs_usage
;
128 static struct vfs_s_subclass undelfs_subclass
;
129 static struct vfs_class
*vfs_undelfs_ops
= VFS_CLASS (&undelfs_subclass
);
131 /* --------------------------------------------------------------------------------------------- */
132 /*** file scope functions ************************************************************************/
133 /* --------------------------------------------------------------------------------------------- */
136 undelfs_shutdown (void)
141 MC_PTR_FREE (ext2_fname
);
142 MC_PTR_FREE (delarray
);
143 MC_PTR_FREE (block_buf
);
146 /* --------------------------------------------------------------------------------------------- */
149 undelfs_get_path (const vfs_path_t
* vpath
, char **fsname
, char **file
)
151 const char *p
, *dirname
;
153 dirname
= vfs_path_get_last_path_str (vpath
);
155 /* To look like filesystem, we have virtual directories
156 undel://XXX, which have no subdirectories. XXX is replaced with
157 hda5, sdb8 etc, which is assumed to live under /dev.
162 if (strncmp (dirname
, "undel://", 8) != 0)
167 /* Since we don't allow subdirectories, it's easy to get a filename,
168 * just scan backwards for a slash */
169 if (*dirname
== '\0')
172 p
= dirname
+ strlen (dirname
);
176 if (p
- dirname
> 2 && IS_PATH_SEP (p
[-1]) && p
[-2] == '.')
182 if (IS_PATH_SEP (*p
))
186 *file
= g_strdup (p
+ 1);
187 tmp
= g_strndup (dirname
, p
- dirname
);
188 *fsname
= g_strconcat ("/dev/", tmp
, (char *) NULL
);
194 *file
= g_strdup ("");
195 *fsname
= g_strconcat ("/dev/", dirname
, (char *) NULL
);
198 /* --------------------------------------------------------------------------------------------- */
201 undelfs_lsdel_proc (ext2_filsys _fs
, blk_t
* block_nr
, int blockcnt
, void *private)
203 struct lsdel_struct
*_lsd
= (struct lsdel_struct
*) private;
207 if (*block_nr
< _fs
->super
->s_first_data_block
|| *block_nr
>= _fs
->super
->s_blocks_count
)
213 if (!ext2fs_test_block_bitmap (_fs
->block_map
, *block_nr
))
219 /* --------------------------------------------------------------------------------------------- */
221 * Load information about deleted files.
222 * Don't abort if there is not enough memory - load as much as we can.
226 undelfs_loaddel (void)
230 struct ext2_inode inode
;
231 ext2_inode_scan scan
;
235 delarray
= g_try_malloc (sizeof (struct deleted_info
) * max_delarray
);
238 message (D_ERROR
, undelfserr
, "%s", _("not enough memory"));
241 block_buf
= g_try_malloc (fs
->blocksize
* 3);
244 message (D_ERROR
, undelfserr
, "%s", _("while allocating block buffer"));
247 retval
= ext2fs_open_inode_scan (fs
, 0, &scan
);
250 message (D_ERROR
, undelfserr
, _("open_inode_scan: %d"), retval
);
253 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
256 message (D_ERROR
, undelfserr
, _("while starting inode scan %d"), retval
);
262 if ((count
++ % 1024) == 0)
263 vfs_print_message (_("undelfs: loading deleted files information %d inodes"), count
);
264 if (inode
.i_dtime
== 0)
267 if (S_ISDIR (inode
.i_mode
))
275 retval
= ext2fs_block_iterate (fs
, ino
, 0, block_buf
, undelfs_lsdel_proc
, &lsd
);
278 message (D_ERROR
, undelfserr
, _("while calling ext2_block_iterate %d"), retval
);
281 if (lsd
.free_blocks
&& !lsd
.bad_blocks
)
283 if (num_delarray
>= max_delarray
)
285 struct deleted_info
*delarray_new
= g_try_realloc (delarray
,
286 sizeof (struct deleted_info
) *
287 (max_delarray
+ 50));
290 message (D_ERROR
, undelfserr
, "%s",
291 _("no more memory while reallocating array"));
294 delarray
= delarray_new
;
298 delarray
[num_delarray
].ino
= ino
;
299 delarray
[num_delarray
].mode
= inode
.i_mode
;
300 delarray
[num_delarray
].uid
= inode
.i_uid
;
301 delarray
[num_delarray
].gid
= inode
.i_gid
;
302 delarray
[num_delarray
].size
= inode
.i_size
;
303 delarray
[num_delarray
].dtime
= inode
.i_dtime
;
304 delarray
[num_delarray
].num_blocks
= lsd
.num_blocks
;
305 delarray
[num_delarray
].free_blocks
= lsd
.free_blocks
;
310 retval
= ext2fs_get_next_inode (scan
, &ino
, &inode
);
313 message (D_ERROR
, undelfserr
, _("while doing inode scan %d"), retval
);
317 readdir_ptr
= READDIR_PTR_INIT
;
318 ext2fs_close_inode_scan (scan
);
322 ext2fs_close_inode_scan (scan
);
324 MC_PTR_FREE (block_buf
);
326 MC_PTR_FREE (delarray
);
330 /* --------------------------------------------------------------------------------------------- */
333 undelfs_opendir (const vfs_path_t
* vpath
)
335 char *file
, *f
= NULL
;
336 const vfs_path_element_t
*path_element
;
338 path_element
= vfs_path_get_by_index (vpath
, -1);
339 undelfs_get_path (vpath
, &file
, &f
);
346 /* We don't use the file name */
349 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
356 /* To avoid expensive re-scannings */
357 readdir_ptr
= READDIR_PTR_INIT
;
362 if (ext2fs_open (ext2_fname
, 0, 0, 0, unix_io_manager
, &fs
))
364 message (D_ERROR
, undelfserr
, _("Cannot open file %s"), ext2_fname
);
367 vfs_print_message ("%s", _("undelfs: reading inode bitmap..."));
368 if (ext2fs_read_inode_bitmap (fs
))
370 message (D_ERROR
, undelfserr
, _("Cannot load inode bitmap from:\n%s"), ext2_fname
);
373 vfs_print_message ("%s", _("undelfs: reading block bitmap..."));
374 if (ext2fs_read_block_bitmap (fs
))
376 message (D_ERROR
, undelfserr
, _("Cannot load block bitmap from:\n%s"), ext2_fname
);
379 /* Now load the deleted information */
380 if (!undelfs_loaddel ())
382 vfs_print_message (_("%s: done."), path_element
->class->name
);
385 vfs_print_message (_("%s: failure"), path_element
->class->name
);
391 /* --------------------------------------------------------------------------------------------- */
393 static struct vfs_dirent
*
394 undelfs_readdir (void *vfs_info
)
396 struct vfs_dirent
*dirent
;
400 message (D_ERROR
, undelfserr
, "%s", _("vfs_info is not fs!"));
403 if (readdir_ptr
== num_delarray
)
406 dirent
= vfs_dirent_init (NULL
, readdir_ptr
== -2 ? "." : "..", 0); /* FIXME: inode */
409 char dirent_dest
[MC_MAXPATHLEN
];
411 g_snprintf (dirent_dest
, MC_MAXPATHLEN
, "%ld:%d",
412 (long) delarray
[readdir_ptr
].ino
, delarray
[readdir_ptr
].num_blocks
);
413 dirent
= vfs_dirent_init (NULL
, dirent_dest
, 0); /* FIXME: inode */
420 /* --------------------------------------------------------------------------------------------- */
423 undelfs_closedir (void *vfs_info
)
429 /* --------------------------------------------------------------------------------------------- */
430 /* We do not support lseek */
433 undelfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
435 char *file
, *f
= NULL
;
437 undelfs_file
*p
= NULL
;
441 /* Only allow reads on this file system */
442 undelfs_get_path (vpath
, &file
, &f
);
449 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
451 message (D_ERROR
, undelfserr
, "%s", _("You have to chdir to extract files first"));
458 /* Search the file into delarray */
459 for (i
= 0; i
< (ext2_ino_t
) num_delarray
; i
++)
461 if (inode
!= delarray
[i
].ino
)
464 /* Found: setup all the structures needed by read */
465 p
= (undelfs_file
*) g_try_malloc (((gsize
) sizeof (undelfs_file
)));
472 p
->buf
= g_try_malloc (fs
->blocksize
);
485 p
->size
= delarray
[i
].size
;
493 /* --------------------------------------------------------------------------------------------- */
496 undelfs_close (void *vfs_info
)
498 undelfs_file
*p
= vfs_info
;
505 /* --------------------------------------------------------------------------------------------- */
508 undelfs_dump_read (ext2_filsys param_fs
, blk_t
* blocknr
, int blockcnt
, void *private)
511 undelfs_file
*p
= (undelfs_file
*) private;
518 p
->error_code
= io_channel_read_blk (param_fs
->io
, *blocknr
, 1, p
->buf
);
523 memset (p
->buf
, 0, param_fs
->blocksize
);
525 if (p
->pos
+ (off_t
) p
->count
< p
->current
)
530 if (p
->pos
> p
->current
+ param_fs
->blocksize
)
532 p
->current
+= param_fs
->blocksize
;
533 return 0; /* we have not arrived yet */
536 /* Now, we know we have to extract some data */
537 if (p
->pos
>= p
->current
)
540 /* First case: starting pointer inside this block */
541 if (p
->pos
+ (off_t
) p
->count
<= p
->current
+ param_fs
->blocksize
)
543 /* Fully contained */
544 copy_count
= p
->count
;
545 p
->finished
= (p
->count
!= 0);
549 /* Still some more data */
550 copy_count
= param_fs
->blocksize
- (p
->pos
- p
->current
);
552 memcpy (p
->dest_buffer
, p
->buf
+ (p
->pos
- p
->current
), copy_count
);
556 /* Second case: we already have passed p->pos */
557 if (p
->pos
+ (off_t
) p
->count
< p
->current
+ param_fs
->blocksize
)
559 copy_count
= (p
->pos
+ p
->count
) - p
->current
;
560 p
->finished
= (p
->count
!= 0);
564 copy_count
= param_fs
->blocksize
;
566 memcpy (p
->dest_buffer
, p
->buf
, copy_count
);
568 p
->dest_buffer
+= copy_count
;
569 p
->current
+= param_fs
->blocksize
;
577 /* --------------------------------------------------------------------------------------------- */
580 undelfs_read (void *vfs_info
, char *buffer
, size_t count
)
582 undelfs_file
*p
= vfs_info
;
585 p
->dest_buffer
= buffer
;
590 if (p
->pos
+ (off_t
) p
->count
> p
->size
)
592 p
->count
= p
->size
- p
->pos
;
594 retval
= ext2fs_block_iterate (fs
, p
->inode
, 0, NULL
, undelfs_dump_read
, p
);
597 message (D_ERROR
, undelfserr
, "%s", _("while iterating over blocks"));
600 if (p
->error_code
&& !p
->finished
)
602 p
->pos
= p
->pos
+ (p
->dest_buffer
- buffer
);
603 return p
->dest_buffer
- buffer
;
606 /* --------------------------------------------------------------------------------------------- */
609 undelfs_getindex (char *path
)
611 ext2_ino_t inode
= atol (path
);
614 for (i
= 0; i
< num_delarray
; i
++)
616 if (delarray
[i
].ino
== inode
)
622 /* --------------------------------------------------------------------------------------------- */
625 undelfs_stat_int (int inode_index
, struct stat
*buf
)
628 buf
->st_ino
= delarray
[inode_index
].ino
;
629 buf
->st_mode
= delarray
[inode_index
].mode
;
631 buf
->st_uid
= delarray
[inode_index
].uid
;
632 buf
->st_gid
= delarray
[inode_index
].gid
;
633 buf
->st_size
= delarray
[inode_index
].size
;
634 buf
->st_atime
= delarray
[inode_index
].dtime
;
635 buf
->st_ctime
= delarray
[inode_index
].dtime
;
636 buf
->st_mtime
= delarray
[inode_index
].dtime
;
637 #ifdef HAVE_STRUCT_STAT_ST_MTIM
638 buf
->st_atim
.tv_nsec
= buf
->st_mtim
.tv_nsec
= buf
->st_ctim
.tv_nsec
= 0;
643 /* --------------------------------------------------------------------------------------------- */
646 undelfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
649 char *file
, *f
= NULL
;
651 undelfs_get_path (vpath
, &file
, &f
);
658 /* When called from save_cwd_stats we get an incorrect file and f here:
659 e.g. incorrect correct
660 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
661 file = "/dev" file="/dev/sda1"
662 f = "sda1" f ="401:1"
663 If the first char in f is no digit -> return error */
671 if (!ext2_fname
|| strcmp (ext2_fname
, file
))
675 message (D_ERROR
, undelfserr
, "%s", _("You have to chdir to extract files first"));
678 inode_index
= undelfs_getindex (f
);
682 if (inode_index
== -1)
685 return undelfs_stat_int (inode_index
, buf
);
688 /* --------------------------------------------------------------------------------------------- */
691 undelfs_fstat (void *vfs_info
, struct stat
*buf
)
693 undelfs_file
*p
= vfs_info
;
695 return undelfs_stat_int (p
->f_index
, buf
);
698 /* --------------------------------------------------------------------------------------------- */
701 undelfs_chdir (const vfs_path_t
* vpath
)
703 char *file
, *f
= NULL
;
706 undelfs_get_path (vpath
, &file
, &f
);
713 /* We may use access because ext2 file systems are local */
714 /* this could be fixed by making an ext2fs io manager to use */
715 /* our vfs, but that is left as an exercise for the reader */
716 fd
= open (file
, O_RDONLY
);
719 message (D_ERROR
, undelfserr
, _("Cannot open file \"%s\""), file
);
730 /* --------------------------------------------------------------------------------------------- */
732 /* this has to stay here for now: vfs layer does not know how to emulate it */
734 undelfs_lseek (void *vfs_info
, off_t offset
, int whence
)
743 /* --------------------------------------------------------------------------------------------- */
746 undelfs_getid (const vfs_path_t
* vpath
)
748 char *fname
= NULL
, *fsname
;
751 undelfs_get_path (vpath
, &fsname
, &fname
);
757 return ok
? (vfsid
) fs
: NULL
;
760 /* --------------------------------------------------------------------------------------------- */
763 undelfs_nothingisopen (vfsid id
)
767 return (undelfs_usage
== 0);
770 /* --------------------------------------------------------------------------------------------- */
773 undelfs_free (vfsid id
)
780 /* --------------------------------------------------------------------------------------------- */
784 undelfs_init (struct vfs_class
*me
)
788 undelfserr
= _(undelfserr
);
792 #define undelfs_init NULL
795 /* --------------------------------------------------------------------------------------------- */
796 /*** public functions ****************************************************************************/
797 /* --------------------------------------------------------------------------------------------- */
799 * This function overrides com_err() from libcom_err library.
800 * It is used in libext2fs to report errors.
804 com_err (const char *whoami
, long err_code
, const char *fmt
, ...)
810 str
= g_strdup_vprintf (fmt
, ap
);
813 message (D_ERROR
, _("Ext2lib error"), "%s (%s: %ld)", str
, whoami
, err_code
);
817 /* --------------------------------------------------------------------------------------------- */
820 vfs_init_undelfs (void)
822 /* NULLize vfs_s_subclass members */
823 memset (&undelfs_subclass
, 0, sizeof (undelfs_subclass
));
825 vfs_init_class (vfs_undelfs_ops
, "undelfs", VFSF_UNKNOWN
, "undel");
826 vfs_undelfs_ops
->init
= undelfs_init
;
827 vfs_undelfs_ops
->open
= undelfs_open
;
828 vfs_undelfs_ops
->close
= undelfs_close
;
829 vfs_undelfs_ops
->read
= undelfs_read
;
830 vfs_undelfs_ops
->opendir
= undelfs_opendir
;
831 vfs_undelfs_ops
->readdir
= undelfs_readdir
;
832 vfs_undelfs_ops
->closedir
= undelfs_closedir
;
833 vfs_undelfs_ops
->stat
= undelfs_stat
;
834 vfs_undelfs_ops
->lstat
= undelfs_lstat
;
835 vfs_undelfs_ops
->fstat
= undelfs_fstat
;
836 vfs_undelfs_ops
->chdir
= undelfs_chdir
;
837 vfs_undelfs_ops
->lseek
= undelfs_lseek
;
838 vfs_undelfs_ops
->getid
= undelfs_getid
;
839 vfs_undelfs_ops
->nothingisopen
= undelfs_nothingisopen
;
840 vfs_undelfs_ops
->free
= undelfs_free
;
841 vfs_register_class (vfs_undelfs_ops
);
844 /* --------------------------------------------------------------------------------------------- */