Partially revert "VFS: (vfs_s_subclass): make the derived class from vfs_class."
[midnight-commander.git] / src / vfs / undelfs / undelfs.c
blob5b06009c132d48ba03d907036052f453705a8eca
1 /*
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.
13 Written by:
14 Miguel de Icaza, 1995
15 Norbert Warmuth, 1997
16 Pavel Machek, 2000
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/>.
34 /**
35 * \file
36 * \brief Source: UnDel File System
38 * Assumptions:
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
46 #include <config.h>
48 #include <errno.h>
49 #include <stdio.h>
50 #include <stdlib.h>
52 #ifdef HAVE_EXT2FS_EXT2_FS_H
53 #include <ext2fs/ext2_fs.h>
54 #else
55 /* asm/types.h defines its own umode_t */
56 #undef umode_t
57 #include <linux/ext2_fs.h>
58 #endif
60 #include <ext2fs/ext2fs.h>
61 #include <ctype.h>
63 #include "lib/global.h"
65 #include "lib/util.h"
66 #include "lib/widget.h" /* message() */
67 #include "lib/vfs/utilvfs.h"
68 #include "lib/vfs/vfs.h"
70 #include "undelfs.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 ****************************************************************/
83 struct deleted_info
85 ext2_ino_t ino;
86 unsigned short mode;
87 unsigned short uid;
88 unsigned short gid;
89 unsigned long size;
90 time_t dtime;
91 int num_blocks;
92 int free_blocks;
95 struct lsdel_struct
97 ext2_ino_t inode;
98 int num_blocks;
99 int free_blocks;
100 int bad_blocks;
103 typedef struct
105 int f_index; /* file index into delarray */
106 char *buf;
107 int error_code; /* */
108 off_t pos; /* file position */
109 off_t current; /* used to determine current position in itereate */
110 gboolean finished;
111 ext2_ino_t inode;
112 int bytes_read;
113 off_t size;
115 /* Used by undelfs_read: */
116 char *dest_buffer; /* destination buffer */
117 size_t count; /* bytes to read */
118 } undelfs_file;
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 /* --------------------------------------------------------------------------------------------- */
137 static void
138 undelfs_shutdown (void)
140 if (fs)
141 ext2fs_close (fs);
142 fs = NULL;
143 MC_PTR_FREE (ext2_fname);
144 MC_PTR_FREE (delarray);
145 MC_PTR_FREE (block_buf);
148 /* --------------------------------------------------------------------------------------------- */
150 static void
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.
161 -- pavel@ucw.cz */
163 dirname = path_element->path;
165 *fsname = NULL;
167 if (strncmp (dirname, "undel://", 8) != 0)
168 return;
170 dirname += 8;
172 /* Since we don't allow subdirectories, it's easy to get a filename,
173 * just scan backwards for a slash */
174 if (*dirname == 0)
175 return;
177 p = dirname + strlen (dirname);
178 #if 0
179 /* Strip trailing ./
181 if (p - dirname > 2 && IS_PATH_SEP (p[-1]) && p[-2] == '.')
182 *(p = p - 2) = 0;
183 #endif
185 while (p > dirname)
187 if (IS_PATH_SEP (*p))
189 char *tmp;
191 *file = g_strdup (p + 1);
192 tmp = g_strndup (dirname, p - dirname);
193 *fsname = g_strconcat ("/dev/", tmp, (char *) NULL);
194 g_free (tmp);
195 return;
197 p--;
199 *file = g_strdup ("");
200 *fsname = g_strconcat ("/dev/", dirname, (char *) NULL);
203 /* --------------------------------------------------------------------------------------------- */
205 static int
206 undelfs_lsdel_proc (ext2_filsys _fs, blk_t * block_nr, int blockcnt, void *private)
208 struct lsdel_struct *_lsd = (struct lsdel_struct *) private;
209 (void) blockcnt;
210 _lsd->num_blocks++;
212 if (*block_nr < _fs->super->s_first_data_block || *block_nr >= _fs->super->s_blocks_count)
214 _lsd->bad_blocks++;
215 return BLOCK_ABORT;
218 if (!ext2fs_test_block_bitmap (_fs->block_map, *block_nr))
219 _lsd->free_blocks++;
221 return 0;
224 /* --------------------------------------------------------------------------------------------- */
226 * Load information about deleted files.
227 * Don't abort if there is not enough memory - load as much as we can.
230 static int
231 undelfs_loaddel (void)
233 int retval, count;
234 ext2_ino_t ino;
235 struct ext2_inode inode;
236 ext2_inode_scan scan;
238 max_delarray = 100;
239 num_delarray = 0;
240 delarray = g_try_malloc (sizeof (struct deleted_info) * max_delarray);
241 if (!delarray)
243 message (D_ERROR, undelfserr, "%s", _("not enough memory"));
244 return 0;
246 block_buf = g_try_malloc (fs->blocksize * 3);
247 if (!block_buf)
249 message (D_ERROR, undelfserr, "%s", _("while allocating block buffer"));
250 goto free_delarray;
252 retval = ext2fs_open_inode_scan (fs, 0, &scan);
253 if (retval != 0)
255 message (D_ERROR, undelfserr, _("open_inode_scan: %d"), retval);
256 goto free_block_buf;
258 retval = ext2fs_get_next_inode (scan, &ino, &inode);
259 if (retval != 0)
261 message (D_ERROR, undelfserr, _("while starting inode scan %d"), retval);
262 goto error_out;
264 count = 0;
265 while (ino)
267 if ((count++ % 1024) == 0)
268 vfs_print_message (_("undelfs: loading deleted files information %d inodes"), count);
269 if (inode.i_dtime == 0)
270 goto next;
272 if (S_ISDIR (inode.i_mode))
273 goto next;
275 lsd.inode = ino;
276 lsd.num_blocks = 0;
277 lsd.free_blocks = 0;
278 lsd.bad_blocks = 0;
280 retval = ext2fs_block_iterate (fs, ino, 0, block_buf, undelfs_lsdel_proc, &lsd);
281 if (retval)
283 message (D_ERROR, undelfserr, _("while calling ext2_block_iterate %d"), retval);
284 goto next;
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));
293 if (!delarray_new)
295 message (D_ERROR, undelfserr, "%s",
296 _("no more memory while reallocating array"));
297 goto error_out;
299 delarray = delarray_new;
300 max_delarray += 50;
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;
311 num_delarray++;
314 next:
315 retval = ext2fs_get_next_inode (scan, &ino, &inode);
316 if (retval)
318 message (D_ERROR, undelfserr, _("while doing inode scan %d"), retval);
319 goto error_out;
322 readdir_ptr = READDIR_PTR_INIT;
323 ext2fs_close_inode_scan (scan);
324 return 1;
326 error_out:
327 ext2fs_close_inode_scan (scan);
328 free_block_buf:
329 MC_PTR_FREE (block_buf);
330 free_delarray:
331 MC_PTR_FREE (delarray);
332 return 0;
335 /* --------------------------------------------------------------------------------------------- */
337 static void *
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);
345 if (file == NULL)
347 g_free (f);
348 return 0;
351 /* We don't use the file name */
352 g_free (f);
354 if (!ext2_fname || strcmp (ext2_fname, file))
356 undelfs_shutdown ();
357 ext2_fname = file;
359 else
361 /* To avoid expensive re-scannings */
362 readdir_ptr = READDIR_PTR_INIT;
363 g_free (file);
364 return fs;
367 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs))
369 message (D_ERROR, undelfserr, _("Cannot open file %s"), ext2_fname);
370 return 0;
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);
376 goto quit_opendir;
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);
382 goto quit_opendir;
384 /* Now load the deleted information */
385 if (!undelfs_loaddel ())
386 goto quit_opendir;
387 vfs_print_message (_("%s: done."), path_element->class->name);
388 return fs;
389 quit_opendir:
390 vfs_print_message (_("%s: failure"), path_element->class->name);
391 ext2fs_close (fs);
392 fs = NULL;
393 return 0;
396 /* --------------------------------------------------------------------------------------------- */
398 static void *
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;
404 if (vfs_info != fs)
406 message (D_ERROR, undelfserr, "%s", _("vfs_info is not fs!"));
407 return NULL;
409 if (readdir_ptr == num_delarray)
410 return NULL;
411 if (readdir_ptr < 0)
412 strcpy (dirent_dest, readdir_ptr == -2 ? "." : "..");
413 else
414 g_snprintf (dirent_dest, MC_MAXPATHLEN, "%ld:%d",
415 (long) delarray[readdir_ptr].ino, delarray[readdir_ptr].num_blocks);
416 readdir_ptr++;
418 return &undelfs_readdir_data;
421 /* --------------------------------------------------------------------------------------------- */
423 static int
424 undelfs_closedir (void *vfs_info)
426 (void) vfs_info;
427 return 0;
430 /* --------------------------------------------------------------------------------------------- */
431 /* We do not support lseek */
433 static void *
434 undelfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
436 char *file, *f = NULL;
437 ext2_ino_t inode, i;
438 undelfs_file *p = NULL;
439 (void) flags;
440 (void) mode;
442 /* Only allow reads on this file system */
443 undelfs_get_path (vpath, &file, &f);
444 if (file == NULL)
446 g_free (f);
447 return 0;
450 if (!ext2_fname || strcmp (ext2_fname, file))
452 message (D_ERROR, undelfserr, "%s", _("You have to chdir to extract files first"));
453 g_free (file);
454 g_free (f);
455 return 0;
457 inode = atol (f);
459 /* Search the file into delarray */
460 for (i = 0; i < (ext2_ino_t) num_delarray; i++)
462 if (inode != delarray[i].ino)
463 continue;
465 /* Found: setup all the structures needed by read */
466 p = (undelfs_file *) g_try_malloc (((gsize) sizeof (undelfs_file)));
467 if (!p)
469 g_free (file);
470 g_free (f);
471 return 0;
473 p->buf = g_try_malloc (fs->blocksize);
474 if (!p->buf)
476 g_free (p);
477 g_free (file);
478 g_free (f);
479 return 0;
481 p->inode = inode;
482 p->finished = FALSE;
483 p->f_index = i;
484 p->error_code = 0;
485 p->pos = 0;
486 p->size = delarray[i].size;
488 g_free (file);
489 g_free (f);
490 undelfs_usage++;
491 return p;
494 /* --------------------------------------------------------------------------------------------- */
496 static int
497 undelfs_close (void *vfs_info)
499 undelfs_file *p = vfs_info;
500 g_free (p->buf);
501 g_free (p);
502 undelfs_usage--;
503 return 0;
506 /* --------------------------------------------------------------------------------------------- */
508 static int
509 undelfs_dump_read (ext2_filsys param_fs, blk_t * blocknr, int blockcnt, void *private)
511 int copy_count;
512 undelfs_file *p = (undelfs_file *) private;
514 if (blockcnt < 0)
515 return 0;
517 if (*blocknr)
519 p->error_code = io_channel_read_blk (param_fs->io, *blocknr, 1, p->buf);
520 if (p->error_code)
521 return BLOCK_ABORT;
523 else
524 memset (p->buf, 0, param_fs->blocksize);
526 if (p->pos + (off_t) p->count < p->current)
528 p->finished = TRUE;
529 return BLOCK_ABORT;
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);
548 else
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);
555 else
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);
563 else
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;
571 if (p->finished)
573 return BLOCK_ABORT;
575 return 0;
578 /* --------------------------------------------------------------------------------------------- */
580 static ssize_t
581 undelfs_read (void *vfs_info, char *buffer, size_t count)
583 undelfs_file *p = vfs_info;
584 int retval;
586 p->dest_buffer = buffer;
587 p->current = 0;
588 p->finished = FALSE;
589 p->count = count;
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);
596 if (retval)
598 message (D_ERROR, undelfserr, "%s", _("while iterating over blocks"));
599 return -1;
601 if (p->error_code && !p->finished)
602 return 0;
603 p->pos = p->pos + (p->dest_buffer - buffer);
604 return p->dest_buffer - buffer;
607 /* --------------------------------------------------------------------------------------------- */
609 static long
610 undelfs_getindex (char *path)
612 ext2_ino_t inode = atol (path);
613 int i;
615 for (i = 0; i < num_delarray; i++)
617 if (delarray[i].ino == inode)
618 return i;
620 return -1;
623 /* --------------------------------------------------------------------------------------------- */
625 static int
626 undelfs_stat_int (int inode_index, struct stat *buf)
628 buf->st_dev = 0;
629 buf->st_ino = delarray[inode_index].ino;
630 buf->st_mode = delarray[inode_index].mode;
631 buf->st_nlink = 1;
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;
640 #endif
641 return 0;
644 /* --------------------------------------------------------------------------------------------- */
646 static int
647 undelfs_lstat (const vfs_path_t * vpath, struct stat *buf)
649 int inode_index;
650 char *file, *f = NULL;
652 undelfs_get_path (vpath, &file, &f);
653 if (file == NULL)
655 g_free (f);
656 return 0;
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 */
665 if (!isdigit (*f))
667 g_free (file);
668 g_free (f);
669 return -1;
672 if (!ext2_fname || strcmp (ext2_fname, file))
674 g_free (file);
675 g_free (f);
676 message (D_ERROR, undelfserr, "%s", _("You have to chdir to extract files first"));
677 return 0;
679 inode_index = undelfs_getindex (f);
680 g_free (file);
681 g_free (f);
683 if (inode_index == -1)
684 return -1;
686 return undelfs_stat_int (inode_index, buf);
689 /* --------------------------------------------------------------------------------------------- */
691 static int
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 /* --------------------------------------------------------------------------------------------- */
701 static int
702 undelfs_chdir (const vfs_path_t * vpath)
704 char *file, *f = NULL;
705 int fd;
707 undelfs_get_path (vpath, &file, &f);
708 if (file == NULL)
710 g_free (f);
711 return (-1);
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);
718 if (fd == -1)
720 message (D_ERROR, undelfserr, _("Cannot open file \"%s\""), file);
721 g_free (f);
722 g_free (file);
723 return -1;
725 close (fd);
726 g_free (f);
727 g_free (file);
728 return 0;
731 /* --------------------------------------------------------------------------------------------- */
733 /* this has to stay here for now: vfs layer does not know how to emulate it */
734 static off_t
735 undelfs_lseek (void *vfs_info, off_t offset, int whence)
737 (void) vfs_info;
738 (void) offset;
739 (void) whence;
741 return -1;
744 /* --------------------------------------------------------------------------------------------- */
746 static vfsid
747 undelfs_getid (const vfs_path_t * vpath)
749 char *fname = NULL, *fsname;
750 gboolean ok;
752 undelfs_get_path (vpath, &fsname, &fname);
753 ok = fsname != NULL;
755 g_free (fname);
756 g_free (fsname);
758 return ok ? (vfsid) fs : NULL;
761 /* --------------------------------------------------------------------------------------------- */
763 static int
764 undelfs_nothingisopen (vfsid id)
766 (void) id;
768 return !undelfs_usage;
771 /* --------------------------------------------------------------------------------------------- */
773 static void
774 undelfs_free (vfsid id)
776 (void) id;
778 undelfs_shutdown ();
781 /* --------------------------------------------------------------------------------------------- */
783 #ifdef ENABLE_NLS
784 static int
785 undelfs_init (struct vfs_class *me)
787 (void) me;
789 undelfserr = _(undelfserr);
790 return 1;
792 #else
793 #define undelfs_init NULL
794 #endif
796 /* --------------------------------------------------------------------------------------------- */
797 /*** public functions ****************************************************************************/
798 /* --------------------------------------------------------------------------------------------- */
800 * This function overrides com_err() from libcom_err library.
801 * It is used in libext2fs to report errors.
804 void
805 com_err (const char *whoami, long err_code, const char *fmt, ...)
807 va_list ap;
808 char *str;
810 va_start (ap, fmt);
811 str = g_strdup_vprintf (fmt, ap);
812 va_end (ap);
814 message (D_ERROR, _("Ext2lib error"), "%s (%s: %ld)", str, whoami, err_code);
815 g_free (str);
818 /* --------------------------------------------------------------------------------------------- */
820 void
821 init_undelfs (void)
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 /* --------------------------------------------------------------------------------------------- */