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) 75 { 76 struct lsdel_struct lsd; 77 struct deleted_info *delarray; 78 int num_delarray, max_delarray; 79 ext2_inode_scan scan = 0; 80 ext2_ino_t ino; 81 struct ext2_inode inode; 82 errcode_t retval; 83 char *block_buf; 84 int i; 85 long secs = 0; 86 char *tmp; 87 time_t now; 88 FILE *out; 89 90 if (common_args_process(argc, argv, 1, 2, "list_deleted_inodes", 91 "[secs]", 0)) 92 return; 93 94 if (argc > 1) { 95 secs = strtol(argv[1],&tmp,0); 96 if (*tmp) { 97 com_err(argv[0], 0, "Bad time - %s",argv[1]); 98 return; 99 } 100 } 101 102 now = current_fs->now ? current_fs->now : time(0); 103 max_delarray = 100; 104 num_delarray = 0; 105 delarray = malloc(max_delarray * sizeof(struct deleted_info)); 106 if (!delarray) { 107 com_err("ls_deleted_inodes", ENOMEM, 108 "while allocating deleted information storage"); 109 exit(1); 110 } 111 112 block_buf = malloc(current_fs->blocksize * 3); 113 if (!block_buf) { 114 com_err("ls_deleted_inodes", ENOMEM, "while allocating block buffer"); 115 goto error_out; 116 } 117 118 retval = ext2fs_open_inode_scan(current_fs, 0, &scan); 119 if (retval) { 120 com_err("ls_deleted_inodes", retval, 121 "while opening inode scan"); 122 goto error_out; 123 } 124 125 do { 126 retval = ext2fs_get_next_inode(scan, &ino, &inode); 127 } while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE); 128 if (retval) { 129 com_err("ls_deleted_inodes", retval, 130 "while starting inode scan"); 131 goto error_out; 132 } 133 134 while (ino) { 135 if ((inode.i_dtime == 0) || 136 (secs && (labs(now - secs) > (long) inode.i_dtime))) 137 goto next; 138 139 lsd.inode = ino; 140 lsd.num_blocks = 0; 141 lsd.free_blocks = 0; 142 lsd.bad_blocks = 0; 143 144 if (ext2fs_inode_has_valid_blocks2(current_fs, &inode)) { 145 retval = ext2fs_block_iterate3(current_fs, ino, 146 BLOCK_FLAG_READ_ONLY, 147 block_buf, 148 lsdel_proc, &lsd); 149 if (retval) { 150 com_err("ls_deleted_inodes", retval, 151 "while calling ext2fs_block_iterate2"); 152 goto next; 153 } 154 } 155 if ((lsd.free_blocks && !lsd.bad_blocks) || 156 inode.i_flags & EXT4_INLINE_DATA_FL) { 157 if (num_delarray >= max_delarray) { 158 max_delarray += 50; 159 delarray = realloc(delarray, 160 max_delarray * sizeof(struct deleted_info)); 161 if (!delarray) { 162 com_err("ls_deleted_inodes", 163 ENOMEM, 164 "while reallocating array"); 165 exit(1); 166 } 167 } 168 169 delarray[num_delarray].ino = ino; 170 delarray[num_delarray].mode = inode.i_mode; 171 delarray[num_delarray].uid = inode_uid(inode); 172 delarray[num_delarray].size = EXT2_I_SIZE(&inode); 173 delarray[num_delarray].dtime = (__s32) inode.i_dtime; 174 delarray[num_delarray].num_blocks = lsd.num_blocks; 175 delarray[num_delarray].free_blocks = lsd.free_blocks; 176 num_delarray++; 177 } 178 179 next: 180 do { 181 retval = ext2fs_get_next_inode(scan, &ino, &inode); 182 } while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE); 183 if (retval) { 184 com_err("ls_deleted_inodes", retval, 185 "while doing inode scan"); 186 goto error_out; 187 } 188 } 189 190 out = open_pager(); 191 192 fprintf(out, " Inode Owner Mode Size Blocks Time deleted\n"); 193 194 qsort(delarray, num_delarray, sizeof(struct deleted_info), 195 deleted_info_compare); 196 197 for (i = 0; i < num_delarray; i++) { 198 fprintf(out, "%6u %6d %6o %6llu %6lld/%6lld %s", 199 delarray[i].ino, 200 delarray[i].uid, delarray[i].mode, delarray[i].size, 201 delarray[i].free_blocks, delarray[i].num_blocks, 202 time_to_string(delarray[i].dtime)); 203 } 204 fprintf(out, "%d deleted inodes found.\n", num_delarray); 205 close_pager(out); 206 207 error_out: 208 free(block_buf); 209 free(delarray); 210 if (scan) 211 ext2fs_close_inode_scan(scan); 212 return; 213 } 214 215 216 217