dir_iterate.c revision 357d1863d64ce807c2904e101fc87d3f6be2f3ca
1/* 2 * dir_iterate.c --- ext2fs directory iteration operations 3 * 4 * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Public 8 * License. 9 * %End-Header% 10 */ 11 12#include <stdio.h> 13#include <string.h> 14#if HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17#if HAVE_ERRNO_H 18#include <errno.h> 19#endif 20 21#include "ext2_fs.h" 22#include "ext2fsP.h" 23 24/* 25 * This function checks to see whether or not a potential deleted 26 * directory entry looks valid. What we do is check the deleted entry 27 * and each successive entry to make sure that they all look valid and 28 * that the last deleted entry ends at the beginning of the next 29 * undeleted entry. Returns 1 if the deleted entry looks valid, zero 30 * if not valid. 31 */ 32static int ext2fs_validate_entry(char *buf, int offset, int final_offset) 33{ 34 struct ext2_dir_entry *dirent; 35 36 while (offset < final_offset) { 37 dirent = (struct ext2_dir_entry *)(buf + offset); 38 offset += dirent->rec_len; 39 if ((dirent->rec_len < 8) || 40 ((dirent->rec_len % 4) != 0) || 41 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) 42 return 0; 43 } 44 return (offset == final_offset); 45} 46 47errcode_t ext2fs_dir_iterate2(ext2_filsys fs, 48 ext2_ino_t dir, 49 int flags, 50 char *block_buf, 51 int (*func)(ext2_ino_t dir, 52 int entry, 53 struct ext2_dir_entry *dirent, 54 int offset, 55 int blocksize, 56 char *buf, 57 void *priv_data), 58 void *priv_data) 59{ 60 struct dir_context ctx; 61 errcode_t retval; 62 63 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 64 65 retval = ext2fs_check_directory(fs, dir); 66 if (retval) 67 return retval; 68 69 ctx.dir = dir; 70 ctx.flags = flags; 71 if (block_buf) 72 ctx.buf = block_buf; 73 else { 74 retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); 75 if (retval) 76 return retval; 77 } 78 ctx.func = func; 79 ctx.priv_data = priv_data; 80 ctx.errcode = 0; 81 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_READ_ONLY, 0, 82 ext2fs_process_dir_block, &ctx); 83 if (!block_buf) 84 ext2fs_free_mem(&ctx.buf); 85 if (retval) 86 return retval; 87 return ctx.errcode; 88} 89 90struct xlate { 91 int (*func)(struct ext2_dir_entry *dirent, 92 int offset, 93 int blocksize, 94 char *buf, 95 void *priv_data); 96 void *real_private; 97}; 98 99static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), 100 int entry EXT2FS_ATTR((unused)), 101 struct ext2_dir_entry *dirent, int offset, 102 int blocksize, char *buf, void *priv_data) 103{ 104 struct xlate *xl = (struct xlate *) priv_data; 105 106 return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); 107} 108 109extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 110 ext2_ino_t dir, 111 int flags, 112 char *block_buf, 113 int (*func)(struct ext2_dir_entry *dirent, 114 int offset, 115 int blocksize, 116 char *buf, 117 void *priv_data), 118 void *priv_data) 119{ 120 struct xlate xl; 121 122 xl.real_private = priv_data; 123 xl.func = func; 124 125 return ext2fs_dir_iterate2(fs, dir, flags, block_buf, 126 xlate_func, &xl); 127} 128 129 130/* 131 * Helper function which is private to this module. Used by 132 * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() 133 */ 134int ext2fs_process_dir_block(ext2_filsys fs, 135 blk_t *blocknr, 136 e2_blkcnt_t blockcnt, 137 blk_t ref_block EXT2FS_ATTR((unused)), 138 int ref_offset EXT2FS_ATTR((unused)), 139 void *priv_data) 140{ 141 struct dir_context *ctx = (struct dir_context *) priv_data; 142 unsigned int offset = 0; 143 unsigned int next_real_entry = 0; 144 int ret = 0; 145 int changed = 0; 146 int do_abort = 0; 147 int entry, size; 148 struct ext2_dir_entry *dirent; 149 150 if (blockcnt < 0) 151 return 0; 152 153 entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; 154 155 ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); 156 if (ctx->errcode) 157 return BLOCK_ABORT; 158 159 while (offset < fs->blocksize) { 160 dirent = (struct ext2_dir_entry *) (ctx->buf + offset); 161 if (((offset + dirent->rec_len) > fs->blocksize) || 162 (dirent->rec_len < 8) || 163 ((dirent->rec_len % 4) != 0) || 164 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { 165 ctx->errcode = EXT2_ET_DIR_CORRUPTED; 166 return BLOCK_ABORT; 167 } 168 if (!dirent->inode && 169 !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) 170 goto next; 171 172 ret = (ctx->func)(ctx->dir, 173 (next_real_entry > offset) ? 174 DIRENT_DELETED_FILE : entry, 175 dirent, offset, 176 fs->blocksize, ctx->buf, 177 ctx->priv_data); 178 if (entry < DIRENT_OTHER_FILE) 179 entry++; 180 181 if (ret & DIRENT_CHANGED) 182 changed++; 183 if (ret & DIRENT_ABORT) { 184 do_abort++; 185 break; 186 } 187next: 188 if (next_real_entry == offset) 189 next_real_entry += dirent->rec_len; 190 191 if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { 192 size = ((dirent->name_len & 0xFF) + 11) & ~3; 193 194 if (dirent->rec_len != size) { 195 unsigned int final_offset; 196 197 final_offset = offset + dirent->rec_len; 198 offset += size; 199 while (offset < final_offset && 200 !ext2fs_validate_entry(ctx->buf, 201 offset, 202 final_offset)) 203 offset += 4; 204 continue; 205 } 206 } 207 offset += dirent->rec_len; 208 } 209 210 if (changed) { 211 ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); 212 if (ctx->errcode) 213 return BLOCK_ABORT; 214 } 215 if (do_abort) 216 return BLOCK_ABORT; 217 return 0; 218} 219 220