*** empty log message ***
[midnight-commander.git] / vfs / undelfs.c
blob7376eb52329411af476e75df6e4e5480f6d22b85
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 (_("%s: done."), me->name);
314 return fs;
315 quit_opendir:
316 print_vfs_message (_("%s: failure"), me->name);
317 ext2fs_close (fs);
318 fs = NULL;
319 return 0;
323 static void *
324 undelfs_readdir(void *vfs_info)
326 static union vfs_dirent undelfs_readdir_data;
327 char *dirent_dest;
329 if (vfs_info != fs) {
330 message_1s(1, undelfserr, _(" vfs_info is not fs! "));
331 return NULL;
333 if (readdir_ptr == num_delarray)
334 return NULL;
335 dirent_dest = undelfs_readdir_data.dent.d_name;
336 if (readdir_ptr < 0)
337 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%s",
338 readdir_ptr == -2 ? "." : "..");
339 else
340 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
341 (long) delarray[readdir_ptr].ino,
342 delarray[readdir_ptr].num_blocks);
343 readdir_ptr++;
345 compute_namelen(&undelfs_readdir_data.dent);
347 return &undelfs_readdir_data;
350 static int
351 undelfs_closedir (void *vfs_info)
353 return 0;
356 typedef struct {
357 int f_index; /* file index into delarray */
358 char *buf;
359 int error_code; /* */
360 int pos; /* file position */
361 int current; /* used to determine current position in itereate */
362 int finished;
363 ext2_ino_t inode;
364 int bytes_read;
365 long size;
367 /* Used by undelfs_read: */
368 char *dest_buffer; /* destination buffer */
369 int count; /* bytes to read */
370 } undelfs_file;
372 /* We do not support lseek */
373 static void *
374 undelfs_open (vfs *me, char *fname, int flags, int mode)
376 char *file, *f;
377 ext2_ino_t inode, i;
378 undelfs_file *p = NULL;
380 /* Only allow reads on this file system */
381 undelfs_get_path (fname, &file, &f);
382 if (!file)
383 return 0;
385 if (!ext2_fname || strcmp (ext2_fname, file)){
386 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
387 g_free (file);
388 g_free (f);
389 return 0;
391 inode = atol (f);
393 /* Search the file into delarray */
394 for (i = 0; i < num_delarray; i++){
395 if (inode != delarray [i].ino)
396 continue;
398 /* Found: setup all the structures needed by read */
399 p = g_new (undelfs_file, 1);
400 if (!p){
401 g_free (file);
402 g_free (f);
403 return 0;
405 p->buf = g_malloc (fs->blocksize);
406 if (!p->buf){
407 g_free (p);
408 g_free (file);
409 g_free (f);
410 return 0;
412 p->inode = inode;
413 p->finished = 0;
414 p->f_index = i;
415 p->error_code = 0;
416 p->pos = 0;
417 p->size = delarray [i].size;
419 g_free (file);
420 g_free (f);
421 undelfs_usage++;
422 return p;
425 static int
426 undelfs_close (void *vfs_info)
428 undelfs_file *p = vfs_info;
429 g_free (p->buf);
430 g_free (p);
431 undelfs_usage--;
432 return 0;
435 static int
436 dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
438 int copy_count;
439 undelfs_file *p = (undelfs_file *) private;
441 if (blockcnt < 0)
442 return 0;
444 if (*blocknr) {
445 p->error_code = io_channel_read_blk(fs->io, *blocknr,
446 1, p->buf);
447 if (p->error_code)
448 return BLOCK_ABORT;
449 } else
450 memset(p->buf, 0, fs->blocksize);
452 if (p->pos + p->count < p->current){
453 p->finished = 1;
454 return BLOCK_ABORT;
456 if (p->pos > p->current + fs->blocksize){
457 p->current += fs->blocksize;
458 return 0; /* we have not arrived yet */
461 /* Now, we know we have to extract some data */
462 if (p->pos >= p->current){
464 /* First case: starting pointer inside this block */
465 if (p->pos + p->count <= p->current + fs->blocksize){
466 /* Fully contained */
467 copy_count = p->count;
468 p->finished = p->count;
469 } else {
470 /* Still some more data */
471 copy_count = fs->blocksize-(p->pos-p->current);
473 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
474 } else {
475 /* Second case: we already have passed p->pos */
476 if (p->pos+p->count < p->current+fs->blocksize){
477 copy_count = (p->pos + p->count) - p->current;
478 p->finished = p->count;
479 } else {
480 copy_count = fs->blocksize;
482 memcpy (p->dest_buffer, p->buf, copy_count);
484 p->dest_buffer += copy_count;
485 p->current += fs->blocksize;
486 if (p->finished){
487 return BLOCK_ABORT;
489 return 0;
492 static int
493 undelfs_read (void *vfs_info, char *buffer, int count)
495 undelfs_file *p = vfs_info;
496 int retval;
498 p->dest_buffer = buffer;
499 p->current = 0;
500 p->finished = 0;
501 p->count = count;
503 if (p->pos + p->count > p->size){
504 p->count = p->size - p->pos;
506 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
507 dump_read, p);
508 if (retval){
509 message_1s (1, undelfserr, _(" while iterating over blocks "));
510 return -1;
512 if (p->error_code && !p->finished)
513 return 0;
514 p->pos = p->pos + (p->dest_buffer - buffer);
515 return p->dest_buffer - buffer;
518 static long
519 undelfs_getindex (char *path)
521 ext2_ino_t inode = atol (path);
522 int i;
524 for (i = 0; i < num_delarray; i++){
525 if (delarray [i].ino == inode)
526 return i;
528 return -1;
531 static int
532 do_stat (int inode_index, struct stat *buf)
534 buf->st_dev = 0;
535 buf->st_ino = delarray [inode_index].ino;
536 buf->st_mode = delarray [inode_index].mode;
537 buf->st_nlink = 1;
538 buf->st_uid = delarray [inode_index].uid;
539 buf->st_gid = delarray [inode_index].gid;
540 buf->st_size = delarray [inode_index].size;
541 buf->st_atime = delarray [inode_index].dtime;
542 buf->st_ctime = delarray [inode_index].dtime;
543 buf->st_mtime = delarray [inode_index].dtime;
544 return 0;
547 static int
548 undelfs_lstat(vfs *me, char *path, struct stat *buf)
550 int inode_index;
551 char *file, *f;
553 undelfs_get_path (path, &file, &f);
554 if (!file)
555 return 0;
557 /* When called from save_cwd_stats we get an incorrect file and f here:
558 e.g. incorrect correct
559 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
560 file = "/dev" file="/dev/sda1"
561 f = "sda1" f ="401:1"
562 If the first char in f is no digit -> return error */
563 if (!isdigit (*f)) {
564 g_free (file);
565 g_free (f);
566 return -1;
569 if (!ext2_fname || strcmp (ext2_fname, file)){
570 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
571 g_free (file);
572 g_free (f);
573 return 0;
575 inode_index = undelfs_getindex (f);
576 g_free (file);
577 g_free (f);
579 if (inode_index == -1)
580 return -1;
582 return do_stat (inode_index, buf);
585 static int
586 undelfs_stat(vfs *me, char *path, struct stat *buf)
588 return undelfs_lstat (me, path, buf);
592 static int
593 undelfs_fstat (void *vfs_info, struct stat *buf)
595 undelfs_file *p = vfs_info;
597 return do_stat (p->f_index, buf);
600 static int
601 undelfs_chdir(vfs *me, char *path)
603 char *file, *f;
604 int fd;
606 undelfs_get_path (path, &file, &f);
607 if (!file)
608 return -1;
610 /* We may use access because ext2 file systems are local */
611 /* this could be fixed by making an ext2fs io manager to use */
612 /* our vfs, but that is left as an excercise for the reader */
613 if ((fd = open (file, O_RDONLY)) == -1){
614 message_2s (1, undelfserr, _(" Could not open file %s "), file);
615 g_free (f);
616 g_free (file);
617 return -1;
619 close (fd);
620 g_free (f);
621 g_free (file);
622 return 0;
625 /* this has to stay here for now: vfs layer does not know how to emulate it */
626 static int
627 undelfs_lseek(void *vfs_info, off_t offset, int whence)
629 return -1;
632 static vfsid
633 undelfs_getid(vfs *me, char *path, struct vfs_stamping **parent)
635 char *ext2_fname, *file;
637 /* We run only on the local fs */
638 *parent = NULL;
639 undelfs_get_path (path, &ext2_fname, &file);
641 if (!ext2_fname)
642 return (vfsid) -1;
643 g_free (ext2_fname);
644 g_free (file);
645 return (vfsid)0;
648 static int
649 undelfs_nothingisopen(vfsid id)
651 return !undelfs_usage;
654 static void
655 undelfs_free(vfsid id)
657 undelfs_shutdown ();
660 #ifdef ENABLE_NLS
661 static int
662 undelfs_init(vfs *me) {
663 undelfserr = _(undelfserr);
664 return 1;
666 #else
667 #define undelfs_init NULL
668 #endif
670 vfs vfs_undelfs_ops = {
671 NULL, /* This is place of next pointer */
672 "undelfs",
673 0, /* flags */
674 "undel:", /* prefix */
675 NULL, /* data */
676 0, /* errno */
677 undelfs_init,
678 NULL,
679 NULL, /* fill_names */
680 NULL,
682 undelfs_open,
683 undelfs_close,
684 undelfs_read,
685 NULL,
687 undelfs_opendir,
688 undelfs_readdir,
689 undelfs_closedir,
690 NULL,
691 NULL,
693 undelfs_stat,
694 undelfs_lstat,
695 undelfs_fstat,
697 NULL,
698 NULL,
699 NULL,
701 NULL, /* readlink */
702 NULL,
703 NULL,
704 NULL,
706 NULL,
707 undelfs_chdir,
708 NULL,
709 undelfs_lseek,
710 NULL,
712 undelfs_getid,
713 undelfs_nothingisopen,
714 undelfs_free,
716 NULL, /* get_local_copy */
717 NULL,
719 NULL,
720 NULL,
721 NULL,
722 NULL
724 MMAPNULL