162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * dir.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1999 Al Smith 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/buffer_head.h> 962306a36Sopenharmony_ci#include "efs.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic int efs_readdir(struct file *, struct dir_context *); 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciconst struct file_operations efs_dir_operations = { 1462306a36Sopenharmony_ci .llseek = generic_file_llseek, 1562306a36Sopenharmony_ci .read = generic_read_dir, 1662306a36Sopenharmony_ci .iterate_shared = efs_readdir, 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciconst struct inode_operations efs_dir_inode_operations = { 2062306a36Sopenharmony_ci .lookup = efs_lookup, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int efs_readdir(struct file *file, struct dir_context *ctx) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 2662306a36Sopenharmony_ci efs_block_t block; 2762306a36Sopenharmony_ci int slot; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (inode->i_size & (EFS_DIRBSIZE-1)) 3062306a36Sopenharmony_ci pr_warn("%s(): directory size not a multiple of EFS_DIRBSIZE\n", 3162306a36Sopenharmony_ci __func__); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* work out where this entry can be found */ 3462306a36Sopenharmony_ci block = ctx->pos >> EFS_DIRBSIZE_BITS; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* each block contains at most 256 slots */ 3762306a36Sopenharmony_ci slot = ctx->pos & 0xff; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* look at all blocks */ 4062306a36Sopenharmony_ci while (block < inode->i_blocks) { 4162306a36Sopenharmony_ci struct efs_dir *dirblock; 4262306a36Sopenharmony_ci struct buffer_head *bh; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* read the dir block */ 4562306a36Sopenharmony_ci bh = sb_bread(inode->i_sb, efs_bmap(inode, block)); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (!bh) { 4862306a36Sopenharmony_ci pr_err("%s(): failed to read dir block %d\n", 4962306a36Sopenharmony_ci __func__, block); 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci dirblock = (struct efs_dir *) bh->b_data; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) { 5662306a36Sopenharmony_ci pr_err("%s(): invalid directory block\n", __func__); 5762306a36Sopenharmony_ci brelse(bh); 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci for (; slot < dirblock->slots; slot++) { 6262306a36Sopenharmony_ci struct efs_dentry *dirslot; 6362306a36Sopenharmony_ci efs_ino_t inodenum; 6462306a36Sopenharmony_ci const char *nameptr; 6562306a36Sopenharmony_ci int namelen; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (dirblock->space[slot] == 0) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot)); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci inodenum = be32_to_cpu(dirslot->inode); 7362306a36Sopenharmony_ci namelen = dirslot->namelen; 7462306a36Sopenharmony_ci nameptr = dirslot->name; 7562306a36Sopenharmony_ci pr_debug("%s(): block %d slot %d/%d: inode %u, name \"%s\", namelen %u\n", 7662306a36Sopenharmony_ci __func__, block, slot, dirblock->slots-1, 7762306a36Sopenharmony_ci inodenum, nameptr, namelen); 7862306a36Sopenharmony_ci if (!namelen) 7962306a36Sopenharmony_ci continue; 8062306a36Sopenharmony_ci /* found the next entry */ 8162306a36Sopenharmony_ci ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* sanity check */ 8462306a36Sopenharmony_ci if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) { 8562306a36Sopenharmony_ci pr_warn("directory entry %d exceeds directory block\n", 8662306a36Sopenharmony_ci slot); 8762306a36Sopenharmony_ci continue; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* copy filename and data in dirslot */ 9162306a36Sopenharmony_ci if (!dir_emit(ctx, nameptr, namelen, inodenum, DT_UNKNOWN)) { 9262306a36Sopenharmony_ci brelse(bh); 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci brelse(bh); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci slot = 0; 9962306a36Sopenharmony_ci block++; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 104