mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / undelfs.c
blob71c5bef02040e4c5d2dc9d60c08a8a2cac69f916
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 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Library General Public License
16 as published by the Free Software Foundation; either version 2 of
17 the License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU Library General Public License for more details.
24 You should have received a copy of the GNU Library General Public
25 License along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28 /* Assumptions:
30 * 1. We don't handle directories (thus undelfs_get_path is easy to write).
31 * 2. Files are on the local file system (we do not support vfs files
32 * because we would have to provide an io_manager for the ext2fs tools,
33 * and I don't think it would be too useful to undelete files
36 #include <config.h>
37 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 /* asm/types.h defines its own umode_t :-( */
42 #undef umode_t
43 #include <linux/ext2_fs.h>
44 #include <ext2fs/ext2fs.h>
45 #include <ctype.h>
47 #include "utilvfs.h"
49 #include "vfs.h"
51 struct deleted_info {
52 ext2_ino_t ino;
53 unsigned short mode;
54 unsigned short uid;
55 unsigned short gid;
56 unsigned long size;
57 time_t dtime;
58 int num_blocks;
59 int free_blocks;
62 struct lsdel_struct {
63 ext2_ino_t inode;
64 int num_blocks;
65 int free_blocks;
66 int bad_blocks;
69 /* We only allow one opened ext2fs */
70 static char *ext2_fname;
71 static ext2_filsys fs = NULL;
72 static struct lsdel_struct lsd;
73 static struct deleted_info *delarray;
74 static int num_delarray, max_delarray;
75 static char *block_buf;
76 static char *undelfserr = N_(" undelfs: error ");
77 static int readdir_ptr;
78 static int undelfs_usage;
80 /* To generate the . and .. entries use -2 */
81 #define READDIR_PTR_INIT 0
83 static void
84 undelfs_shutdown (void)
86 if (fs)
87 ext2fs_close (fs);
88 fs = NULL;
89 if (ext2_fname)
90 g_free (ext2_fname);
91 ext2_fname = NULL;
92 if (delarray)
93 g_free (delarray);
94 delarray = NULL;
95 if (block_buf)
96 g_free (block_buf);
97 block_buf = NULL;
100 static void
101 undelfs_get_path (char *dirname, char **ext2_fname, char **file)
103 char *p;
105 /* To look like filesystem, we have virtual directories
106 /#undel:XXX, which have no subdirectories. XXX is replaced with
107 hda5, sdb8 etc, which is assumed to live under /dev.
108 -- pavel@ucw.cz */
110 *ext2_fname = NULL;
112 if (strncmp (dirname, "/#undel:", 8))
113 return;
114 else
115 dirname += 8;
117 /* Since we don't allow subdirectories, it's easy to get a filename,
118 * just scan backwards for a slash */
119 if (*dirname == 0)
120 return;
122 p = dirname + strlen (dirname);
123 #if 0
124 /* Strip trailing ./
126 if (p - dirname > 2 && *(p-1) == '/' && *(p-2) == '.')
127 *(p = p-2) = 0;
128 #endif
130 while (p > dirname){
131 if (*p == '/'){
132 *file = g_strdup (p+1);
133 *p = 0;
134 *ext2_fname = g_strconcat ("/dev/", dirname, NULL);
135 *p = '/';
136 return;
138 p--;
140 *file = g_strdup ("");
141 *ext2_fname = g_strconcat ("/dev/", dirname, NULL);
142 return;
145 static int
146 lsdel_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *private)
148 struct lsdel_struct *lsd = (struct lsdel_struct *) private;
150 lsd->num_blocks++;
152 if (*block_nr < fs->super->s_first_data_block ||
153 *block_nr >= fs->super->s_blocks_count) {
154 lsd->bad_blocks++;
155 return BLOCK_ABORT;
158 if (!ext2fs_test_block_bitmap(fs->block_map,*block_nr))
159 lsd->free_blocks++;
161 return 0;
164 /* We don't use xmalloc, since we want to recover and not abort the program
165 * if we don't have enough memory
167 static int
168 undelfs_loaddel (void)
170 int retval, count;
171 ext2_ino_t ino;
172 struct ext2_inode inode;
173 ext2_inode_scan scan;
175 max_delarray = 100;
176 num_delarray = 0;
177 delarray = g_new (struct deleted_info, max_delarray);
178 if (!delarray) {
179 message_1s (1, undelfserr, _(" not enough memory "));
180 return 0;
182 block_buf = g_malloc (fs->blocksize * 3);
183 if (!block_buf) {
184 message_1s (1, undelfserr, _(" while allocating block buffer "));
185 goto free_delarray;
187 if ((retval = ext2fs_open_inode_scan(fs, 0, &scan))){
188 message_1s1d (1, undelfserr, _(" open_inode_scan: %d "), retval);
189 goto free_block_buf;
191 if ((retval = ext2fs_get_next_inode(scan, &ino, &inode))){
192 message_1s1d (1, undelfserr, _(" while starting inode scan %d "), retval);
193 goto error_out;
196 count = 0;
197 while (ino) {
198 if ((count++ % 1024) == 0)
199 print_vfs_message (_("undelfs: loading deleted files information %d inodes"), count);
200 if (inode.i_dtime == 0)
201 goto next;
203 if (S_ISDIR(inode.i_mode))
204 goto next;
206 lsd.inode = ino;
207 lsd.num_blocks = 0;
208 lsd.free_blocks = 0;
209 lsd.bad_blocks = 0;
211 retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
212 lsdel_proc, &lsd);
213 if (retval) {
214 message_1s1d (1, undelfserr, _(" while calling ext2_block_iterate %d "), retval);
215 goto next;
217 if (lsd.free_blocks && !lsd.bad_blocks) {
218 if (num_delarray >= max_delarray) {
219 max_delarray += 50;
220 delarray = g_renew (struct deleted_info, delarray, max_delarray);
221 if (!delarray) {
222 message_1s (1, undelfserr, _(" no more memory while reallocating array "));
223 goto error_out;
227 delarray[num_delarray].ino = ino;
228 delarray[num_delarray].mode = inode.i_mode;
229 delarray[num_delarray].uid = inode.i_uid;
230 delarray[num_delarray].gid = inode.i_gid;
231 delarray[num_delarray].size = inode.i_size;
232 delarray[num_delarray].dtime = inode.i_dtime;
233 delarray[num_delarray].num_blocks = lsd.num_blocks;
234 delarray[num_delarray].free_blocks = lsd.free_blocks;
235 num_delarray++;
238 next:
239 retval = ext2fs_get_next_inode(scan, &ino, &inode);
240 if (retval) {
241 message_1s1d(1, undelfserr, _(" while doing inode scan %d "), retval);
242 goto error_out;
245 readdir_ptr = READDIR_PTR_INIT;
246 ext2fs_close_inode_scan (scan);
247 return 1;
249 error_out:
250 ext2fs_close_inode_scan (scan);
251 free_block_buf:
252 g_free (block_buf);
253 block_buf = NULL;
254 free_delarray:
255 g_free (delarray);
256 delarray = NULL;
257 return 0;
260 void com_err (const char *str, long err_code, const char *s2, ...)
262 char *cptr;
264 cptr = g_strdup_printf (" %s (%s: %ld) ", s2, str, err_code);
265 message_1s (1, _(" Ext2lib error "), cptr);
266 g_free (cptr);
269 static void *
270 undelfs_opendir (vfs *me, char *dirname)
272 char *file, *f;
274 undelfs_get_path (dirname, &file, &f);
275 if (!file)
276 return 0;
278 /* We don't use the file name */
279 g_free (f);
281 if (!ext2_fname || strcmp (ext2_fname, file)){
282 undelfs_shutdown ();
283 ext2_fname = file;
284 } else {
285 /* To avoid expensive re-scannings */
286 readdir_ptr = READDIR_PTR_INIT;
287 g_free (file);
288 return fs;
291 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)){
292 message_2s (1, undelfserr, _(" Could not open file %s "), ext2_fname);
293 return 0;
295 print_vfs_message (_("undelfs: reading inode bitmap..."));
296 if (ext2fs_read_inode_bitmap (fs)){
297 message_2s (1, undelfserr,
298 _(" Could not load inode bitmap from: \n %s \n"), ext2_fname);
299 goto quit_opendir;
301 print_vfs_message (_("undelfs: reading block bitmap..."));
302 if (ext2fs_read_block_bitmap (fs)){
303 message_2s (1, undelfserr,
304 _(" Could not load block bitmap from: \n %s \n"), ext2_fname);
305 goto quit_opendir;
307 /* Now load the deleted information */
308 if (!undelfs_loaddel ())
309 goto quit_opendir;
310 print_vfs_message (_("%s: done."), me->name);
311 return fs;
312 quit_opendir:
313 print_vfs_message (_("%s: failure"), me->name);
314 ext2fs_close (fs);
315 fs = NULL;
316 return 0;
320 static void *
321 undelfs_readdir(void *vfs_info)
323 static union vfs_dirent undelfs_readdir_data;
324 char *dirent_dest;
326 if (vfs_info != fs) {
327 message_1s(1, undelfserr, _(" vfs_info is not fs! "));
328 return NULL;
330 if (readdir_ptr == num_delarray)
331 return NULL;
332 dirent_dest = undelfs_readdir_data.dent.d_name;
333 if (readdir_ptr < 0)
334 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%s",
335 readdir_ptr == -2 ? "." : "..");
336 else
337 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
338 (long) delarray[readdir_ptr].ino,
339 delarray[readdir_ptr].num_blocks);
340 readdir_ptr++;
342 compute_namelen(&undelfs_readdir_data.dent);
344 return &undelfs_readdir_data;
347 static int
348 undelfs_closedir (void *vfs_info)
350 return 0;
353 typedef struct {
354 int f_index; /* file index into delarray */
355 char *buf;
356 int error_code; /* */
357 int pos; /* file position */
358 int current; /* used to determine current position in itereate */
359 int finished;
360 ext2_ino_t inode;
361 int bytes_read;
362 long size;
364 /* Used by undelfs_read: */
365 char *dest_buffer; /* destination buffer */
366 int count; /* bytes to read */
367 } undelfs_file;
369 /* We do not support lseek */
370 static void *
371 undelfs_open (vfs *me, char *fname, int flags, int mode)
373 char *file, *f;
374 ext2_ino_t inode, i;
375 undelfs_file *p = NULL;
377 /* Only allow reads on this file system */
378 undelfs_get_path (fname, &file, &f);
379 if (!file)
380 return 0;
382 if (!ext2_fname || strcmp (ext2_fname, file)){
383 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
384 g_free (file);
385 g_free (f);
386 return 0;
388 inode = atol (f);
390 /* Search the file into delarray */
391 for (i = 0; i < num_delarray; i++){
392 if (inode != delarray [i].ino)
393 continue;
395 /* Found: setup all the structures needed by read */
396 p = g_new (undelfs_file, 1);
397 if (!p){
398 g_free (file);
399 g_free (f);
400 return 0;
402 p->buf = g_malloc (fs->blocksize);
403 if (!p->buf){
404 g_free (p);
405 g_free (file);
406 g_free (f);
407 return 0;
409 p->inode = inode;
410 p->finished = 0;
411 p->f_index = i;
412 p->error_code = 0;
413 p->pos = 0;
414 p->size = delarray [i].size;
416 g_free (file);
417 g_free (f);
418 undelfs_usage++;
419 return p;
422 static int
423 undelfs_close (void *vfs_info)
425 undelfs_file *p = vfs_info;
426 g_free (p->buf);
427 g_free (p);
428 undelfs_usage--;
429 return 0;
432 static int
433 dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
435 int copy_count;
436 undelfs_file *p = (undelfs_file *) private;
438 if (blockcnt < 0)
439 return 0;
441 if (*blocknr) {
442 p->error_code = io_channel_read_blk(fs->io, *blocknr,
443 1, p->buf);
444 if (p->error_code)
445 return BLOCK_ABORT;
446 } else
447 memset(p->buf, 0, fs->blocksize);
449 if (p->pos + p->count < p->current){
450 p->finished = 1;
451 return BLOCK_ABORT;
453 if (p->pos > p->current + fs->blocksize){
454 p->current += fs->blocksize;
455 return 0; /* we have not arrived yet */
458 /* Now, we know we have to extract some data */
459 if (p->pos >= p->current){
461 /* First case: starting pointer inside this block */
462 if (p->pos + p->count <= p->current + fs->blocksize){
463 /* Fully contained */
464 copy_count = p->count;
465 p->finished = p->count;
466 } else {
467 /* Still some more data */
468 copy_count = fs->blocksize-(p->pos-p->current);
470 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
471 } else {
472 /* Second case: we already have passed p->pos */
473 if (p->pos+p->count < p->current+fs->blocksize){
474 copy_count = (p->pos + p->count) - p->current;
475 p->finished = p->count;
476 } else {
477 copy_count = fs->blocksize;
479 memcpy (p->dest_buffer, p->buf, copy_count);
481 p->dest_buffer += copy_count;
482 p->current += fs->blocksize;
483 if (p->finished){
484 return BLOCK_ABORT;
486 return 0;
489 static int
490 undelfs_read (void *vfs_info, char *buffer, int count)
492 undelfs_file *p = vfs_info;
493 int retval;
495 p->dest_buffer = buffer;
496 p->current = 0;
497 p->finished = 0;
498 p->count = count;
500 if (p->pos + p->count > p->size){
501 p->count = p->size - p->pos;
503 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
504 dump_read, p);
505 if (retval){
506 message_1s (1, undelfserr, _(" while iterating over blocks "));
507 return -1;
509 if (p->error_code && !p->finished)
510 return 0;
511 p->pos = p->pos + (p->dest_buffer - buffer);
512 return p->dest_buffer - buffer;
515 static long
516 undelfs_getindex (char *path)
518 ext2_ino_t inode = atol (path);
519 int i;
521 for (i = 0; i < num_delarray; i++){
522 if (delarray [i].ino == inode)
523 return i;
525 return -1;
528 static int
529 do_stat (int inode_index, struct stat *buf)
531 buf->st_dev = 0;
532 buf->st_ino = delarray [inode_index].ino;
533 buf->st_mode = delarray [inode_index].mode;
534 buf->st_nlink = 1;
535 buf->st_uid = delarray [inode_index].uid;
536 buf->st_gid = delarray [inode_index].gid;
537 buf->st_size = delarray [inode_index].size;
538 buf->st_atime = delarray [inode_index].dtime;
539 buf->st_ctime = delarray [inode_index].dtime;
540 buf->st_mtime = delarray [inode_index].dtime;
541 return 0;
544 static int
545 undelfs_lstat(vfs *me, char *path, struct stat *buf)
547 int inode_index;
548 char *file, *f;
550 undelfs_get_path (path, &file, &f);
551 if (!file)
552 return 0;
554 /* When called from save_cwd_stats we get an incorrect file and f here:
555 e.g. incorrect correct
556 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
557 file = "/dev" file="/dev/sda1"
558 f = "sda1" f ="401:1"
559 If the first char in f is no digit -> return error */
560 if (!isdigit (*f)) {
561 g_free (file);
562 g_free (f);
563 return -1;
566 if (!ext2_fname || strcmp (ext2_fname, file)){
567 message_1s (1, undelfserr, _(" You have to chdir to extract files first "));
568 g_free (file);
569 g_free (f);
570 return 0;
572 inode_index = undelfs_getindex (f);
573 g_free (file);
574 g_free (f);
576 if (inode_index == -1)
577 return -1;
579 return do_stat (inode_index, buf);
582 static int
583 undelfs_stat(vfs *me, char *path, struct stat *buf)
585 return undelfs_lstat (me, path, buf);
589 static int
590 undelfs_fstat (void *vfs_info, struct stat *buf)
592 undelfs_file *p = vfs_info;
594 return do_stat (p->f_index, buf);
597 static int
598 undelfs_chdir(vfs *me, char *path)
600 char *file, *f;
601 int fd;
603 undelfs_get_path (path, &file, &f);
604 if (!file)
605 return -1;
607 /* We may use access because ext2 file systems are local */
608 /* this could be fixed by making an ext2fs io manager to use */
609 /* our vfs, but that is left as an excercise for the reader */
610 if ((fd = open (file, O_RDONLY)) == -1){
611 message_2s (1, undelfserr, _(" Could not open file %s "), file);
612 g_free (f);
613 g_free (file);
614 return -1;
616 close (fd);
617 g_free (f);
618 g_free (file);
619 return 0;
622 /* this has to stay here for now: vfs layer does not know how to emulate it */
623 static int
624 undelfs_lseek(void *vfs_info, off_t offset, int whence)
626 return -1;
629 static vfsid
630 undelfs_getid(vfs *me, char *path, struct vfs_stamping **parent)
632 char *ext2_fname, *file;
634 /* We run only on the local fs */
635 *parent = NULL;
636 undelfs_get_path (path, &ext2_fname, &file);
638 if (!ext2_fname)
639 return (vfsid) -1;
640 g_free (ext2_fname);
641 g_free (file);
642 return (vfsid)0;
645 static int
646 undelfs_nothingisopen(vfsid id)
648 return !undelfs_usage;
651 static void
652 undelfs_free(vfsid id)
654 undelfs_shutdown ();
657 #ifdef ENABLE_NLS
658 static int
659 undelfs_init(vfs *me) {
660 undelfserr = _(undelfserr);
661 return 1;
663 #else
664 #define undelfs_init NULL
665 #endif
667 vfs vfs_undelfs_ops = {
668 NULL, /* This is place of next pointer */
669 "undelfs",
670 0, /* flags */
671 "undel:", /* prefix */
672 NULL, /* data */
673 0, /* errno */
674 undelfs_init,
675 NULL,
676 NULL, /* fill_names */
677 NULL,
679 undelfs_open,
680 undelfs_close,
681 undelfs_read,
682 NULL,
684 undelfs_opendir,
685 undelfs_readdir,
686 undelfs_closedir,
687 NULL,
688 NULL,
690 undelfs_stat,
691 undelfs_lstat,
692 undelfs_fstat,
694 NULL,
695 NULL,
696 NULL,
698 NULL, /* readlink */
699 NULL,
700 NULL,
701 NULL,
703 NULL,
704 undelfs_chdir,
705 NULL,
706 undelfs_lseek,
707 NULL,
709 undelfs_getid,
710 undelfs_nothingisopen,
711 undelfs_free,
713 NULL, /* get_local_copy */
714 NULL,
716 NULL,
717 NULL,
718 NULL,
719 NULL
721 MMAPNULL