* Makefile.am (AM_CPPFLAGS): Remove REGEX_MALLOC, it's now in
[midnight-commander.git] / vfs / undelfs.c
blob8fcc79df32f2b2ad32abf6e112165160d51c88a3
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 the Free Software Foundation
10 Written by: 1995 Miguel de Icaza.
11 1997 Norbert Warmuth.
12 2000 Pavel Machek
14 $Id$
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU Library General Public License
18 as published by the Free Software Foundation; either version 2 of
19 the License, or (at your option) any later version.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU Library General Public License for more details.
26 You should have received a copy of the GNU Library General Public
27 License along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
30 /* Assumptions:
32 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
33 * 2. Files are on the local file system (we do not support vfs files
34 * because we would have to provide an io_manager for the ext2fs tools,
35 * and I don't think it would be too useful to undelete files
38 #include <config.h>
39 #include <errno.h>
41 #include <stdio.h>
42 #include <fcntl.h>
43 #include <stdlib.h>
44 /* asm/types.h defines its own umode_t :-( */
45 #undef umode_t
46 #include <linux/ext2_fs.h>
47 #include <ext2fs/ext2fs.h>
48 #include <ctype.h>
50 #include "utilvfs.h"
52 #include "vfs.h"
54 struct deleted_info {
55 ext2_ino_t ino;
56 unsigned short mode;
57 unsigned short uid;
58 unsigned short gid;
59 unsigned long size;
60 time_t dtime;
61 int num_blocks;
62 int free_blocks;
65 struct lsdel_struct {
66 ext2_ino_t inode;
67 int num_blocks;
68 int free_blocks;
69 int bad_blocks;
72 /* We only allow one opened ext2fs */
73 static char *ext2_fname;
74 static ext2_filsys fs = NULL;
75 static struct lsdel_struct lsd;
76 static struct deleted_info *delarray;
77 static int num_delarray, max_delarray;
78 static char *block_buf;
79 static char *undelfserr = N_(" undelfs: error ");
80 static int readdir_ptr;
81 static int undelfs_usage;
83 /* To generate the . and .. entries use -2 */
84 #define READDIR_PTR_INIT 0
86 static void
87 undelfs_shutdown (void)
89 if (fs)
90 ext2fs_close (fs);
91 fs = NULL;
92 if (ext2_fname)
93 g_free (ext2_fname);
94 ext2_fname = NULL;
95 if (delarray)
96 g_free (delarray);
97 delarray = NULL;
98 if (block_buf)
99 g_free (block_buf);
100 block_buf = NULL;
103 static void
104 undelfs_get_path (char *dirname, char **ext2_fname, char **file)
106 char *p;
108 /* To look like filesystem, we have virtual directories
109 /#undel:XXX, which have no subdirectories. XXX is replaced with
110 hda5, sdb8 etc, which is assumed to live under /dev.
111 -- pavel@ucw.cz */
113 *ext2_fname = NULL;
115 if (strncmp (dirname, "/#undel:", 8))
116 return;
117 else
118 dirname += 8;
120 /* Since we don't allow subdirectories, it's easy to get a filename,
121 * just scan backwards for a slash */
122 if (*dirname == 0)
123 return;
125 p = dirname + strlen (dirname);
126 #if 0
127 /* Strip trailing ./
129 if (p - dirname > 2 && *(p-1) == '/' && *(p-2) == '.')
130 *(p = p-2) = 0;
131 #endif
133 while (p > dirname){
134 if (*p == '/'){
135 *file = g_strdup (p+1);
136 *p = 0;
137 *ext2_fname = g_strconcat ("/dev/", dirname, NULL);
138 *p = '/';
139 return;
141 p--;
143 *file = g_strdup ("");
144 *ext2_fname = g_strconcat ("/dev/", dirname, NULL);
145 return;
148 static int
149 lsdel_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *private)
151 struct lsdel_struct *lsd = (struct lsdel_struct *) private;
153 lsd->num_blocks++;
155 if (*block_nr < fs->super->s_first_data_block ||
156 *block_nr >= fs->super->s_blocks_count) {
157 lsd->bad_blocks++;
158 return BLOCK_ABORT;
161 if (!ext2fs_test_block_bitmap(fs->block_map,*block_nr))
162 lsd->free_blocks++;
164 return 0;
167 /* We don't use xmalloc, since we want to recover and not abort the program
168 * if we don't have enough memory
170 static int
171 undelfs_loaddel (void)
173 int retval, count;
174 ext2_ino_t ino;
175 struct ext2_inode inode;
176 ext2_inode_scan scan;
178 max_delarray = 100;
179 num_delarray = 0;
180 delarray = g_new (struct deleted_info, max_delarray);
181 if (!delarray) {
182 message_1s (1, undelfserr, _(" not enough memory "));
183 return 0;
185 block_buf = g_malloc (fs->blocksize * 3);
186 if (!block_buf) {
187 message_1s (1, undelfserr, _(" while allocating block buffer "));
188 goto free_delarray;
190 if ((retval = ext2fs_open_inode_scan(fs, 0, &scan))){
191 message_1s1d (1, undelfserr, _(" open_inode_scan: %d "), retval);
192 goto free_block_buf;
194 if ((retval = ext2fs_get_next_inode(scan, &ino, &inode))){
195 message_1s1d (1, undelfserr, _(" while starting inode scan %d "), retval);
196 goto error_out;
199 count = 0;
200 while (ino) {
201 if ((count++ % 1024) == 0)
202 print_vfs_message (_("undelfs: loading deleted files information %d inodes"), count);
203 if (inode.i_dtime == 0)
204 goto next;
206 if (S_ISDIR(inode.i_mode))
207 goto next;
209 lsd.inode = ino;
210 lsd.num_blocks = 0;
211 lsd.free_blocks = 0;
212 lsd.bad_blocks = 0;
214 retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
215 lsdel_proc, &lsd);
216 if (retval) {
217 message_1s1d (1, undelfserr, _(" while calling ext2_block_iterate %d "), retval);
218 goto next;
220 if (lsd.free_blocks && !lsd.bad_blocks) {
221 if (num_delarray >= max_delarray) {
222 max_delarray += 50;
223 delarray = g_renew (struct deleted_info, delarray, max_delarray);
224 if (!delarray) {
225 message_1s (1, undelfserr, _(" no more memory while reallocating array "));
226 goto error_out;
230 delarray[num_delarray].ino = ino;
231 delarray[num_delarray].mode = inode.i_mode;
232 delarray[num_delarray].uid = inode.i_uid;
233 delarray[num_delarray].gid = inode.i_gid;
234 delarray[num_delarray].size = inode.i_size;
235 delarray[num_delarray].dtime = inode.i_dtime;
236 delarray[num_delarray].num_blocks = lsd.num_blocks;
237 delarray[num_delarray].free_blocks = lsd.free_blocks;
238 num_delarray++;
241 next:
242 retval = ext2fs_get_next_inode(scan, &ino, &inode);
243 if (retval) {
244 message_1s1d(1, undelfserr, _(" while doing inode scan %d "), retval);
245 goto error_out;
248 readdir_ptr = READDIR_PTR_INIT;
249 ext2fs_close_inode_scan (scan);
250 return 1;
252 error_out:
253 ext2fs_close_inode_scan (scan);
254 free_block_buf:
255 g_free (block_buf);
256 block_buf = NULL;
257 free_delarray:
258 g_free (delarray);
259 delarray = NULL;
260 return 0;
263 void com_err (const char *str, long err_code, const char *s2, ...)
265 char *cptr;
267 cptr = g_strdup_printf (" %s (%s: %ld) ", s2, str, err_code);
268 message_1s (1, _(" Ext2lib error "), cptr);
269 g_free (cptr);
272 static void *
273 undelfs_opendir (vfs *me, char *dirname)
275 char *file, *f;
277 undelfs_get_path (dirname, &file, &f);
278 if (!file)
279 return 0;
281 /* We don't use the file name */
282 g_free (f);
284 if (!ext2_fname || strcmp (ext2_fname, file)){
285 undelfs_shutdown ();
286 ext2_fname = file;
287 } else {
288 /* To avoid expensive re-scannings */
289 readdir_ptr = READDIR_PTR_INIT;
290 g_free (file);
291 return fs;
294 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)){
295 message_2s (1, undelfserr, _(" Could not open file %s "), ext2_fname);
296 return 0;
298 print_vfs_message (_("undelfs: reading inode bitmap..."));
299 if (ext2fs_read_inode_bitmap (fs)){
300 message_2s (1, undelfserr,
301 _(" Could not load inode bitmap from: \n %s \n"), ext2_fname);
302 goto quit_opendir;
304 print_vfs_message (_("undelfs: reading block bitmap..."));
305 if (ext2fs_read_block_bitmap (fs)){
306 message_2s (1, undelfserr,
307 _(" Could not load block bitmap from: \n %s \n"), ext2_fname);
308 goto quit_opendir;
310 /* Now load the deleted information */
311 if (!undelfs_loaddel ())
312 goto quit_opendir;
313 print_vfs_message (_("undelfs: done."));
314 return fs;
315 quit_opendir:
316 print_vfs_message (_("undelfs: failure"));
317 ext2fs_close (fs);
318 fs = NULL;
319 return 0;
322 /* Explanation:
323 * On some operating systems (Slowaris 2 for example)
324 * the d_name member is just a char long (Nice trick that break everything,
325 * so we need to set up some space for the filename.
327 static struct {
328 struct dirent dent;
329 #ifdef NEED_EXTRA_DIRENT_BUFFER
330 char extra_buffer [MC_MAXPATHLEN];
331 #endif
332 } undelfs_readdir_data;
334 static void *
335 undelfs_readdir (void *vfs_info)
337 char *dirent_dest;
339 if (vfs_info != fs){
340 message_1s (1, undelfserr, _(" vfs_info is not fs! "));
341 return NULL;
343 if (readdir_ptr == num_delarray)
344 return NULL;
345 dirent_dest = (char *) &(undelfs_readdir_data.dent.d_name [0]);
346 if (readdir_ptr < 0)
347 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%s", readdir_ptr == -2 ? "." : "..");
348 else
349 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
350 (long)delarray [readdir_ptr].ino,
351 delarray [readdir_ptr].num_blocks);
352 readdir_ptr++;
354 #if 0
355 undelfs_readdir_data.dent.d_namlen = strlen (undelfs_readdir_data.dent.d_name);
356 #endif
357 return &undelfs_readdir_data;
360 static int
361 undelfs_closedir (void *vfs_info)
363 return 0;
366 typedef struct {
367 int f_index; /* file index into delarray */
368 char *buf;
369 int error_code; /* */
370 int pos; /* file position */
371 int current; /* used to determine current position in itereate */
372 int finished;
373 ext2_ino_t inode;
374 int bytes_read;
375 long size;
377 /* Used by undelfs_read: */
378 char *dest_buffer; /* destination buffer */
379 int count; /* bytes to read */
380 } undelfs_file;
382 /* We do not support lseek */
383 static void *
384 undelfs_open (vfs *me, char *fname, int flags, int mode)
386 char *file, *f;
387 ext2_ino_t inode, i;
388 undelfs_file *p = NULL;
390 /* Only allow reads on this file system */
391 undelfs_get_path (fname, &file, &f);
392 if (!file)
393 return 0;
395 if (!ext2_fname || strcmp (ext2_fname, file)){
396 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
397 g_free (file);
398 g_free (f);
399 return 0;
401 inode = atol (f);
403 /* Search the file into delarray */
404 for (i = 0; i < num_delarray; i++){
405 if (inode != delarray [i].ino)
406 continue;
408 /* Found: setup all the structures needed by read */
409 p = g_new (undelfs_file, 1);
410 if (!p){
411 g_free (file);
412 g_free (f);
413 return 0;
415 p->buf = g_malloc (fs->blocksize);
416 if (!p->buf){
417 g_free (p);
418 g_free (file);
419 g_free (f);
420 return 0;
422 p->inode = inode;
423 p->finished = 0;
424 p->f_index = i;
425 p->error_code = 0;
426 p->pos = 0;
427 p->size = delarray [i].size;
429 g_free (file);
430 g_free (f);
431 undelfs_usage++;
432 return p;
435 static int
436 undelfs_close (void *vfs_info)
438 undelfs_file *p = vfs_info;
439 g_free (p->buf);
440 g_free (p);
441 undelfs_usage--;
442 return 0;
445 static int
446 dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
448 int copy_count;
449 undelfs_file *p = (undelfs_file *) private;
451 if (blockcnt < 0)
452 return 0;
454 if (*blocknr) {
455 p->error_code = io_channel_read_blk(fs->io, *blocknr,
456 1, p->buf);
457 if (p->error_code)
458 return BLOCK_ABORT;
459 } else
460 memset(p->buf, 0, fs->blocksize);
462 if (p->pos + p->count < p->current){
463 p->finished = 1;
464 return BLOCK_ABORT;
466 if (p->pos > p->current + fs->blocksize){
467 p->current += fs->blocksize;
468 return 0; /* we have not arrived yet */
471 /* Now, we know we have to extract some data */
472 if (p->pos >= p->current){
474 /* First case: starting pointer inside this block */
475 if (p->pos + p->count <= p->current + fs->blocksize){
476 /* Fully contained */
477 copy_count = p->count;
478 p->finished = p->count;
479 } else {
480 /* Still some more data */
481 copy_count = fs->blocksize-(p->pos-p->current);
483 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
484 } else {
485 /* Second case: we already have passed p->pos */
486 if (p->pos+p->count < p->current+fs->blocksize){
487 copy_count = (p->pos + p->count) - p->current;
488 p->finished = p->count;
489 } else {
490 copy_count = fs->blocksize;
492 memcpy (p->dest_buffer, p->buf, copy_count);
494 p->dest_buffer += copy_count;
495 p->current += fs->blocksize;
496 if (p->finished){
497 return BLOCK_ABORT;
499 return 0;
502 static int
503 undelfs_read (void *vfs_info, char *buffer, int count)
505 undelfs_file *p = vfs_info;
506 int retval;
508 p->dest_buffer = buffer;
509 p->current = 0;
510 p->finished = 0;
511 p->count = count;
513 if (p->pos + p->count > p->size){
514 p->count = p->size - p->pos;
516 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
517 dump_read, p);
518 if (retval){
519 message_1s (1, undelfserr, _(" while iterating over blocks "));
520 return -1;
522 if (p->error_code && !p->finished)
523 return 0;
524 p->pos = p->pos + (p->dest_buffer - buffer);
525 return p->dest_buffer - buffer;
528 static long
529 undelfs_getindex (char *path)
531 ext2_ino_t inode = atol (path);
532 int i;
534 for (i = 0; i < num_delarray; i++){
535 if (delarray [i].ino == inode)
536 return i;
538 return -1;
541 static int
542 do_stat (int inode_index, struct stat *buf)
544 buf->st_dev = 0;
545 buf->st_ino = delarray [inode_index].ino;
546 buf->st_mode = delarray [inode_index].mode;
547 buf->st_nlink = 1;
548 buf->st_uid = delarray [inode_index].uid;
549 buf->st_gid = delarray [inode_index].gid;
550 buf->st_size = delarray [inode_index].size;
551 buf->st_atime = delarray [inode_index].dtime;
552 buf->st_ctime = delarray [inode_index].dtime;
553 buf->st_mtime = delarray [inode_index].dtime;
554 return 0;
557 static int
558 undelfs_lstat(vfs *me, char *path, struct stat *buf)
560 int inode_index;
561 char *file, *f;
563 undelfs_get_path (path, &file, &f);
564 if (!file)
565 return 0;
567 /* When called from save_cwd_stats we get an incorrect file and f here:
568 e.g. incorrect correct
569 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
570 file = "/dev" file="/dev/sda1"
571 f = "sda1" f ="401:1"
572 If the first char in f is no digit -> return error */
573 if (!isdigit (*f)) {
574 g_free (file);
575 g_free (f);
576 return -1;
579 if (!ext2_fname || strcmp (ext2_fname, file)){
580 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
581 g_free (file);
582 g_free (f);
583 return 0;
585 inode_index = undelfs_getindex (f);
586 g_free (file);
587 g_free (f);
589 if (inode_index == -1)
590 return -1;
592 return do_stat (inode_index, buf);
595 static int
596 undelfs_stat(vfs *me, char *path, struct stat *buf)
598 return undelfs_lstat (me, path, buf);
602 static int
603 undelfs_fstat (void *vfs_info, struct stat *buf)
605 undelfs_file *p = vfs_info;
607 return do_stat (p->f_index, buf);
610 static int
611 undelfs_chdir(vfs *me, char *path)
613 char *file, *f;
614 int fd;
616 undelfs_get_path (path, &file, &f);
617 if (!file)
618 return -1;
620 /* We may use access because ext2 file systems are local */
621 /* this could be fixed by making an ext2fs io manager to use */
622 /* our vfs, but that is left as an excercise for the reader */
623 if ((fd = open (file, O_RDONLY)) == -1){
624 message_2s (1, undelfserr, _(" Could not open file: %s "), file);
625 g_free (f);
626 g_free (file);
627 return -1;
629 close (fd);
630 g_free (f);
631 g_free (file);
632 return 0;
635 /* this has to stay here for now: vfs layer does not know how to emulate it */
636 static int
637 undelfs_lseek(void *vfs_info, off_t offset, int whence)
639 return -1;
642 static vfsid
643 undelfs_getid(vfs *me, char *path, struct vfs_stamping **parent)
645 char *ext2_fname, *file;
647 /* We run only on the local fs */
648 *parent = NULL;
649 undelfs_get_path (path, &ext2_fname, &file);
651 if (!ext2_fname)
652 return (vfsid) -1;
653 g_free (ext2_fname);
654 g_free (file);
655 return (vfsid)0;
658 static int
659 undelfs_nothingisopen(vfsid id)
661 return !undelfs_usage;
664 static void
665 undelfs_free(vfsid id)
667 undelfs_shutdown ();
670 #ifdef ENABLE_NLS
671 static int
672 undelfs_init(vfs *me) {
673 undelfserr = _(undelfserr);
674 return 1;
676 #else
677 #define undelfs_init NULL
678 #endif
680 vfs vfs_undelfs_ops = {
681 NULL, /* This is place of next pointer */
682 "undelfs",
683 0, /* flags */
684 "undel:", /* prefix */
685 NULL, /* data */
686 0, /* errno */
687 undelfs_init,
688 NULL,
689 NULL, /* fill_names */
690 NULL,
692 undelfs_open,
693 undelfs_close,
694 undelfs_read,
695 NULL,
697 undelfs_opendir,
698 undelfs_readdir,
699 undelfs_closedir,
700 NULL,
701 NULL,
703 undelfs_stat,
704 undelfs_lstat,
705 undelfs_fstat,
707 NULL,
708 NULL,
709 NULL,
711 NULL, /* readlink */
712 NULL,
713 NULL,
714 NULL,
716 NULL,
717 undelfs_chdir,
718 NULL,
719 undelfs_lseek,
720 NULL,
722 undelfs_getid,
723 undelfs_nothingisopen,
724 undelfs_free,
726 NULL, /* get_local_copy */
727 NULL,
729 NULL,
730 NULL,
731 NULL,
732 NULL
734 MMAPNULL