dir_iterate.c revision ab13b5a9795a8c20f1d6da8fe1da340f545ec0e0
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 Library 8 * General Public License, version 2. 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#define EXT4_MAX_REC_LEN ((1<<16)-1) 25 26errcode_t ext2fs_get_rec_len(ext2_filsys fs, 27 struct ext2_dir_entry *dirent, 28 unsigned int *rec_len) 29{ 30 unsigned int len = dirent->rec_len; 31 32 if (len == EXT4_MAX_REC_LEN || len == 0) 33 *rec_len = fs->blocksize; 34 else 35 *rec_len = (len & 65532) | ((len & 3) << 16); 36 return 0; 37} 38 39errcode_t ext2fs_set_rec_len(ext2_filsys fs, 40 unsigned int len, 41 struct ext2_dir_entry *dirent) 42{ 43 if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) 44 return EINVAL; 45 if (len < 65536) { 46 dirent->rec_len = len; 47 return 0; 48 } 49 if (len == fs->blocksize) { 50 if (fs->blocksize == 65536) 51 dirent->rec_len = EXT4_MAX_REC_LEN; 52 else 53 dirent->rec_len = 0; 54 } else 55 dirent->rec_len = (len & 65532) | ((len >> 16) & 3); 56 return 0; 57} 58 59/* 60 * This function checks to see whether or not a potential deleted 61 * directory entry looks valid. What we do is check the deleted entry 62 * and each successive entry to make sure that they all look valid and 63 * that the last deleted entry ends at the beginning of the next 64 * undeleted entry. Returns 1 if the deleted entry looks valid, zero 65 * if not valid. 66 */ 67static int ext2fs_validate_entry(ext2_filsys fs, char *buf, 68 unsigned int offset, 69 unsigned int final_offset) 70{ 71 struct ext2_dir_entry *dirent; 72 unsigned int rec_len; 73#define DIRENT_MIN_LENGTH 12 74 75 while ((offset < final_offset) && 76 (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { 77 dirent = (struct ext2_dir_entry *)(buf + offset); 78 if (ext2fs_get_rec_len(fs, dirent, &rec_len)) 79 return 0; 80 offset += rec_len; 81 if ((rec_len < 8) || 82 ((rec_len % 4) != 0) || 83 ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) 84 return 0; 85 } 86 return (offset == final_offset); 87} 88 89errcode_t ext2fs_dir_iterate2(ext2_filsys fs, 90 ext2_ino_t dir, 91 int flags, 92 char *block_buf, 93 int (*func)(ext2_ino_t dir, 94 int entry, 95 struct ext2_dir_entry *dirent, 96 int offset, 97 int blocksize, 98 char *buf, 99 void *priv_data), 100 void *priv_data) 101{ 102 struct dir_context ctx; 103 errcode_t retval; 104 105 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 106 107 retval = ext2fs_check_directory(fs, dir); 108 if (retval) 109 return retval; 110 111 ctx.dir = dir; 112 ctx.flags = flags; 113 if (block_buf) 114 ctx.buf = block_buf; 115 else { 116 retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); 117 if (retval) 118 return retval; 119 } 120 ctx.func = func; 121 ctx.priv_data = priv_data; 122 ctx.errcode = 0; 123 retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, 124 ext2fs_process_dir_block, &ctx); 125 if (!block_buf) 126 ext2fs_free_mem(&ctx.buf); 127 if (retval) 128 return retval; 129 return ctx.errcode; 130} 131 132struct xlate { 133 int (*func)(struct ext2_dir_entry *dirent, 134 int offset, 135 int blocksize, 136 char *buf, 137 void *priv_data); 138 void *real_private; 139}; 140 141static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), 142 int entry EXT2FS_ATTR((unused)), 143 struct ext2_dir_entry *dirent, int offset, 144 int blocksize, char *buf, void *priv_data) 145{ 146 struct xlate *xl = (struct xlate *) priv_data; 147 148 return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); 149} 150 151extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 152 ext2_ino_t dir, 153 int flags, 154 char *block_buf, 155 int (*func)(struct ext2_dir_entry *dirent, 156 int offset, 157 int blocksize, 158 char *buf, 159 void *priv_data), 160 void *priv_data) 161{ 162 struct xlate xl; 163 164 xl.real_private = priv_data; 165 xl.func = func; 166 167 return ext2fs_dir_iterate2(fs, dir, flags, block_buf, 168 xlate_func, &xl); 169} 170 171 172/* 173 * Helper function which is private to this module. Used by 174 * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() 175 */ 176int ext2fs_process_dir_block(ext2_filsys fs, 177 blk64_t *blocknr, 178 e2_blkcnt_t blockcnt, 179 blk64_t ref_block EXT2FS_ATTR((unused)), 180 int ref_offset EXT2FS_ATTR((unused)), 181 void *priv_data) 182{ 183 struct dir_context *ctx = (struct dir_context *) priv_data; 184 unsigned int offset = 0; 185 unsigned int next_real_entry = 0; 186 int ret = 0; 187 int changed = 0; 188 int do_abort = 0; 189 unsigned int rec_len, size; 190 int entry; 191 struct ext2_dir_entry *dirent; 192 193 if (blockcnt < 0) 194 return 0; 195 196 entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; 197 198 ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0); 199 if (ctx->errcode) 200 return BLOCK_ABORT; 201 202 while (offset < fs->blocksize) { 203 dirent = (struct ext2_dir_entry *) (ctx->buf + offset); 204 if (ext2fs_get_rec_len(fs, dirent, &rec_len)) 205 return BLOCK_ABORT; 206 if (((offset + rec_len) > fs->blocksize) || 207 (rec_len < 8) || 208 ((rec_len % 4) != 0) || 209 ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { 210 ctx->errcode = EXT2_ET_DIR_CORRUPTED; 211 return BLOCK_ABORT; 212 } 213 if (!dirent->inode && 214 !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) 215 goto next; 216 217 ret = (ctx->func)(ctx->dir, 218 (next_real_entry > offset) ? 219 DIRENT_DELETED_FILE : entry, 220 dirent, offset, 221 fs->blocksize, ctx->buf, 222 ctx->priv_data); 223 if (entry < DIRENT_OTHER_FILE) 224 entry++; 225 226 if (ret & DIRENT_CHANGED) { 227 if (ext2fs_get_rec_len(fs, dirent, &rec_len)) 228 return BLOCK_ABORT; 229 changed++; 230 } 231 if (ret & DIRENT_ABORT) { 232 do_abort++; 233 break; 234 } 235next: 236 if (next_real_entry == offset) 237 next_real_entry += rec_len; 238 239 if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { 240 size = ((dirent->name_len & 0xFF) + 11) & ~3; 241 242 if (rec_len != size) { 243 unsigned int final_offset; 244 245 final_offset = offset + rec_len; 246 offset += size; 247 while (offset < final_offset && 248 !ext2fs_validate_entry(fs, ctx->buf, 249 offset, 250 final_offset)) 251 offset += 4; 252 continue; 253 } 254 } 255 offset += rec_len; 256 } 257 258 if (changed) { 259 ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf, 260 0); 261 if (ctx->errcode) 262 return BLOCK_ABORT; 263 } 264 if (do_abort) 265 return BLOCK_ABORT; 266 return 0; 267} 268 269