Updated italian translation
[midnight-commander.git] / vfs / undelfs.c
blob9bf2f2299208f4bd6573060c828d02bcec9f5fad
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
42 #ifdef HAVE_EXT2FS_EXT2_FS_H
43 #include <ext2fs/ext2_fs.h>
44 #else
45 /* asm/types.h defines its own umode_t */
46 #undef umode_t
47 #include <linux/ext2_fs.h>
48 #endif
50 #include <ext2fs/ext2fs.h>
51 #include <ctype.h>
53 #include "../src/global.h"
54 #include "../src/tty.h" /* enable/disable interrupt key */
55 #include "../src/wtools.h" /* message() */
56 #include "../src/main.h" /* print_vfs_message */
57 #include "utilvfs.h"
58 #include "vfs.h"
59 #include "vfs-impl.h"
61 struct deleted_info {
62 ext2_ino_t ino;
63 unsigned short mode;
64 unsigned short uid;
65 unsigned short gid;
66 unsigned long size;
67 time_t dtime;
68 int num_blocks;
69 int free_blocks;
72 struct lsdel_struct {
73 ext2_ino_t inode;
74 int num_blocks;
75 int free_blocks;
76 int bad_blocks;
79 /* We only allow one opened ext2fs */
80 static char *ext2_fname;
81 static ext2_filsys fs = NULL;
82 static struct lsdel_struct lsd;
83 static struct deleted_info *delarray;
84 static int num_delarray, max_delarray;
85 static char *block_buf;
86 static char *undelfserr = N_(" undelfs: error ");
87 static int readdir_ptr;
88 static int undelfs_usage;
89 static struct vfs_class vfs_undelfs_ops;
91 /* To generate the . and .. entries use -2 */
92 #define READDIR_PTR_INIT 0
94 static void
95 undelfs_shutdown (void)
97 if (fs)
98 ext2fs_close (fs);
99 fs = NULL;
100 g_free (ext2_fname);
101 ext2_fname = NULL;
102 g_free (delarray);
103 delarray = NULL;
104 g_free (block_buf);
105 block_buf = NULL;
108 static void
109 undelfs_get_path (const char *dirname, char **fsname, char **file)
111 const char *p;
113 /* To look like filesystem, we have virtual directories
114 /#undel:XXX, which have no subdirectories. XXX is replaced with
115 hda5, sdb8 etc, which is assumed to live under /dev.
116 -- pavel@ucw.cz */
118 *fsname = NULL;
120 if (strncmp (dirname, "/#undel:", 8))
121 return;
122 else
123 dirname += 8;
125 /* Since we don't allow subdirectories, it's easy to get a filename,
126 * just scan backwards for a slash */
127 if (*dirname == 0)
128 return;
130 p = dirname + strlen (dirname);
131 #if 0
132 /* Strip trailing ./
134 if (p - dirname > 2 && *(p-1) == '/' && *(p-2) == '.')
135 *(p = p-2) = 0;
136 #endif
138 while (p > dirname){
139 if (*p == '/'){
140 char *tmp;
142 *file = g_strdup (p+1);
143 tmp = g_strndup (dirname, p - dirname);
144 *fsname = g_strconcat ("/dev/", tmp, (char *) NULL);
145 g_free (tmp);
146 return;
148 p--;
150 *file = g_strdup ("");
151 *fsname = g_strconcat ("/dev/", dirname, (char *) NULL);
152 return;
155 static int
156 undelfs_lsdel_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *private)
158 struct lsdel_struct *lsd = (struct lsdel_struct *) private;
160 lsd->num_blocks++;
162 if (*block_nr < fs->super->s_first_data_block ||
163 *block_nr >= fs->super->s_blocks_count) {
164 lsd->bad_blocks++;
165 return BLOCK_ABORT;
168 if (!ext2fs_test_block_bitmap(fs->block_map,*block_nr))
169 lsd->free_blocks++;
171 return 0;
175 * Load information about deleted files.
176 * Don't abort if there is not enough memory - load as much as we can.
178 static int
179 undelfs_loaddel (void)
181 int retval, count;
182 ext2_ino_t ino;
183 struct ext2_inode inode;
184 ext2_inode_scan scan;
186 max_delarray = 100;
187 num_delarray = 0;
188 delarray = g_try_malloc (sizeof (struct deleted_info) * max_delarray);
189 if (!delarray) {
190 message (1, undelfserr, _(" not enough memory "));
191 return 0;
193 block_buf = g_try_malloc (fs->blocksize * 3);
194 if (!block_buf) {
195 message (1, undelfserr, _(" while allocating block buffer "));
196 goto free_delarray;
198 if ((retval = ext2fs_open_inode_scan (fs, 0, &scan))) {
199 message (1, undelfserr, _(" open_inode_scan: %d "), retval);
200 goto free_block_buf;
202 if ((retval = ext2fs_get_next_inode (scan, &ino, &inode))) {
203 message (1, undelfserr, _(" while starting inode scan %d "),
204 retval);
205 goto error_out;
208 count = 0;
209 while (ino) {
210 if ((count++ % 1024) == 0)
211 print_vfs_message (_
212 ("undelfs: loading deleted files information %d inodes"),
213 count);
214 if (inode.i_dtime == 0)
215 goto next;
217 if (S_ISDIR (inode.i_mode))
218 goto next;
220 lsd.inode = ino;
221 lsd.num_blocks = 0;
222 lsd.free_blocks = 0;
223 lsd.bad_blocks = 0;
225 retval =
226 ext2fs_block_iterate (fs, ino, 0, block_buf,
227 undelfs_lsdel_proc, &lsd);
228 if (retval) {
229 message (1, undelfserr,
230 _(" while calling ext2_block_iterate %d "), retval);
231 goto next;
233 if (lsd.free_blocks && !lsd.bad_blocks) {
234 if (num_delarray >= max_delarray) {
235 struct deleted_info *delarray_new =
236 g_try_realloc (delarray,
237 sizeof (struct deleted_info) *
238 (max_delarray + 50));
239 if (!delarray_new) {
240 message (1, undelfserr,
242 (" no more memory while reallocating array "));
243 goto error_out;
245 delarray = delarray_new;
246 max_delarray += 50;
249 delarray[num_delarray].ino = ino;
250 delarray[num_delarray].mode = inode.i_mode;
251 delarray[num_delarray].uid = inode.i_uid;
252 delarray[num_delarray].gid = inode.i_gid;
253 delarray[num_delarray].size = inode.i_size;
254 delarray[num_delarray].dtime = inode.i_dtime;
255 delarray[num_delarray].num_blocks = lsd.num_blocks;
256 delarray[num_delarray].free_blocks = lsd.free_blocks;
257 num_delarray++;
260 next:
261 retval = ext2fs_get_next_inode (scan, &ino, &inode);
262 if (retval) {
263 message (1, undelfserr, _(" while doing inode scan %d "),
264 retval);
265 goto error_out;
268 readdir_ptr = READDIR_PTR_INIT;
269 ext2fs_close_inode_scan (scan);
270 return 1;
272 error_out:
273 ext2fs_close_inode_scan (scan);
274 free_block_buf:
275 g_free (block_buf);
276 block_buf = NULL;
277 free_delarray:
278 g_free (delarray);
279 delarray = NULL;
280 return 0;
285 * This function overrides com_err() from libcom_err library.
286 * It is used in libext2fs to report errors.
288 void
289 com_err (const char *whoami, long err_code, const char *fmt, ...)
291 va_list ap;
292 char *str;
294 va_start (ap, fmt);
295 str = g_strdup_vprintf (fmt, ap);
296 va_end (ap);
298 message (1, _(" Ext2lib error "), " %s (%s: %ld) ", str, whoami,
299 err_code);
300 g_free (str);
303 static void *
304 undelfs_opendir (struct vfs_class *me, const char *dirname)
306 char *file, *f;
308 undelfs_get_path (dirname, &file, &f);
309 if (!file)
310 return 0;
312 /* We don't use the file name */
313 g_free (f);
315 if (!ext2_fname || strcmp (ext2_fname, file)){
316 undelfs_shutdown ();
317 ext2_fname = file;
318 } else {
319 /* To avoid expensive re-scannings */
320 readdir_ptr = READDIR_PTR_INIT;
321 g_free (file);
322 return fs;
325 if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)){
326 message (1, undelfserr, _(" Cannot open file %s "), ext2_fname);
327 return 0;
329 print_vfs_message (_("undelfs: reading inode bitmap..."));
330 if (ext2fs_read_inode_bitmap (fs)){
331 message (1, undelfserr,
332 _(" Cannot load inode bitmap from: \n %s \n"), ext2_fname);
333 goto quit_opendir;
335 print_vfs_message (_("undelfs: reading block bitmap..."));
336 if (ext2fs_read_block_bitmap (fs)){
337 message (1, undelfserr,
338 _(" Cannot load block bitmap from: \n %s \n"), ext2_fname);
339 goto quit_opendir;
341 /* Now load the deleted information */
342 if (!undelfs_loaddel ())
343 goto quit_opendir;
344 print_vfs_message (_("%s: done."), me->name);
345 return fs;
346 quit_opendir:
347 print_vfs_message (_("%s: failure"), me->name);
348 ext2fs_close (fs);
349 fs = NULL;
350 return 0;
354 static void *
355 undelfs_readdir(void *vfs_info)
357 static union vfs_dirent undelfs_readdir_data;
358 static char *const dirent_dest = undelfs_readdir_data.dent.d_name;
360 if (vfs_info != fs) {
361 message (1, undelfserr, _(" vfs_info is not fs! "));
362 return NULL;
364 if (readdir_ptr == num_delarray)
365 return NULL;
366 if (readdir_ptr < 0)
367 strcpy (dirent_dest, readdir_ptr == -2 ? "." : "..");
368 else
369 g_snprintf(dirent_dest, MC_MAXPATHLEN, "%ld:%d",
370 (long) delarray[readdir_ptr].ino,
371 delarray[readdir_ptr].num_blocks);
372 readdir_ptr++;
374 compute_namelen(&undelfs_readdir_data.dent);
376 return &undelfs_readdir_data;
379 static int
380 undelfs_closedir (void *vfs_info)
382 return 0;
385 typedef struct {
386 int f_index; /* file index into delarray */
387 char *buf;
388 int error_code; /* */
389 int pos; /* file position */
390 int current; /* used to determine current position in itereate */
391 int finished;
392 ext2_ino_t inode;
393 int bytes_read;
394 long size;
396 /* Used by undelfs_read: */
397 char *dest_buffer; /* destination buffer */
398 int count; /* bytes to read */
399 } undelfs_file;
401 /* We do not support lseek */
402 static void *
403 undelfs_open (struct vfs_class *me, const char *fname, int flags, int mode)
405 char *file, *f;
406 ext2_ino_t inode, i;
407 undelfs_file *p = NULL;
409 /* Only allow reads on this file system */
410 undelfs_get_path (fname, &file, &f);
411 if (!file)
412 return 0;
414 if (!ext2_fname || strcmp (ext2_fname, file)) {
415 message (1, undelfserr,
416 _(" You have to chdir to extract files first "));
417 g_free (file);
418 g_free (f);
419 return 0;
421 inode = atol (f);
423 /* Search the file into delarray */
424 for (i = 0; i < num_delarray; i++) {
425 if (inode != delarray[i].ino)
426 continue;
428 /* Found: setup all the structures needed by read */
429 p = (undelfs_file *) g_try_malloc (((gsize) sizeof (undelfs_file)));
430 if (!p) {
431 g_free (file);
432 g_free (f);
433 return 0;
435 p->buf = g_try_malloc (fs->blocksize);
436 if (!p->buf) {
437 g_free (p);
438 g_free (file);
439 g_free (f);
440 return 0;
442 p->inode = inode;
443 p->finished = 0;
444 p->f_index = i;
445 p->error_code = 0;
446 p->pos = 0;
447 p->size = delarray[i].size;
449 g_free (file);
450 g_free (f);
451 undelfs_usage++;
452 return p;
455 static int
456 undelfs_close (void *vfs_info)
458 undelfs_file *p = vfs_info;
459 g_free (p->buf);
460 g_free (p);
461 undelfs_usage--;
462 return 0;
465 static int
466 undelfs_dump_read(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
468 int copy_count;
469 undelfs_file *p = (undelfs_file *) private;
471 if (blockcnt < 0)
472 return 0;
474 if (*blocknr) {
475 p->error_code = io_channel_read_blk(fs->io, *blocknr,
476 1, p->buf);
477 if (p->error_code)
478 return BLOCK_ABORT;
479 } else
480 memset(p->buf, 0, fs->blocksize);
482 if (p->pos + p->count < p->current){
483 p->finished = 1;
484 return BLOCK_ABORT;
486 if (p->pos > p->current + fs->blocksize){
487 p->current += fs->blocksize;
488 return 0; /* we have not arrived yet */
491 /* Now, we know we have to extract some data */
492 if (p->pos >= p->current){
494 /* First case: starting pointer inside this block */
495 if (p->pos + p->count <= p->current + fs->blocksize){
496 /* Fully contained */
497 copy_count = p->count;
498 p->finished = p->count;
499 } else {
500 /* Still some more data */
501 copy_count = fs->blocksize-(p->pos-p->current);
503 memcpy (p->dest_buffer, p->buf + (p->pos-p->current), copy_count);
504 } else {
505 /* Second case: we already have passed p->pos */
506 if (p->pos+p->count < p->current+fs->blocksize){
507 copy_count = (p->pos + p->count) - p->current;
508 p->finished = p->count;
509 } else {
510 copy_count = fs->blocksize;
512 memcpy (p->dest_buffer, p->buf, copy_count);
514 p->dest_buffer += copy_count;
515 p->current += fs->blocksize;
516 if (p->finished){
517 return BLOCK_ABORT;
519 return 0;
522 static int
523 undelfs_read (void *vfs_info, char *buffer, int count)
525 undelfs_file *p = vfs_info;
526 int retval;
528 p->dest_buffer = buffer;
529 p->current = 0;
530 p->finished = 0;
531 p->count = count;
533 if (p->pos + p->count > p->size){
534 p->count = p->size - p->pos;
536 retval = ext2fs_block_iterate(fs, p->inode, 0, NULL,
537 undelfs_dump_read, p);
538 if (retval){
539 message (1, undelfserr, _(" while iterating over blocks "));
540 return -1;
542 if (p->error_code && !p->finished)
543 return 0;
544 p->pos = p->pos + (p->dest_buffer - buffer);
545 return p->dest_buffer - buffer;
548 static long
549 undelfs_getindex (char *path)
551 ext2_ino_t inode = atol (path);
552 int i;
554 for (i = 0; i < num_delarray; i++){
555 if (delarray [i].ino == inode)
556 return i;
558 return -1;
561 static int
562 undelfs_stat_int (int inode_index, struct stat *buf)
564 buf->st_dev = 0;
565 buf->st_ino = delarray [inode_index].ino;
566 buf->st_mode = delarray [inode_index].mode;
567 buf->st_nlink = 1;
568 buf->st_uid = delarray [inode_index].uid;
569 buf->st_gid = delarray [inode_index].gid;
570 buf->st_size = delarray [inode_index].size;
571 buf->st_atime = delarray [inode_index].dtime;
572 buf->st_ctime = delarray [inode_index].dtime;
573 buf->st_mtime = delarray [inode_index].dtime;
574 return 0;
577 static int
578 undelfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
580 int inode_index;
581 char *file, *f;
583 undelfs_get_path (path, &file, &f);
584 if (!file)
585 return 0;
587 /* When called from save_cwd_stats we get an incorrect file and f here:
588 e.g. incorrect correct
589 path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1"
590 file = "/dev" file="/dev/sda1"
591 f = "sda1" f ="401:1"
592 If the first char in f is no digit -> return error */
593 if (!isdigit (*f)) {
594 g_free (file);
595 g_free (f);
596 return -1;
599 if (!ext2_fname || strcmp (ext2_fname, file)){
600 message (1, undelfserr, _(" You have to chdir to extract files first "));
601 g_free (file);
602 g_free (f);
603 return 0;
605 inode_index = undelfs_getindex (f);
606 g_free (file);
607 g_free (f);
609 if (inode_index == -1)
610 return -1;
612 return undelfs_stat_int (inode_index, buf);
615 #define undelfs_stat undelfs_lstat
617 static int
618 undelfs_fstat (void *vfs_info, struct stat *buf)
620 undelfs_file *p = vfs_info;
622 return undelfs_stat_int (p->f_index, buf);
625 static int
626 undelfs_chdir(struct vfs_class *me, const char *path)
628 char *file, *f;
629 int fd;
631 undelfs_get_path (path, &file, &f);
632 if (!file)
633 return -1;
635 /* We may use access because ext2 file systems are local */
636 /* this could be fixed by making an ext2fs io manager to use */
637 /* our vfs, but that is left as an excercise for the reader */
638 if ((fd = open (file, O_RDONLY)) == -1){
639 message (1, undelfserr, _(" Cannot open file %s "), file);
640 g_free (f);
641 g_free (file);
642 return -1;
644 close (fd);
645 g_free (f);
646 g_free (file);
647 return 0;
650 /* this has to stay here for now: vfs layer does not know how to emulate it */
651 static int
652 undelfs_lseek(void *vfs_info, off_t offset, int whence)
654 return -1;
657 static vfsid
658 undelfs_getid (struct vfs_class *me, const char *path)
660 char *fname, *fsname;
662 undelfs_get_path (path, &fsname, &fname);
664 if (!fsname)
665 return NULL;
666 g_free (fname);
667 g_free (fsname);
668 return (vfsid) fs;
671 static int
672 undelfs_nothingisopen(vfsid id)
674 return !undelfs_usage;
677 static void
678 undelfs_free(vfsid id)
680 undelfs_shutdown ();
683 #ifdef ENABLE_NLS
684 static int
685 undelfs_init(struct vfs_class *me) {
686 undelfserr = _(undelfserr);
687 return 1;
689 #else
690 #define undelfs_init NULL
691 #endif
693 void
694 init_undelfs (void)
696 vfs_undelfs_ops.name = "undelfs";
697 vfs_undelfs_ops.prefix = "undel:";
698 vfs_undelfs_ops.init = undelfs_init;
699 vfs_undelfs_ops.open = undelfs_open;
700 vfs_undelfs_ops.close = undelfs_close;
701 vfs_undelfs_ops.read = undelfs_read;
702 vfs_undelfs_ops.opendir = undelfs_opendir;
703 vfs_undelfs_ops.readdir = undelfs_readdir;
704 vfs_undelfs_ops.closedir = undelfs_closedir;
705 vfs_undelfs_ops.stat = undelfs_stat;
706 vfs_undelfs_ops.lstat = undelfs_lstat;
707 vfs_undelfs_ops.fstat = undelfs_fstat;
708 vfs_undelfs_ops.chdir = undelfs_chdir;
709 vfs_undelfs_ops.lseek = undelfs_lseek;
710 vfs_undelfs_ops.getid = undelfs_getid;
711 vfs_undelfs_ops.nothingisopen = undelfs_nothingisopen;
712 vfs_undelfs_ops.free = undelfs_free;
713 vfs_register_class (&vfs_undelfs_ops);