Home | History | Annotate | Download | only in ext2fs
      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  */
     32 static 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 
     47 errcode_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, 0, 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 
     90 struct 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 
     99 static 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 
    109 extern 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  */
    134 int 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 		}
    187 next:
    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