Merge branch '1858_segfault_in_search'
[midnight-commander.git] / vfs / undelfs.c
blob3f45570045f7a740d090caafe6e939292a2e3d1e
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.
12 1997 Norbert Warmuth.
13 2000 Pavel Machek
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. */
29 /**
30 * \file
31 * \brief Source: UnDel File System
33 * Assumptions:
35 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
36 * 2. Files are on the local file system (we do not support vfs files
37 * because we would have to provide an io_manager for the ext2fs tools,
38 * and I don't think it would be too useful to undelete files
41 #include <config.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <fcntl.h>
48 #ifdef HAVE_EXT2FS_EXT2_FS_H
49 #include <ext2fs/ext2_fs.h>
50 #else
51 /* asm/types.h defines its own umode_t */
52 #undef umode_t
53 #include <linux/ext2_fs.h>
54 #endif
56 #include <ext2fs/ext2fs.h>
57 #include <ctype.h>
59 #include "../src/global.h"
60 #include "../src/wtools.h" /* message() */
61 #include "../src/main.h" /* print_vfs_message */
62 #include "utilvfs.h"
63 #include "vfs.h"
64 #include "vfs-impl.h"
66 struct deleted_info {
67 ext2_ino_t ino;
68 unsigned short mode;
69 unsigned short uid;
70 unsigned short gid;
71 unsigned long size;
72 time_t dtime;
73 int num_blocks;
74 int free_blocks;
77 struct lsdel_struct {
78 ext2_ino_t inode;
79 int num_blocks;
80 int free_blocks;
81 int bad_blocks;
84 /* We only allow one opened ext2fs */
85 static char *ext2_fname;
86 static ext2_filsys fs = NULL;
87 static struct lsdel_struct lsd;
88 static struct deleted_info *delarray;
89 static int num_delarray, max_delarray;
90 static char *block_buf;
91 static const char *undelfserr = N_(" undelfs: error ");
92 static int readdir_ptr;
93 static int undelfs_usage;
94 static struct vfs_class vfs_undelfs_ops;
96 /* To generate the . and .. entries use -2 */
97 #define READDIR_PTR_INIT 0
99 static void
100 undelfs_shutdown (void)
102 if (fs)
103 ext2fs_close (fs);
104 fs = NULL;
105 g_free (ext2_fname);
106 ext2_fname = NULL;
107 g_free (delarray);
108 delarray = NULL;
109 g_free (block_buf);
110 block_buf = NULL;
113 static void
114 undelfs_get_path (const char *dirname, char **fsname, char **file)
116 const char *p;
118 /* To look like filesystem, we have virtual directories
119 /#undel:XXX, which have no subdirectories. XXX is replaced with
120 hda5, sdb8 etc, which is assumed to live under /dev.
121 -- pavel@ucw.cz */
123 *fsname = NULL;
125 if (strncmp (dirname, "/#undel:", 8))
126 return;
128 dirname += 8;
130 /* Since we don't allow subdirectories, it's easy to get a filename,
131 * just scan backwards for a slash */
132 if (*dirname == 0)
133 return;
135 p = dirname + strlen (dirname);
136 #if 0
137 /* Strip trailing ./
139 if (p - dirname > 2 && *(p-1) == '/' && *(p-2) == '.')
140 *(p = p-2) = 0;
141 #endif
143 while (p > dirname){
144 if (*p == '/'){
145 char *tmp;
147 *file = g_strdup (p+1);
148 tmp = g_strndup (dirname, p - dirname);
149 *fsname = g_strconcat ("/dev/", tmp, (char *) NULL);
150 g_free (tmp);
151 return;
153 p--;
155 *file = g_strdup ("");
156 *fsname = g_strconcat ("/dev/", dirname, (char *) NULL);
157 return;
160 static int
161 undelfs_lsdel_proc(ext2_filsys _fs, blk_t *block_nr, int blockcnt, void *private)
163 struct lsdel_struct *_lsd = (struct lsdel_struct *) private;
164 (void) blockcnt;
165 _lsd->num_blocks++;
167 if (*block_nr < _fs->super->s_first_data_block ||
168 *block_nr >= _fs->super->s_blocks_count) {
169 _lsd->bad_blocks++;
170 return BLOCK_ABORT;
173 if (!ext2fs_test_block_bitmap(_fs->block_map,*block_nr))
174 _lsd->free_blocks++;
176 return 0;
180 * Load information about deleted files.
181 * Don't abort if there is not enough memory - load as much as we can.
183 static int
184 undelfs_loaddel (void)
186 int retval, count;
187 ext2_ino_t ino;
188 struct ext2_inode inode;
189 ext2_inode_scan scan;
191 max_delarray = 100;
192 num_delarray = 0;
193 delarray = g_try_malloc (sizeof (struct deleted_info) * max_delarray);
194 if (!delarray) {
195 message (D_ERROR, undelfserr, _(" not enough memory "));
196 return 0;
198 block_buf = g_try_malloc (fs->blocksize * 3);
199 if (!block_buf) {
200 message (D_ERROR, undelfserr, _(" while allocating block buffer "));
201 goto free_delarray;
203 if ((retval = ext2fs_open_inode_scan (fs, 0, &scan))) {
204 message (D_ERROR, undelfserr, _(" open_inode_scan: %d "), retval);
205 goto free_block_buf;
207 if ((retval = ext2fs_get_next_inode (scan, &ino, &inode))) {
208 message (D_ERROR, undelfserr, _(" while starting inode scan %d "),
209 retval);
210 goto error_out;
213 count = 0;
214 while (ino) {
215 if ((count++ % 1024) == 0)
216 print_vfs_message (_
217 ("undelfs: loading deleted files information %d inodes"),
218 count);
219 if (inode.i_dtime == 0)
220 goto next;
222 if (S_ISDIR (inode.i_mode))
223 goto next;
225 lsd.inode = ino;
226 lsd.num_blocks = 0;
227 lsd.free_blocks = 0;
228 lsd.bad_blocks = 0;
230 retval =
231 ext2fs_block_iterate (fs, ino, 0, block_buf,
232 undelfs_lsdel_proc, &lsd);
233 if (retval) {
234 message (D_ERROR, undelfserr,
235 _(" while calling ext2_block_iterate %d "), retval);
236 goto next;
238 if (lsd.free_blocks && !lsd.bad_blocks) {
239 if (num_delarray >= max_delarray) {
240 struct deleted_info *delarray_new =
241 g_try_realloc (delarray,
242 sizeof (struct deleted_info) *
243 (max_delarray + 50));
244 if (!delarray_new) {
245 message (D_ERROR, undelfserr,
247 (" no more memory while reallocating array "));
248 goto error_out;
250 delarray = delarray_new;
251 max_delarray += 50;
254 delarray[num_delarray].ino = ino;
255 delarray[num_delarray].mode = inode.i_mode;
256 delarray[num_delarray].uid = inode.i_uid;
257 delarray[num_delarray].gid = inode.i_gid;
258 delarray[num_delarray].size = inode.i_size;
259 delarray[num_delarray].dtime = inode.i_dtime;
260 delarray[num_delarray].num_blocks = lsd.num_blocks;
261 delarray[num_delarray].free_blocks = lsd.free_blocks;
262 num_delarray++;
265 next:
266 retval = ext2fs_get_next_inode (scan, &ino, &inode);
267 if (retval) {
268 message (D_ERROR, undelfserr, _(" while doing inode scan %d "),
269 retval);
270 goto error_out;
273 readdir_ptr = READDIR_PTR_INIT;
274 ext2fs_close_inode_scan (scan);
275 return 1;
277 error_out:
278 ext2fs_close_inode_scan (scan);
279 free_block_buf:
280 g_free (block_buf);
281 block_buf = NULL;
282 free_delarray:
283 g_free (delarray);
284 delarray = NULL;
285 return 0;
290 * This function overrides com_err() from libcom_err library.
291 * It is used in libext2fs to report errors.
293 void
294 com_err (const char *whoami, long err_code, const char *fmt, ...)
296 va_list ap;
297 char *str;
299 va_start (ap, fmt);
300 str = g_strdup_vprintf (fmt, ap);
301 va_end (ap);
303 message (D_ERROR, _(" Ext2lib error "), " %s (%s: %ld) ", str, whoami,
304 err_code);
305 g_free (str);
308 static void *
309 undelfs_opendir (struct vfs_class *me, const char *dirname)
311 char *file, *f;
313 undelfs_get_path (dirname, &file, &f);
314 if (!file)
315 return 0;
317 /* We don't use the file name */
318 g_free (f);
320 if (!ext2_fname || strcmp (ext2_fname, file)){
321 undelfs_shutdown ();
322 ext2_fname = file;
323 } else {
324 /* To avoid expensive re-scannings */
325 readdir_ptr = READDIR_PTR_INIT;
326 g_free (file);
327 return fs;
330 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)){
331 message (D_ERROR, undelfserr, _(" Cannot open file %s "), ext2_fname);
332 return 0;
334 print_vfs_message (_("undelfs: reading inode bitmap..."));
335 if (ext2fs_read_inode_bitmap (fs)){
336 message (D_ERROR, undelfserr,
337 _(" Cannot load inode bitmap from: \n %s \n"), ext2_fname);
338 goto quit_opendir;
340 print_vfs_message (_("undelfs: reading block bitmap..."));
341 if (ext2fs_read_block_bitmap (fs)){
342 message (D_ERROR, undelfserr,
343 _(" Cannot load block bitmap from: \n %s \n"), ext2_fname);
344 goto quit_opendir;
346 /* Now load the deleted information */
347 if (!undelfs_loaddel ())
348 goto quit_opendir;
349 print_vfs_message (_("%s: done."), me->name);
350 return fs;
351 quit_opendir:
352 print_vfs_message (_("%s: failure"), me->name);
353 ext2fs_close (fs);
354 fs = NULL;
355 return 0;
359 static void *
360 undelfs_readdir(void *vfs_info)
362 static union vfs_dirent undelfs_readdir_data;
363 static char *const dirent_dest = undelfs_readdir_data.dent.d_name;
365 if (vfs_info != fs) {
366 message (D_ERROR, undelfserr, _(" vfs_info is not fs! "));
367 return NULL;
369 if (readdir_ptr == num_delarray)
370 return NULL;
371 if (readdir_ptr < 0)
372 strcpy (dirent_dest, readdir_ptr == -2 ? "." : "..");
373 else
374 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
375 (long) delarray[readdir_ptr].ino,
376 delarray[readdir_ptr].num_blocks);
377 readdir_ptr++;
379 compute_namelen(&undelfs_readdir_data.dent);
381 return &undelfs_readdir_data;
384 static int
385 undelfs_closedir (void *vfs_info)
387 return 0;
390 typedef struct {
391 int f_index; /* file index into delarray */
392 char *buf;
393 int error_code; /* */
394 int pos; /* file position */
395 int current; /* used to determine current position in itereate */
396 int finished;
397 ext2_ino_t inode;
398 int bytes_read;
399 long size;
401 /* Used by undelfs_read: */
402 char *dest_buffer; /* destination buffer */
403 int count; /* bytes to read */
404 } undelfs_file;
406 /* We do not support lseek */
407 static void *
408 undelfs_open (struct vfs_class *me, const char *fname, int flags, int mode)
410 char *file, *f;
411 ext2_ino_t inode, i;
412 undelfs_file *p = NULL;
414 /* Only allow reads on this file system */
415 undelfs_get_path (fname, &file, &f);
416 if (!file)
417 return 0;
419 if (!ext2_fname || strcmp (ext2_fname, file)) {
420 message (D_ERROR, undelfserr,
421 _(" You have to chdir to extract files first "));
422 g_free (file);
423 g_free (f);
424 return 0;
426 inode = atol (f);
428 /* Search the file into delarray */
429 for (i = 0; i < num_delarray; i++) {
430 if (inode != delarray[i].ino)
431 continue;
433 /* Found: setup all the structures needed by read */
434 p = (undelfs_file *) g_try_malloc (((gsize) sizeof (undelfs_file)));
435 if (!p) {
436 g_free (file);
437 g_free (f);
438 return 0;
440 p->buf = g_try_malloc (fs->blocksize);
441 if (!p->buf) {
442 g_free (p);
443 g_free (file);
444 g_free (f);
445 return 0;
447 p->inode = inode;
448 p->finished = 0;
449 p->f_index = i;
450 p->error_code = 0;
451 p->pos = 0;
452 p->size = delarray[i].size;
454 g_free (file);
455 g_free (f);
456 undelfs_usage++;
457 return p;
460 static int
461 undelfs_close (void *vfs_info)
463 undelfs_file *p = vfs_info;
464 g_free (p->buf);
465 g_free (p);
466 undelfs_usage--;
467 return 0;
470 static int
471 undelfs_dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
473 int copy_count;
474 undelfs_file *p = (undelfs_file *) private;
476 if (blockcnt < 0)
477 return 0;
479 if (*blocknr) {
480 p->error_code = io_channel_read_blk(fs->io, *blocknr,
481 1, p->buf);
482 if (p->error_code)
483 return BLOCK_ABORT;
484 } else
485 memset(p->buf, 0, fs->blocksize);
487 if (p->pos + p->count < p->current){
488 p->finished = 1;
489 return BLOCK_ABORT;
491 if (p->pos > p->current + fs->blocksize){
492 p->current += fs->blocksize;
493 return 0; /* we have not arrived yet */
496 /* Now, we know we have to extract some data */
497 if (p->pos >= p->current){
499 /* First case: starting pointer inside this block */
500 if (p->pos + p->count <= p->current + fs->blocksize){
501 /* Fully contained */
502 copy_count = p->count;
503 p->finished = p->count;
504 } else {
505 /* Still some more data */
506 copy_count = fs->blocksize-(p->pos-p->current);
508 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
509 } else {
510 /* Second case: we already have passed p->pos */
511 if (p->pos+p->count < p->current+fs->blocksize){
512 copy_count = (p->pos + p->count) - p->current;
513 p->finished = p->count;
514 } else {
515 copy_count = fs->blocksize;
517 memcpy (p->dest_buffer, p->buf, copy_count);
519 p->dest_buffer += copy_count;
520 p->current += fs->blocksize;
521 if (p->finished){
522 return BLOCK_ABORT;
524 return 0;
527 static ssize_t
528 undelfs_read (void *vfs_info, char *buffer, int count)
530 undelfs_file *p = vfs_info;
531 int retval;
533 p->dest_buffer = buffer;
534 p->current = 0;
535 p->finished = 0;
536 p->count = count;
538 if (p->pos + p->count > p->size){
539 p->count = p->size - p->pos;
541 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
542 undelfs_dump_read, p);
543 if (retval){
544 message (D_ERROR, undelfserr, _(" while iterating over blocks "));
545 return -1;
547 if (p->error_code && !p->finished)
548 return 0;
549 p->pos = p->pos + (p->dest_buffer - buffer);
550 return p->dest_buffer - buffer;
553 static long
554 undelfs_getindex (char *path)
556 ext2_ino_t inode = atol (path);
557 int i;
559 for (i = 0; i < num_delarray; i++){
560 if (delarray [i].ino == inode)
561 return i;
563 return -1;
566 static int
567 undelfs_stat_int (int inode_index, struct stat *buf)
569 buf->st_dev = 0;
570 buf->st_ino = delarray [inode_index].ino;
571 buf->st_mode = delarray [inode_index].mode;
572 buf->st_nlink = 1;
573 buf->st_uid = delarray [inode_index].uid;
574 buf->st_gid = delarray [inode_index].gid;
575 buf->st_size = delarray [inode_index].size;
576 buf->st_atime = delarray [inode_index].dtime;
577 buf->st_ctime = delarray [inode_index].dtime;
578 buf->st_mtime = delarray [inode_index].dtime;
579 return 0;
582 static int
583 undelfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
585 int inode_index;
586 char *file, *f;
588 undelfs_get_path (path, &file, &f);
589 if (!file)
590 return 0;
592 /* When called from save_cwd_stats we get an incorrect file and f here:
593 e.g. incorrect correct
594 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
595 file = "/dev" file="/dev/sda1"
596 f = "sda1" f ="401:1"
597 If the first char in f is no digit -> return error */
598 if (!isdigit (*f)) {
599 g_free (file);
600 g_free (f);
601 return -1;
604 if (!ext2_fname || strcmp (ext2_fname, file)){
605 message (D_ERROR, undelfserr, _(" You have to chdir to extract files first "));
606 g_free (file);
607 g_free (f);
608 return 0;
610 inode_index = undelfs_getindex (f);
611 g_free (file);
612 g_free (f);
614 if (inode_index == -1)
615 return -1;
617 return undelfs_stat_int (inode_index, buf);
620 #define undelfs_stat undelfs_lstat
622 static int
623 undelfs_fstat (void *vfs_info, struct stat *buf)
625 undelfs_file *p = vfs_info;
627 return undelfs_stat_int (p->f_index, buf);
630 static int
631 undelfs_chdir(struct vfs_class *me, const char *path)
633 char *file, *f;
634 int fd;
636 undelfs_get_path (path, &file, &f);
637 if (!file)
638 return -1;
640 /* We may use access because ext2 file systems are local */
641 /* this could be fixed by making an ext2fs io manager to use */
642 /* our vfs, but that is left as an excercise for the reader */
643 if ((fd = open (file, O_RDONLY)) == -1){
644 message (D_ERROR, undelfserr, _(" Cannot open file %s "), file);
645 g_free (f);
646 g_free (file);
647 return -1;
649 close (fd);
650 g_free (f);
651 g_free (file);
652 return 0;
655 /* this has to stay here for now: vfs layer does not know how to emulate it */
656 static off_t
657 undelfs_lseek(void *vfs_info, off_t offset, int whence)
659 return -1;
662 static vfsid
663 undelfs_getid (struct vfs_class *me, const char *path)
665 char *fname, *fsname;
667 undelfs_get_path (path, &fsname, &fname);
669 if (!fsname)
670 return NULL;
671 g_free (fname);
672 g_free (fsname);
673 return (vfsid) fs;
676 static int
677 undelfs_nothingisopen(vfsid id)
679 return !undelfs_usage;
682 static void
683 undelfs_free(vfsid id)
685 undelfs_shutdown ();
688 #ifdef ENABLE_NLS
689 static int
690 undelfs_init(struct vfs_class *me) {
691 undelfserr = _(undelfserr);
692 return 1;
694 #else
695 #define undelfs_init NULL
696 #endif
698 void
699 init_undelfs (void)
701 vfs_undelfs_ops.name = "undelfs";
702 vfs_undelfs_ops.prefix = "undel:";
703 vfs_undelfs_ops.init = undelfs_init;
704 vfs_undelfs_ops.open = undelfs_open;
705 vfs_undelfs_ops.close = undelfs_close;
706 vfs_undelfs_ops.read = undelfs_read;
707 vfs_undelfs_ops.opendir = undelfs_opendir;
708 vfs_undelfs_ops.readdir = undelfs_readdir;
709 vfs_undelfs_ops.closedir = undelfs_closedir;
710 vfs_undelfs_ops.stat = undelfs_stat;
711 vfs_undelfs_ops.lstat = undelfs_lstat;
712 vfs_undelfs_ops.fstat = undelfs_fstat;
713 vfs_undelfs_ops.chdir = undelfs_chdir;
714 vfs_undelfs_ops.lseek = undelfs_lseek;
715 vfs_undelfs_ops.getid = undelfs_getid;
716 vfs_undelfs_ops.nothingisopen = undelfs_nothingisopen;
717 vfs_undelfs_ops.free = undelfs_free;
718 vfs_register_class (&vfs_undelfs_ops);