Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  linux/fs/ext4/dir.c
0004  *
0005  * Copyright (C) 1992, 1993, 1994, 1995
0006  * Remy Card (card@masi.ibp.fr)
0007  * Laboratoire MASI - Institut Blaise Pascal
0008  * Universite Pierre et Marie Curie (Paris VI)
0009  *
0010  *  from
0011  *
0012  *  linux/fs/minix/dir.c
0013  *
0014  *  Copyright (C) 1991, 1992  Linus Torvalds
0015  *
0016  *  ext4 directory handling functions
0017  *
0018  *  Big-endian to little-endian byte-swapping/bitmaps by
0019  *        David S. Miller (davem@caip.rutgers.edu), 1995
0020  *
0021  * Hash Tree Directory indexing (c) 2001  Daniel Phillips
0022  *
0023  */
0024 
0025 #include <linux/fs.h>
0026 #include <linux/buffer_head.h>
0027 #include <linux/slab.h>
0028 #include <linux/iversion.h>
0029 #include <linux/unicode.h>
0030 #include "ext4.h"
0031 #include "xattr.h"
0032 
0033 static int ext4_dx_readdir(struct file *, struct dir_context *);
0034 
0035 /**
0036  * is_dx_dir() - check if a directory is using htree indexing
0037  * @inode: directory inode
0038  *
0039  * Check if the given dir-inode refers to an htree-indexed directory
0040  * (or a directory which could potentially get converted to use htree
0041  * indexing).
0042  *
0043  * Return 1 if it is a dx dir, 0 if not
0044  */
0045 static int is_dx_dir(struct inode *inode)
0046 {
0047     struct super_block *sb = inode->i_sb;
0048 
0049     if (ext4_has_feature_dir_index(inode->i_sb) &&
0050         ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) ||
0051          ((inode->i_size >> sb->s_blocksize_bits) == 1) ||
0052          ext4_has_inline_data(inode)))
0053         return 1;
0054 
0055     return 0;
0056 }
0057 
0058 static bool is_fake_dir_entry(struct ext4_dir_entry_2 *de)
0059 {
0060     /* Check if . or .. , or skip if namelen is 0 */
0061     if ((de->name_len > 0) && (de->name_len <= 2) && (de->name[0] == '.') &&
0062         (de->name[1] == '.' || de->name[1] == '\0'))
0063         return true;
0064     /* Check if this is a csum entry */
0065     if (de->file_type == EXT4_FT_DIR_CSUM)
0066         return true;
0067     return false;
0068 }
0069 
0070 /*
0071  * Return 0 if the directory entry is OK, and 1 if there is a problem
0072  *
0073  * Note: this is the opposite of what ext2 and ext3 historically returned...
0074  *
0075  * bh passed here can be an inode block or a dir data block, depending
0076  * on the inode inline data flag.
0077  */
0078 int __ext4_check_dir_entry(const char *function, unsigned int line,
0079                struct inode *dir, struct file *filp,
0080                struct ext4_dir_entry_2 *de,
0081                struct buffer_head *bh, char *buf, int size,
0082                unsigned int offset)
0083 {
0084     const char *error_msg = NULL;
0085     const int rlen = ext4_rec_len_from_disk(de->rec_len,
0086                         dir->i_sb->s_blocksize);
0087     const int next_offset = ((char *) de - buf) + rlen;
0088     bool fake = is_fake_dir_entry(de);
0089     bool has_csum = ext4_has_metadata_csum(dir->i_sb);
0090 
0091     if (unlikely(rlen < ext4_dir_rec_len(1, fake ? NULL : dir)))
0092         error_msg = "rec_len is smaller than minimal";
0093     else if (unlikely(rlen % 4 != 0))
0094         error_msg = "rec_len % 4 != 0";
0095     else if (unlikely(rlen < ext4_dir_rec_len(de->name_len,
0096                             fake ? NULL : dir)))
0097         error_msg = "rec_len is too small for name_len";
0098     else if (unlikely(next_offset > size))
0099         error_msg = "directory entry overrun";
0100     else if (unlikely(next_offset > size - ext4_dir_rec_len(1,
0101                           has_csum ? NULL : dir) &&
0102               next_offset != size))
0103         error_msg = "directory entry too close to block end";
0104     else if (unlikely(le32_to_cpu(de->inode) >
0105             le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
0106         error_msg = "inode out of bounds";
0107     else
0108         return 0;
0109 
0110     if (filp)
0111         ext4_error_file(filp, function, line, bh->b_blocknr,
0112                 "bad entry in directory: %s - offset=%u, "
0113                 "inode=%u, rec_len=%d, size=%d fake=%d",
0114                 error_msg, offset, le32_to_cpu(de->inode),
0115                 rlen, size, fake);
0116     else
0117         ext4_error_inode(dir, function, line, bh->b_blocknr,
0118                 "bad entry in directory: %s - offset=%u, "
0119                 "inode=%u, rec_len=%d, size=%d fake=%d",
0120                  error_msg, offset, le32_to_cpu(de->inode),
0121                  rlen, size, fake);
0122 
0123     return 1;
0124 }
0125 
0126 static int ext4_readdir(struct file *file, struct dir_context *ctx)
0127 {
0128     unsigned int offset;
0129     int i;
0130     struct ext4_dir_entry_2 *de;
0131     int err;
0132     struct inode *inode = file_inode(file);
0133     struct super_block *sb = inode->i_sb;
0134     struct buffer_head *bh = NULL;
0135     struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
0136 
0137     err = fscrypt_prepare_readdir(inode);
0138     if (err)
0139         return err;
0140 
0141     if (is_dx_dir(inode)) {
0142         err = ext4_dx_readdir(file, ctx);
0143         if (err != ERR_BAD_DX_DIR)
0144             return err;
0145 
0146         /* Can we just clear INDEX flag to ignore htree information? */
0147         if (!ext4_has_metadata_csum(sb)) {
0148             /*
0149              * We don't set the inode dirty flag since it's not
0150              * critical that it gets flushed back to the disk.
0151              */
0152             ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
0153         }
0154     }
0155 
0156     if (ext4_has_inline_data(inode)) {
0157         int has_inline_data = 1;
0158         err = ext4_read_inline_dir(file, ctx,
0159                        &has_inline_data);
0160         if (has_inline_data)
0161             return err;
0162     }
0163 
0164     if (IS_ENCRYPTED(inode)) {
0165         err = fscrypt_fname_alloc_buffer(EXT4_NAME_LEN, &fstr);
0166         if (err < 0)
0167             return err;
0168     }
0169 
0170     while (ctx->pos < inode->i_size) {
0171         struct ext4_map_blocks map;
0172 
0173         if (fatal_signal_pending(current)) {
0174             err = -ERESTARTSYS;
0175             goto errout;
0176         }
0177         cond_resched();
0178         offset = ctx->pos & (sb->s_blocksize - 1);
0179         map.m_lblk = ctx->pos >> EXT4_BLOCK_SIZE_BITS(sb);
0180         map.m_len = 1;
0181         err = ext4_map_blocks(NULL, inode, &map, 0);
0182         if (err == 0) {
0183             /* m_len should never be zero but let's avoid
0184              * an infinite loop if it somehow is */
0185             if (map.m_len == 0)
0186                 map.m_len = 1;
0187             ctx->pos += map.m_len * sb->s_blocksize;
0188             continue;
0189         }
0190         if (err > 0) {
0191             pgoff_t index = map.m_pblk >>
0192                     (PAGE_SHIFT - inode->i_blkbits);
0193             if (!ra_has_index(&file->f_ra, index))
0194                 page_cache_sync_readahead(
0195                     sb->s_bdev->bd_inode->i_mapping,
0196                     &file->f_ra, file,
0197                     index, 1);
0198             file->f_ra.prev_pos = (loff_t)index << PAGE_SHIFT;
0199             bh = ext4_bread(NULL, inode, map.m_lblk, 0);
0200             if (IS_ERR(bh)) {
0201                 err = PTR_ERR(bh);
0202                 bh = NULL;
0203                 goto errout;
0204             }
0205         }
0206 
0207         if (!bh) {
0208             /* corrupt size?  Maybe no more blocks to read */
0209             if (ctx->pos > inode->i_blocks << 9)
0210                 break;
0211             ctx->pos += sb->s_blocksize - offset;
0212             continue;
0213         }
0214 
0215         /* Check the checksum */
0216         if (!buffer_verified(bh) &&
0217             !ext4_dirblock_csum_verify(inode, bh)) {
0218             EXT4_ERROR_FILE(file, 0, "directory fails checksum "
0219                     "at offset %llu",
0220                     (unsigned long long)ctx->pos);
0221             ctx->pos += sb->s_blocksize - offset;
0222             brelse(bh);
0223             bh = NULL;
0224             continue;
0225         }
0226         set_buffer_verified(bh);
0227 
0228         /* If the dir block has changed since the last call to
0229          * readdir(2), then we might be pointing to an invalid
0230          * dirent right now.  Scan from the start of the block
0231          * to make sure. */
0232         if (!inode_eq_iversion(inode, file->f_version)) {
0233             for (i = 0; i < sb->s_blocksize && i < offset; ) {
0234                 de = (struct ext4_dir_entry_2 *)
0235                     (bh->b_data + i);
0236                 /* It's too expensive to do a full
0237                  * dirent test each time round this
0238                  * loop, but we do have to test at
0239                  * least that it is non-zero.  A
0240                  * failure will be detected in the
0241                  * dirent test below. */
0242                 if (ext4_rec_len_from_disk(de->rec_len,
0243                     sb->s_blocksize) < ext4_dir_rec_len(1,
0244                                     inode))
0245                     break;
0246                 i += ext4_rec_len_from_disk(de->rec_len,
0247                                 sb->s_blocksize);
0248             }
0249             offset = i;
0250             ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
0251                 | offset;
0252             file->f_version = inode_query_iversion(inode);
0253         }
0254 
0255         while (ctx->pos < inode->i_size
0256                && offset < sb->s_blocksize) {
0257             de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
0258             if (ext4_check_dir_entry(inode, file, de, bh,
0259                          bh->b_data, bh->b_size,
0260                          offset)) {
0261                 /*
0262                  * On error, skip to the next block
0263                  */
0264                 ctx->pos = (ctx->pos |
0265                         (sb->s_blocksize - 1)) + 1;
0266                 break;
0267             }
0268             offset += ext4_rec_len_from_disk(de->rec_len,
0269                     sb->s_blocksize);
0270             if (le32_to_cpu(de->inode)) {
0271                 if (!IS_ENCRYPTED(inode)) {
0272                     if (!dir_emit(ctx, de->name,
0273                         de->name_len,
0274                         le32_to_cpu(de->inode),
0275                         get_dtype(sb, de->file_type)))
0276                         goto done;
0277                 } else {
0278                     int save_len = fstr.len;
0279                     struct fscrypt_str de_name =
0280                             FSTR_INIT(de->name,
0281                                 de->name_len);
0282 
0283                     /* Directory is encrypted */
0284                     err = fscrypt_fname_disk_to_usr(inode,
0285                         EXT4_DIRENT_HASH(de),
0286                         EXT4_DIRENT_MINOR_HASH(de),
0287                         &de_name, &fstr);
0288                     de_name = fstr;
0289                     fstr.len = save_len;
0290                     if (err)
0291                         goto errout;
0292                     if (!dir_emit(ctx,
0293                         de_name.name, de_name.len,
0294                         le32_to_cpu(de->inode),
0295                         get_dtype(sb, de->file_type)))
0296                         goto done;
0297                 }
0298             }
0299             ctx->pos += ext4_rec_len_from_disk(de->rec_len,
0300                         sb->s_blocksize);
0301         }
0302         if ((ctx->pos < inode->i_size) && !dir_relax_shared(inode))
0303             goto done;
0304         brelse(bh);
0305         bh = NULL;
0306     }
0307 done:
0308     err = 0;
0309 errout:
0310     fscrypt_fname_free_buffer(&fstr);
0311     brelse(bh);
0312     return err;
0313 }
0314 
0315 static inline int is_32bit_api(void)
0316 {
0317 #ifdef CONFIG_COMPAT
0318     return in_compat_syscall();
0319 #else
0320     return (BITS_PER_LONG == 32);
0321 #endif
0322 }
0323 
0324 /*
0325  * These functions convert from the major/minor hash to an f_pos
0326  * value for dx directories
0327  *
0328  * Upper layer (for example NFS) should specify FMODE_32BITHASH or
0329  * FMODE_64BITHASH explicitly. On the other hand, we allow ext4 to be mounted
0330  * directly on both 32-bit and 64-bit nodes, under such case, neither
0331  * FMODE_32BITHASH nor FMODE_64BITHASH is specified.
0332  */
0333 static inline loff_t hash2pos(struct file *filp, __u32 major, __u32 minor)
0334 {
0335     if ((filp->f_mode & FMODE_32BITHASH) ||
0336         (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
0337         return major >> 1;
0338     else
0339         return ((__u64)(major >> 1) << 32) | (__u64)minor;
0340 }
0341 
0342 static inline __u32 pos2maj_hash(struct file *filp, loff_t pos)
0343 {
0344     if ((filp->f_mode & FMODE_32BITHASH) ||
0345         (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
0346         return (pos << 1) & 0xffffffff;
0347     else
0348         return ((pos >> 32) << 1) & 0xffffffff;
0349 }
0350 
0351 static inline __u32 pos2min_hash(struct file *filp, loff_t pos)
0352 {
0353     if ((filp->f_mode & FMODE_32BITHASH) ||
0354         (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
0355         return 0;
0356     else
0357         return pos & 0xffffffff;
0358 }
0359 
0360 /*
0361  * Return 32- or 64-bit end-of-file for dx directories
0362  */
0363 static inline loff_t ext4_get_htree_eof(struct file *filp)
0364 {
0365     if ((filp->f_mode & FMODE_32BITHASH) ||
0366         (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
0367         return EXT4_HTREE_EOF_32BIT;
0368     else
0369         return EXT4_HTREE_EOF_64BIT;
0370 }
0371 
0372 
0373 /*
0374  * ext4_dir_llseek() calls generic_file_llseek_size to handle htree
0375  * directories, where the "offset" is in terms of the filename hash
0376  * value instead of the byte offset.
0377  *
0378  * Because we may return a 64-bit hash that is well beyond offset limits,
0379  * we need to pass the max hash as the maximum allowable offset in
0380  * the htree directory case.
0381  *
0382  * For non-htree, ext4_llseek already chooses the proper max offset.
0383  */
0384 static loff_t ext4_dir_llseek(struct file *file, loff_t offset, int whence)
0385 {
0386     struct inode *inode = file->f_mapping->host;
0387     int dx_dir = is_dx_dir(inode);
0388     loff_t ret, htree_max = ext4_get_htree_eof(file);
0389 
0390     if (likely(dx_dir))
0391         ret = generic_file_llseek_size(file, offset, whence,
0392                             htree_max, htree_max);
0393     else
0394         ret = ext4_llseek(file, offset, whence);
0395     file->f_version = inode_peek_iversion(inode) - 1;
0396     return ret;
0397 }
0398 
0399 /*
0400  * This structure holds the nodes of the red-black tree used to store
0401  * the directory entry in hash order.
0402  */
0403 struct fname {
0404     __u32       hash;
0405     __u32       minor_hash;
0406     struct rb_node  rb_hash;
0407     struct fname    *next;
0408     __u32       inode;
0409     __u8        name_len;
0410     __u8        file_type;
0411     char        name[];
0412 };
0413 
0414 /*
0415  * This function implements a non-recursive way of freeing all of the
0416  * nodes in the red-black tree.
0417  */
0418 static void free_rb_tree_fname(struct rb_root *root)
0419 {
0420     struct fname *fname, *next;
0421 
0422     rbtree_postorder_for_each_entry_safe(fname, next, root, rb_hash)
0423         while (fname) {
0424             struct fname *old = fname;
0425             fname = fname->next;
0426             kfree(old);
0427         }
0428 
0429     *root = RB_ROOT;
0430 }
0431 
0432 
0433 static struct dir_private_info *ext4_htree_create_dir_info(struct file *filp,
0434                                loff_t pos)
0435 {
0436     struct dir_private_info *p;
0437 
0438     p = kzalloc(sizeof(*p), GFP_KERNEL);
0439     if (!p)
0440         return NULL;
0441     p->curr_hash = pos2maj_hash(filp, pos);
0442     p->curr_minor_hash = pos2min_hash(filp, pos);
0443     return p;
0444 }
0445 
0446 void ext4_htree_free_dir_info(struct dir_private_info *p)
0447 {
0448     free_rb_tree_fname(&p->root);
0449     kfree(p);
0450 }
0451 
0452 /*
0453  * Given a directory entry, enter it into the fname rb tree.
0454  *
0455  * When filename encryption is enabled, the dirent will hold the
0456  * encrypted filename, while the htree will hold decrypted filename.
0457  * The decrypted filename is passed in via ent_name.  parameter.
0458  */
0459 int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
0460                  __u32 minor_hash,
0461                 struct ext4_dir_entry_2 *dirent,
0462                 struct fscrypt_str *ent_name)
0463 {
0464     struct rb_node **p, *parent = NULL;
0465     struct fname *fname, *new_fn;
0466     struct dir_private_info *info;
0467     int len;
0468 
0469     info = dir_file->private_data;
0470     p = &info->root.rb_node;
0471 
0472     /* Create and allocate the fname structure */
0473     len = sizeof(struct fname) + ent_name->len + 1;
0474     new_fn = kzalloc(len, GFP_KERNEL);
0475     if (!new_fn)
0476         return -ENOMEM;
0477     new_fn->hash = hash;
0478     new_fn->minor_hash = minor_hash;
0479     new_fn->inode = le32_to_cpu(dirent->inode);
0480     new_fn->name_len = ent_name->len;
0481     new_fn->file_type = dirent->file_type;
0482     memcpy(new_fn->name, ent_name->name, ent_name->len);
0483 
0484     while (*p) {
0485         parent = *p;
0486         fname = rb_entry(parent, struct fname, rb_hash);
0487 
0488         /*
0489          * If the hash and minor hash match up, then we put
0490          * them on a linked list.  This rarely happens...
0491          */
0492         if ((new_fn->hash == fname->hash) &&
0493             (new_fn->minor_hash == fname->minor_hash)) {
0494             new_fn->next = fname->next;
0495             fname->next = new_fn;
0496             return 0;
0497         }
0498 
0499         if (new_fn->hash < fname->hash)
0500             p = &(*p)->rb_left;
0501         else if (new_fn->hash > fname->hash)
0502             p = &(*p)->rb_right;
0503         else if (new_fn->minor_hash < fname->minor_hash)
0504             p = &(*p)->rb_left;
0505         else /* if (new_fn->minor_hash > fname->minor_hash) */
0506             p = &(*p)->rb_right;
0507     }
0508 
0509     rb_link_node(&new_fn->rb_hash, parent, p);
0510     rb_insert_color(&new_fn->rb_hash, &info->root);
0511     return 0;
0512 }
0513 
0514 
0515 
0516 /*
0517  * This is a helper function for ext4_dx_readdir.  It calls filldir
0518  * for all entries on the fname linked list.  (Normally there is only
0519  * one entry on the linked list, unless there are 62 bit hash collisions.)
0520  */
0521 static int call_filldir(struct file *file, struct dir_context *ctx,
0522             struct fname *fname)
0523 {
0524     struct dir_private_info *info = file->private_data;
0525     struct inode *inode = file_inode(file);
0526     struct super_block *sb = inode->i_sb;
0527 
0528     if (!fname) {
0529         ext4_msg(sb, KERN_ERR, "%s:%d: inode #%lu: comm %s: "
0530              "called with null fname?!?", __func__, __LINE__,
0531              inode->i_ino, current->comm);
0532         return 0;
0533     }
0534     ctx->pos = hash2pos(file, fname->hash, fname->minor_hash);
0535     while (fname) {
0536         if (!dir_emit(ctx, fname->name,
0537                 fname->name_len,
0538                 fname->inode,
0539                 get_dtype(sb, fname->file_type))) {
0540             info->extra_fname = fname;
0541             return 1;
0542         }
0543         fname = fname->next;
0544     }
0545     return 0;
0546 }
0547 
0548 static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
0549 {
0550     struct dir_private_info *info = file->private_data;
0551     struct inode *inode = file_inode(file);
0552     struct fname *fname;
0553     int ret = 0;
0554 
0555     if (!info) {
0556         info = ext4_htree_create_dir_info(file, ctx->pos);
0557         if (!info)
0558             return -ENOMEM;
0559         file->private_data = info;
0560     }
0561 
0562     if (ctx->pos == ext4_get_htree_eof(file))
0563         return 0;   /* EOF */
0564 
0565     /* Some one has messed with f_pos; reset the world */
0566     if (info->last_pos != ctx->pos) {
0567         free_rb_tree_fname(&info->root);
0568         info->curr_node = NULL;
0569         info->extra_fname = NULL;
0570         info->curr_hash = pos2maj_hash(file, ctx->pos);
0571         info->curr_minor_hash = pos2min_hash(file, ctx->pos);
0572     }
0573 
0574     /*
0575      * If there are any leftover names on the hash collision
0576      * chain, return them first.
0577      */
0578     if (info->extra_fname) {
0579         if (call_filldir(file, ctx, info->extra_fname))
0580             goto finished;
0581         info->extra_fname = NULL;
0582         goto next_node;
0583     } else if (!info->curr_node)
0584         info->curr_node = rb_first(&info->root);
0585 
0586     while (1) {
0587         /*
0588          * Fill the rbtree if we have no more entries,
0589          * or the inode has changed since we last read in the
0590          * cached entries.
0591          */
0592         if ((!info->curr_node) ||
0593             !inode_eq_iversion(inode, file->f_version)) {
0594             info->curr_node = NULL;
0595             free_rb_tree_fname(&info->root);
0596             file->f_version = inode_query_iversion(inode);
0597             ret = ext4_htree_fill_tree(file, info->curr_hash,
0598                            info->curr_minor_hash,
0599                            &info->next_hash);
0600             if (ret < 0)
0601                 goto finished;
0602             if (ret == 0) {
0603                 ctx->pos = ext4_get_htree_eof(file);
0604                 break;
0605             }
0606             info->curr_node = rb_first(&info->root);
0607         }
0608 
0609         fname = rb_entry(info->curr_node, struct fname, rb_hash);
0610         info->curr_hash = fname->hash;
0611         info->curr_minor_hash = fname->minor_hash;
0612         if (call_filldir(file, ctx, fname))
0613             break;
0614     next_node:
0615         info->curr_node = rb_next(info->curr_node);
0616         if (info->curr_node) {
0617             fname = rb_entry(info->curr_node, struct fname,
0618                      rb_hash);
0619             info->curr_hash = fname->hash;
0620             info->curr_minor_hash = fname->minor_hash;
0621         } else {
0622             if (info->next_hash == ~0) {
0623                 ctx->pos = ext4_get_htree_eof(file);
0624                 break;
0625             }
0626             info->curr_hash = info->next_hash;
0627             info->curr_minor_hash = 0;
0628         }
0629     }
0630 finished:
0631     info->last_pos = ctx->pos;
0632     return ret < 0 ? ret : 0;
0633 }
0634 
0635 static int ext4_release_dir(struct inode *inode, struct file *filp)
0636 {
0637     if (filp->private_data)
0638         ext4_htree_free_dir_info(filp->private_data);
0639 
0640     return 0;
0641 }
0642 
0643 int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf,
0644               int buf_size)
0645 {
0646     struct ext4_dir_entry_2 *de;
0647     int rlen;
0648     unsigned int offset = 0;
0649     char *top;
0650 
0651     de = buf;
0652     top = buf + buf_size;
0653     while ((char *) de < top) {
0654         if (ext4_check_dir_entry(dir, NULL, de, bh,
0655                      buf, buf_size, offset))
0656             return -EFSCORRUPTED;
0657         rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
0658         de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
0659         offset += rlen;
0660     }
0661     if ((char *) de > top)
0662         return -EFSCORRUPTED;
0663 
0664     return 0;
0665 }
0666 
0667 const struct file_operations ext4_dir_operations = {
0668     .llseek     = ext4_dir_llseek,
0669     .read       = generic_read_dir,
0670     .iterate_shared = ext4_readdir,
0671     .unlocked_ioctl = ext4_ioctl,
0672 #ifdef CONFIG_COMPAT
0673     .compat_ioctl   = ext4_compat_ioctl,
0674 #endif
0675     .fsync      = ext4_sync_file,
0676     .release    = ext4_release_dir,
0677 };