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