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.
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. */
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
44 /* asm/types.h defines its own umode_t :-( */
46 #include <linux/ext2_fs.h>
47 #include <ext2fs/ext2fs.h>
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
87 undelfs_shutdown (void)
104 undelfs_get_path (char *dirname
, char **ext2_fname
, char **file
)
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.
115 if (strncmp (dirname
, "/#undel:", 8))
120 /* Since we don't allow subdirectories, it's easy to get a filename,
121 * just scan backwards for a slash */
125 p
= dirname
+ strlen (dirname
);
129 if (p
- dirname
> 2 && *(p
-1) == '/' && *(p
-2) == '.')
135 *file
= g_strdup (p
+1);
137 *ext2_fname
= g_strconcat ("/dev/", dirname
, NULL
);
143 *file
= g_strdup ("");
144 *ext2_fname
= g_strconcat ("/dev/", dirname
, NULL
);
149 lsdel_proc(ext2_filsys fs
, blk_t
*block_nr
, int blockcnt
, void *private)
151 struct lsdel_struct
*lsd
= (struct lsdel_struct
*) private;
155 if (*block_nr
< fs
->super
->s_first_data_block
||
156 *block_nr
>= fs
->super
->s_blocks_count
) {
161 if (!ext2fs_test_block_bitmap(fs
->block_map
,*block_nr
))
167 /* We don't use xmalloc, since we want to recover and not abort the program
168 * if we don't have enough memory
171 undelfs_loaddel (void)
175 struct ext2_inode inode
;
176 ext2_inode_scan scan
;
180 delarray
= g_new (struct deleted_info
, max_delarray
);
182 message_1s (1, undelfserr
, _(" not enough memory "));
185 block_buf
= g_malloc (fs
->blocksize
* 3);
187 message_1s (1, undelfserr
, _(" while allocating block buffer "));
190 if ((retval
= ext2fs_open_inode_scan(fs
, 0, &scan
))){
191 message_1s1d (1, undelfserr
, _(" open_inode_scan: %d "), retval
);
194 if ((retval
= ext2fs_get_next_inode(scan
, &ino
, &inode
))){
195 message_1s1d (1, undelfserr
, _(" while starting inode scan %d "), retval
);
201 if ((count
++ % 1024) == 0)
202 print_vfs_message (_("undelfs: loading deleted files information %d inodes"), count
);
203 if (inode
.i_dtime
== 0)
206 if (S_ISDIR(inode
.i_mode
))
214 retval
= ext2fs_block_iterate(fs
, ino
, 0, block_buf
,
217 message_1s1d (1, undelfserr
, _(" while calling ext2_block_iterate %d "), retval
);
220 if (lsd
.free_blocks
&& !lsd
.bad_blocks
) {
221 if (num_delarray
>= max_delarray
) {
223 delarray
= g_renew (struct deleted_info
, delarray
, max_delarray
);
225 message_1s (1, undelfserr
, _(" no more memory while reallocating array "));
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
;
242 retval
= ext2fs_get_next_inode(scan
, &ino
, &inode
);
244 message_1s1d(1, undelfserr
, _(" while doing inode scan %d "), retval
);
248 readdir_ptr
= READDIR_PTR_INIT
;
249 ext2fs_close_inode_scan (scan
);
253 ext2fs_close_inode_scan (scan
);
263 void com_err (const char *str
, long err_code
, const char *s2
, ...)
267 cptr
= g_strdup_printf (" %s (%s: %ld) ", s2
, str
, err_code
);
268 message_1s (1, _(" Ext2lib error "), cptr
);
273 undelfs_opendir (vfs
*me
, char *dirname
)
277 undelfs_get_path (dirname
, &file
, &f
);
281 /* We don't use the file name */
284 if (!ext2_fname
|| strcmp (ext2_fname
, file
)){
288 /* To avoid expensive re-scannings */
289 readdir_ptr
= READDIR_PTR_INIT
;
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
);
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
);
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
);
310 /* Now load the deleted information */
311 if (!undelfs_loaddel ())
313 print_vfs_message (_("undelfs: done."));
316 print_vfs_message (_("undelfs: failure"));
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.
329 #ifdef NEED_EXTRA_DIRENT_BUFFER
330 char extra_buffer
[MC_MAXPATHLEN
];
332 } undelfs_readdir_data
;
335 undelfs_readdir (void *vfs_info
)
340 message_1s (1, undelfserr
, _(" vfs_info is not fs! "));
343 if (readdir_ptr
== num_delarray
)
345 dirent_dest
= (char *) &(undelfs_readdir_data
.dent
.d_name
[0]);
347 g_snprintf(dirent_dest
, MC_MAXPATHLEN
, "%s", readdir_ptr
== -2 ? "." : "..");
349 g_snprintf(dirent_dest
, MC_MAXPATHLEN
, "%ld:%d",
350 (long)delarray
[readdir_ptr
].ino
,
351 delarray
[readdir_ptr
].num_blocks
);
355 undelfs_readdir_data
.dent
.d_namlen
= strlen (undelfs_readdir_data
.dent
.d_name
);
357 return &undelfs_readdir_data
;
361 undelfs_closedir (void *vfs_info
)
367 int f_index
; /* file index into delarray */
369 int error_code
; /* */
370 int pos
; /* file position */
371 int current
; /* used to determine current position in itereate */
377 /* Used by undelfs_read: */
378 char *dest_buffer
; /* destination buffer */
379 int count
; /* bytes to read */
382 /* We do not support lseek */
384 undelfs_open (vfs
*me
, char *fname
, int flags
, int mode
)
388 undelfs_file
*p
= NULL
;
390 /* Only allow reads on this file system */
391 undelfs_get_path (fname
, &file
, &f
);
395 if (!ext2_fname
|| strcmp (ext2_fname
, file
)){
396 message_1s (1, undelfserr
, _(" You have to chdir to extract files first "));
403 /* Search the file into delarray */
404 for (i
= 0; i
< num_delarray
; i
++){
405 if (inode
!= delarray
[i
].ino
)
408 /* Found: setup all the structures needed by read */
409 p
= g_new (undelfs_file
, 1);
415 p
->buf
= g_malloc (fs
->blocksize
);
427 p
->size
= delarray
[i
].size
;
436 undelfs_close (void *vfs_info
)
438 undelfs_file
*p
= vfs_info
;
446 dump_read(ext2_filsys fs
, blk_t
*blocknr
, int blockcnt
, void *private)
449 undelfs_file
*p
= (undelfs_file
*) private;
455 p
->error_code
= io_channel_read_blk(fs
->io
, *blocknr
,
460 memset(p
->buf
, 0, fs
->blocksize
);
462 if (p
->pos
+ p
->count
< p
->current
){
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
;
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
);
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
;
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
;
503 undelfs_read (void *vfs_info
, char *buffer
, int count
)
505 undelfs_file
*p
= vfs_info
;
508 p
->dest_buffer
= buffer
;
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
,
519 message_1s (1, undelfserr
, _(" while iterating over blocks "));
522 if (p
->error_code
&& !p
->finished
)
524 p
->pos
= p
->pos
+ (p
->dest_buffer
- buffer
);
525 return p
->dest_buffer
- buffer
;
529 undelfs_getindex (char *path
)
531 ext2_ino_t inode
= atol (path
);
534 for (i
= 0; i
< num_delarray
; i
++){
535 if (delarray
[i
].ino
== inode
)
542 do_stat (int inode_index
, struct stat
*buf
)
545 buf
->st_ino
= delarray
[inode_index
].ino
;
546 buf
->st_mode
= delarray
[inode_index
].mode
;
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
;
558 undelfs_lstat(vfs
*me
, char *path
, struct stat
*buf
)
563 undelfs_get_path (path
, &file
, &f
);
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 */
579 if (!ext2_fname
|| strcmp (ext2_fname
, file
)){
580 message_1s (1, undelfserr
, _(" You have to chdir to extract files first "));
585 inode_index
= undelfs_getindex (f
);
589 if (inode_index
== -1)
592 return do_stat (inode_index
, buf
);
596 undelfs_stat(vfs
*me
, char *path
, struct stat
*buf
)
598 return undelfs_lstat (me
, path
, buf
);
603 undelfs_fstat (void *vfs_info
, struct stat
*buf
)
605 undelfs_file
*p
= vfs_info
;
607 return do_stat (p
->f_index
, buf
);
611 undelfs_chdir(vfs
*me
, char *path
)
616 undelfs_get_path (path
, &file
, &f
);
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
);
635 /* this has to stay here for now: vfs layer does not know how to emulate it */
637 undelfs_lseek(void *vfs_info
, off_t offset
, int whence
)
643 undelfs_getid(vfs
*me
, char *path
, struct vfs_stamping
**parent
)
645 char *ext2_fname
, *file
;
647 /* We run only on the local fs */
649 undelfs_get_path (path
, &ext2_fname
, &file
);
659 undelfs_nothingisopen(vfsid id
)
661 return !undelfs_usage
;
665 undelfs_free(vfsid id
)
672 undelfs_init(vfs
*me
) {
673 undelfserr
= _(undelfserr
);
677 #define undelfs_init NULL
680 vfs vfs_undelfs_ops
= {
681 NULL
, /* This is place of next pointer */
684 "undel:", /* prefix */
689 NULL
, /* fill_names */
723 undelfs_nothingisopen
,
726 NULL
, /* get_local_copy */