dir_iterate.c revision ab13b5a9795a8c20f1d6da8fe1da340f545ec0e0
14b6829f0d28990dd645e16386eb226d0f10c8731shiqian/* 24b6829f0d28990dd645e16386eb226d0f10c8731shiqian * dir_iterate.c --- ext2fs directory iteration operations 34b6829f0d28990dd645e16386eb226d0f10c8731shiqian * 44b6829f0d28990dd645e16386eb226d0f10c8731shiqian * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. 54b6829f0d28990dd645e16386eb226d0f10c8731shiqian * 64b6829f0d28990dd645e16386eb226d0f10c8731shiqian * %Begin-Header% 74b6829f0d28990dd645e16386eb226d0f10c8731shiqian * This file may be redistributed under the terms of the GNU Library 84b6829f0d28990dd645e16386eb226d0f10c8731shiqian * General Public License, version 2. 94b6829f0d28990dd645e16386eb226d0f10c8731shiqian * %End-Header% 104b6829f0d28990dd645e16386eb226d0f10c8731shiqian */ 114b6829f0d28990dd645e16386eb226d0f10c8731shiqian 124b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include <stdio.h> 134b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include <string.h> 144b6829f0d28990dd645e16386eb226d0f10c8731shiqian#if HAVE_UNISTD_H 154b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include <unistd.h> 164b6829f0d28990dd645e16386eb226d0f10c8731shiqian#endif 174b6829f0d28990dd645e16386eb226d0f10c8731shiqian#if HAVE_ERRNO_H 184b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include <errno.h> 194b6829f0d28990dd645e16386eb226d0f10c8731shiqian#endif 204b6829f0d28990dd645e16386eb226d0f10c8731shiqian 214b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include "ext2_fs.h" 224b6829f0d28990dd645e16386eb226d0f10c8731shiqian#include "ext2fsP.h" 234b6829f0d28990dd645e16386eb226d0f10c8731shiqian 244b6829f0d28990dd645e16386eb226d0f10c8731shiqian#define EXT4_MAX_REC_LEN ((1<<16)-1) 254b6829f0d28990dd645e16386eb226d0f10c8731shiqian 264b6829f0d28990dd645e16386eb226d0f10c8731shiqianerrcode_t ext2fs_get_rec_len(ext2_filsys fs, 274b6829f0d28990dd645e16386eb226d0f10c8731shiqian struct ext2_dir_entry *dirent, 284b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int *rec_len) 294b6829f0d28990dd645e16386eb226d0f10c8731shiqian{ 304b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int len = dirent->rec_len; 314b6829f0d28990dd645e16386eb226d0f10c8731shiqian 324b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (len == EXT4_MAX_REC_LEN || len == 0) 334b6829f0d28990dd645e16386eb226d0f10c8731shiqian *rec_len = fs->blocksize; 344b6829f0d28990dd645e16386eb226d0f10c8731shiqian else 354b6829f0d28990dd645e16386eb226d0f10c8731shiqian *rec_len = (len & 65532) | ((len & 3) << 16); 364b6829f0d28990dd645e16386eb226d0f10c8731shiqian return 0; 374b6829f0d28990dd645e16386eb226d0f10c8731shiqian} 384b6829f0d28990dd645e16386eb226d0f10c8731shiqian 394b6829f0d28990dd645e16386eb226d0f10c8731shiqianerrcode_t ext2fs_set_rec_len(ext2_filsys fs, 404b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int len, 412620c79810d4741922e9fa89050c0af564994f24zhanyong.wan struct ext2_dir_entry *dirent) 424b6829f0d28990dd645e16386eb226d0f10c8731shiqian{ 43f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) 44f6d087b78d230d875bf5d8281112662795044680zhanyong.wan return EINVAL; 45a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan if (len < 65536) { 46fff033497b70e96a5dcadb6ba9570c12b5921d74zhanyong.wan dirent->rec_len = len; 47a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan return 0; 48a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan } 49f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan if (len == fs->blocksize) { 50a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan if (fs->blocksize == 65536) 514b6829f0d28990dd645e16386eb226d0f10c8731shiqian dirent->rec_len = EXT4_MAX_REC_LEN; 524b6829f0d28990dd645e16386eb226d0f10c8731shiqian else 534b6829f0d28990dd645e16386eb226d0f10c8731shiqian dirent->rec_len = 0; 544b6829f0d28990dd645e16386eb226d0f10c8731shiqian } else 554b6829f0d28990dd645e16386eb226d0f10c8731shiqian dirent->rec_len = (len & 65532) | ((len >> 16) & 3); 564b6829f0d28990dd645e16386eb226d0f10c8731shiqian return 0; 574b6829f0d28990dd645e16386eb226d0f10c8731shiqian} 58a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan 594b6829f0d28990dd645e16386eb226d0f10c8731shiqian/* 604b6829f0d28990dd645e16386eb226d0f10c8731shiqian * This function checks to see whether or not a potential deleted 614b6829f0d28990dd645e16386eb226d0f10c8731shiqian * directory entry looks valid. What we do is check the deleted entry 624b6829f0d28990dd645e16386eb226d0f10c8731shiqian * and each successive entry to make sure that they all look valid and 634b6829f0d28990dd645e16386eb226d0f10c8731shiqian * that the last deleted entry ends at the beginning of the next 644b6829f0d28990dd645e16386eb226d0f10c8731shiqian * undeleted entry. Returns 1 if the deleted entry looks valid, zero 65a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan * if not valid. 664b6829f0d28990dd645e16386eb226d0f10c8731shiqian */ 674b6829f0d28990dd645e16386eb226d0f10c8731shiqianstatic int ext2fs_validate_entry(ext2_filsys fs, char *buf, 684b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int offset, 694b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int final_offset) 704b6829f0d28990dd645e16386eb226d0f10c8731shiqian{ 714b6829f0d28990dd645e16386eb226d0f10c8731shiqian struct ext2_dir_entry *dirent; 724b6829f0d28990dd645e16386eb226d0f10c8731shiqian unsigned int rec_len; 734b6829f0d28990dd645e16386eb226d0f10c8731shiqian#define DIRENT_MIN_LENGTH 12 744b6829f0d28990dd645e16386eb226d0f10c8731shiqian 754b6829f0d28990dd645e16386eb226d0f10c8731shiqian while ((offset < final_offset) && 764b6829f0d28990dd645e16386eb226d0f10c8731shiqian (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { 774b6829f0d28990dd645e16386eb226d0f10c8731shiqian dirent = (struct ext2_dir_entry *)(buf + offset); 784b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (ext2fs_get_rec_len(fs, dirent, &rec_len)) 794b6829f0d28990dd645e16386eb226d0f10c8731shiqian return 0; 804b6829f0d28990dd645e16386eb226d0f10c8731shiqian offset += rec_len; 814b6829f0d28990dd645e16386eb226d0f10c8731shiqian if ((rec_len < 8) || 824b6829f0d28990dd645e16386eb226d0f10c8731shiqian ((rec_len % 4) != 0) || 834b6829f0d28990dd645e16386eb226d0f10c8731shiqian ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) 844b6829f0d28990dd645e16386eb226d0f10c8731shiqian return 0; 854b6829f0d28990dd645e16386eb226d0f10c8731shiqian } 86f6d087b78d230d875bf5d8281112662795044680zhanyong.wan return (offset == final_offset); 87f6d087b78d230d875bf5d8281112662795044680zhanyong.wan} 88f6d087b78d230d875bf5d8281112662795044680zhanyong.wan 89f6d087b78d230d875bf5d8281112662795044680zhanyong.wanerrcode_t ext2fs_dir_iterate2(ext2_filsys fs, 90f6d087b78d230d875bf5d8281112662795044680zhanyong.wan ext2_ino_t dir, 91f6d087b78d230d875bf5d8281112662795044680zhanyong.wan int flags, 92f6d087b78d230d875bf5d8281112662795044680zhanyong.wan char *block_buf, 93f6d087b78d230d875bf5d8281112662795044680zhanyong.wan int (*func)(ext2_ino_t dir, 94f6d087b78d230d875bf5d8281112662795044680zhanyong.wan int entry, 95f6d087b78d230d875bf5d8281112662795044680zhanyong.wan struct ext2_dir_entry *dirent, 96f6d087b78d230d875bf5d8281112662795044680zhanyong.wan int offset, 97a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan int blocksize, 98dfbdf0bab51520595679a99710983daba6fc18ebvladlosev char *buf, 99dfbdf0bab51520595679a99710983daba6fc18ebvladlosev void *priv_data), 100dfbdf0bab51520595679a99710983daba6fc18ebvladlosev void *priv_data) 1014b6829f0d28990dd645e16386eb226d0f10c8731shiqian{ 1024b6829f0d28990dd645e16386eb226d0f10c8731shiqian struct dir_context ctx; 1034b6829f0d28990dd645e16386eb226d0f10c8731shiqian errcode_t retval; 1044b6829f0d28990dd645e16386eb226d0f10c8731shiqian 1054b6829f0d28990dd645e16386eb226d0f10c8731shiqian EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 1064b6829f0d28990dd645e16386eb226d0f10c8731shiqian 1074b6829f0d28990dd645e16386eb226d0f10c8731shiqian retval = ext2fs_check_directory(fs, dir); 1084b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (retval) 1094b6829f0d28990dd645e16386eb226d0f10c8731shiqian return retval; 1104b6829f0d28990dd645e16386eb226d0f10c8731shiqian 1114b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.dir = dir; 1124b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.flags = flags; 1134b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (block_buf) 1144b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.buf = block_buf; 1154b6829f0d28990dd645e16386eb226d0f10c8731shiqian else { 1164b6829f0d28990dd645e16386eb226d0f10c8731shiqian retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); 1174b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (retval) 1184b6829f0d28990dd645e16386eb226d0f10c8731shiqian return retval; 1194b6829f0d28990dd645e16386eb226d0f10c8731shiqian } 1204b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.func = func; 1214b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.priv_data = priv_data; 1224b6829f0d28990dd645e16386eb226d0f10c8731shiqian ctx.errcode = 0; 1234b6829f0d28990dd645e16386eb226d0f10c8731shiqian retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, 1244b6829f0d28990dd645e16386eb226d0f10c8731shiqian ext2fs_process_dir_block, &ctx); 1254b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (!block_buf) 126dfbdf0bab51520595679a99710983daba6fc18ebvladlosev ext2fs_free_mem(&ctx.buf); 1274b6829f0d28990dd645e16386eb226d0f10c8731shiqian if (retval) 1284b6829f0d28990dd645e16386eb226d0f10c8731shiqian return retval; 1294b6829f0d28990dd645e16386eb226d0f10c8731shiqian return ctx.errcode; 130dfbdf0bab51520595679a99710983daba6fc18ebvladlosev} 131dfbdf0bab51520595679a99710983daba6fc18ebvladlosev 1324b6829f0d28990dd645e16386eb226d0f10c8731shiqianstruct xlate { 1334b6829f0d28990dd645e16386eb226d0f10c8731shiqian int (*func)(struct ext2_dir_entry *dirent, 1344b6829f0d28990dd645e16386eb226d0f10c8731shiqian int offset, 1354b6829f0d28990dd645e16386eb226d0f10c8731shiqian int blocksize, 1364b6829f0d28990dd645e16386eb226d0f10c8731shiqian char *buf, 1374b6829f0d28990dd645e16386eb226d0f10c8731shiqian void *priv_data); 1384b6829f0d28990dd645e16386eb226d0f10c8731shiqian void *real_private; 1394b6829f0d28990dd645e16386eb226d0f10c8731shiqian}; 1404b6829f0d28990dd645e16386eb226d0f10c8731shiqian 1414b6829f0d28990dd645e16386eb226d0f10c8731shiqianstatic int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), 142f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan int entry EXT2FS_ATTR((unused)), 143a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan struct ext2_dir_entry *dirent, int offset, 144a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan int blocksize, char *buf, void *priv_data) 145a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan{ 146a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan struct xlate *xl = (struct xlate *) priv_data; 147a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan 148a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); 149a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan} 15095a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev 151a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wanextern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 152a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan ext2_ino_t dir, 153a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan int flags, 15495a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev char *block_buf, 155a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan int (*func)(struct ext2_dir_entry *dirent, 156a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan int offset, 15795a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev int blocksize, 158a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan char *buf, 15995a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev void *priv_data), 160a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan void *priv_data) 161a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan{ 162a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan struct xlate xl; 163a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan 164a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan xl.real_private = priv_data; 165a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan xl.func = func; 16695a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev 167a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan return ext2fs_dir_iterate2(fs, dir, flags, block_buf, 168a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan xlate_func, &xl); 169a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan} 17095a77adfeb8f97d2a61956e2eda12afe7371f1dbvladlosev 171a33163a3ddbb60a6c45340e436310f78044c1a7dzhanyong.wan 172f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan/* 173f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan * Helper function which is private to this module. Used by 174f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() 175f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan */ 176f6d087b78d230d875bf5d8281112662795044680zhanyong.wanint ext2fs_process_dir_block(ext2_filsys fs, 177f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan blk64_t *blocknr, 178f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan e2_blkcnt_t blockcnt, 179dfbdf0bab51520595679a99710983daba6fc18ebvladlosev blk64_t ref_block EXT2FS_ATTR((unused)), 180f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan int ref_offset EXT2FS_ATTR((unused)), 181f39160b423e8f90902066cf6774e4180667dcbeezhanyong.wan 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