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