1 /* 2 * lsdel.c --- routines to try to help a user recover a deleted file. 3 * 4 * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 5 * Theodore Ts'o. This file may be redistributed under the terms of 6 * the GNU Public License. 7 */ 8 9 #include "config.h" 10 #include <stdio.h> 11 #include <unistd.h> 12 #include <stdlib.h> 13 #include <ctype.h> 14 #include <string.h> 15 #include <time.h> 16 #ifdef HAVE_ERRNO_H 17 #include <errno.h> 18 #endif 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 22 #include "debugfs.h" 23 24 struct deleted_info { 25 ext2_ino_t ino; 26 unsigned short mode; 27 __u32 uid; 28 __u64 size; 29 time_t dtime; 30 e2_blkcnt_t num_blocks; 31 e2_blkcnt_t free_blocks; 32 }; 33 34 struct lsdel_struct { 35 ext2_ino_t inode; 36 e2_blkcnt_t num_blocks; 37 e2_blkcnt_t free_blocks; 38 e2_blkcnt_t bad_blocks; 39 }; 40 41 static int deleted_info_compare(const void *a, const void *b) 42 { 43 const struct deleted_info *arg1, *arg2; 44 45 arg1 = (const struct deleted_info *) a; 46 arg2 = (const struct deleted_info *) b; 47 48 return arg1->dtime - arg2->dtime; 49 } 50 51 static int lsdel_proc(ext2_filsys fs, 52 blk64_t *block_nr, 53 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 54 blk64_t ref_block EXT2FS_ATTR((unused)), 55 int ref_offset EXT2FS_ATTR((unused)), 56 void *private) 57 { 58 struct lsdel_struct *lsd = (struct lsdel_struct *) private; 59 60 lsd->num_blocks++; 61 62 if (*block_nr < fs->super->s_first_data_block || 63 *block_nr >= ext2fs_blocks_count(fs->super)) { 64 lsd->bad_blocks++; 65 return BLOCK_ABORT; 66 } 67 68 if (!ext2fs_test_block_bitmap2(fs->block_map,*block_nr)) 69 lsd->free_blocks++; 70 71 return 0; 72 } 73 74 void do_lsdel(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 75 void *infop EXT2FS_ATTR((unused))) 76 { 77 struct lsdel_struct lsd; 78 struct deleted_info *delarray; 79 int num_delarray, max_delarray; 80 ext2_inode_scan scan = 0; 81 ext2_ino_t ino; 82 struct ext2_inode inode; 83 errcode_t retval; 84 char *block_buf; 85 int i; 86 long secs = 0; 87 char *tmp; 88 time_t now; 89 FILE *out; 90 91 if (common_args_process(argc, argv, 1, 2, "list_deleted_inodes", 92 "[secs]", 0)) 93 return; 94 95 if (argc > 1) { 96 secs = strtol(argv[1],&tmp,0); 97 if (*tmp) { 98 com_err(argv[0], 0, "Bad time - %s",argv[1]); 99 return; 100 } 101 } 102 103 now = current_fs->now ? current_fs->now : time(0); 104 max_delarray = 100; 105 num_delarray = 0; 106 delarray = malloc(max_delarray * sizeof(struct deleted_info)); 107 if (!delarray) { 108 com_err("ls_deleted_inodes", ENOMEM, 109 "while allocating deleted information storage"); 110 exit(1); 111 } 112 113 block_buf = malloc(current_fs->blocksize * 3); 114 if (!block_buf) { 115 com_err("ls_deleted_inodes", ENOMEM, "while allocating block buffer"); 116 goto error_out; 117 } 118 119 retval = ext2fs_open_inode_scan(current_fs, 0, &scan); 120 if (retval) { 121 com_err("ls_deleted_inodes", retval, 122 "while opening inode scan"); 123 goto error_out; 124 } 125 126 do { 127 retval = ext2fs_get_next_inode(scan, &ino, &inode); 128 } while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE); 129 if (retval) { 130 com_err("ls_deleted_inodes", retval, 131 "while starting inode scan"); 132 goto error_out; 133 } 134 135 while (ino) { 136 if ((inode.i_dtime == 0) || 137 (secs && (labs(now - secs) > (long) inode.i_dtime))) 138 goto next; 139 140 lsd.inode = ino; 141 lsd.num_blocks = 0; 142 lsd.free_blocks = 0; 143 lsd.bad_blocks = 0; 144 145 if (ext2fs_inode_has_valid_blocks2(current_fs, &inode)) { 146 retval = ext2fs_block_iterate3(current_fs, ino, 147 BLOCK_FLAG_READ_ONLY, 148 block_buf, 149 lsdel_proc, &lsd); 150 if (retval) { 151 com_err("ls_deleted_inodes", retval, 152 "while calling ext2fs_block_iterate2"); 153 goto next; 154 } 155 } 156 if ((lsd.free_blocks && !lsd.bad_blocks) || 157 inode.i_flags & EXT4_INLINE_DATA_FL) { 158 if (num_delarray >= max_delarray) { 159 max_delarray += 50; 160 delarray = realloc(delarray, 161 max_delarray * sizeof(struct deleted_info)); 162 if (!delarray) { 163 com_err("ls_deleted_inodes", 164 ENOMEM, 165 "while reallocating array"); 166 exit(1); 167 } 168 } 169 170 delarray[num_delarray].ino = ino; 171 delarray[num_delarray].mode = inode.i_mode; 172 delarray[num_delarray].uid = inode_uid(inode); 173 delarray[num_delarray].size = EXT2_I_SIZE(&inode); 174 delarray[num_delarray].dtime = (__s32) inode.i_dtime; 175 delarray[num_delarray].num_blocks = lsd.num_blocks; 176 delarray[num_delarray].free_blocks = lsd.free_blocks; 177 num_delarray++; 178 } 179 180 next: 181 do { 182 retval = ext2fs_get_next_inode(scan, &ino, &inode); 183 } while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE); 184 if (retval) { 185 com_err("ls_deleted_inodes", retval, 186 "while doing inode scan"); 187 goto error_out; 188 } 189 } 190 191 out = open_pager(); 192 193 fprintf(out, " Inode Owner Mode Size Blocks Time deleted\n"); 194 195 qsort(delarray, num_delarray, sizeof(struct deleted_info), 196 deleted_info_compare); 197 198 for (i = 0; i < num_delarray; i++) { 199 fprintf(out, "%6u %6d %6o %6llu %6lld/%6lld %s", 200 delarray[i].ino, 201 delarray[i].uid, delarray[i].mode, delarray[i].size, 202 delarray[i].free_blocks, delarray[i].num_blocks, 203 time_to_string(delarray[i].dtime)); 204 } 205 fprintf(out, "%d deleted inodes found.\n", num_delarray); 206 close_pager(out); 207 208 error_out: 209 free(block_buf); 210 free(delarray); 211 if (scan) 212 ext2fs_close_inode_scan(scan); 213 return; 214 } 215 216 217 218