18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2000-2001 Christoph Hellwig.
38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Krzysztof Blaszkowski
48c2ecf20Sopenharmony_ci * All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
78c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
88c2ecf20Sopenharmony_ci * are met:
98c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
108c2ecf20Sopenharmony_ci *    notice, this list of conditions, and the following disclaimer,
118c2ecf20Sopenharmony_ci *    without modification.
128c2ecf20Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products
138c2ecf20Sopenharmony_ci *    derived from this software without specific prior written permission.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
168c2ecf20Sopenharmony_ci * GNU General Public License ("GPL").
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
198c2ecf20Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
208c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
218c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
228c2ecf20Sopenharmony_ci * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
238c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
248c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
258c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
268c2ecf20Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
278c2ecf20Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
288c2ecf20Sopenharmony_ci * SUCH DAMAGE.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * Veritas filesystem driver - lookup and other directory related code.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#include <linux/fs.h>
358c2ecf20Sopenharmony_ci#include <linux/time.h>
368c2ecf20Sopenharmony_ci#include <linux/mm.h>
378c2ecf20Sopenharmony_ci#include <linux/highmem.h>
388c2ecf20Sopenharmony_ci#include <linux/kernel.h>
398c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "vxfs.h"
428c2ecf20Sopenharmony_ci#include "vxfs_dir.h"
438c2ecf20Sopenharmony_ci#include "vxfs_inode.h"
448c2ecf20Sopenharmony_ci#include "vxfs_extern.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * Number of VxFS blocks per page.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define VXFS_BLOCK_PER_PAGE(sbp)  ((PAGE_SIZE / (sbp)->s_blocksize))
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic struct dentry *	vxfs_lookup(struct inode *, struct dentry *, unsigned int);
538c2ecf20Sopenharmony_cistatic int		vxfs_readdir(struct file *, struct dir_context *);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciconst struct inode_operations vxfs_dir_inode_ops = {
568c2ecf20Sopenharmony_ci	.lookup =		vxfs_lookup,
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciconst struct file_operations vxfs_dir_operations = {
608c2ecf20Sopenharmony_ci	.llseek =		generic_file_llseek,
618c2ecf20Sopenharmony_ci	.read =			generic_read_dir,
628c2ecf20Sopenharmony_ci	.iterate_shared =	vxfs_readdir,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * vxfs_find_entry - find a mathing directory entry for a dentry
688c2ecf20Sopenharmony_ci * @ip:		directory inode
698c2ecf20Sopenharmony_ci * @dp:		dentry for which we want to find a direct
708c2ecf20Sopenharmony_ci * @ppp:	gets filled with the page the return value sits in
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Description:
738c2ecf20Sopenharmony_ci *   vxfs_find_entry finds a &struct vxfs_direct for the VFS directory
748c2ecf20Sopenharmony_ci *   cache entry @dp.  @ppp will be filled with the page the return
758c2ecf20Sopenharmony_ci *   value resides in.
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * Returns:
788c2ecf20Sopenharmony_ci *   The wanted direct on success, else a NULL pointer.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic struct vxfs_direct *
818c2ecf20Sopenharmony_civxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	u_long bsize = ip->i_sb->s_blocksize;
848c2ecf20Sopenharmony_ci	const char *name = dp->d_name.name;
858c2ecf20Sopenharmony_ci	int namelen = dp->d_name.len;
868c2ecf20Sopenharmony_ci	loff_t limit = VXFS_DIRROUND(ip->i_size);
878c2ecf20Sopenharmony_ci	struct vxfs_direct *de_exit = NULL;
888c2ecf20Sopenharmony_ci	loff_t pos = 0;
898c2ecf20Sopenharmony_ci	struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	while (pos < limit) {
928c2ecf20Sopenharmony_ci		struct page *pp;
938c2ecf20Sopenharmony_ci		char *kaddr;
948c2ecf20Sopenharmony_ci		int pg_ofs = pos & ~PAGE_MASK;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT);
978c2ecf20Sopenharmony_ci		if (IS_ERR(pp))
988c2ecf20Sopenharmony_ci			return NULL;
998c2ecf20Sopenharmony_ci		kaddr = (char *)page_address(pp);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		while (pg_ofs < PAGE_SIZE && pos < limit) {
1028c2ecf20Sopenharmony_ci			struct vxfs_direct *de;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci			if ((pos & (bsize - 1)) < 4) {
1058c2ecf20Sopenharmony_ci				struct vxfs_dirblk *dbp =
1068c2ecf20Sopenharmony_ci					(struct vxfs_dirblk *)
1078c2ecf20Sopenharmony_ci					 (kaddr + (pos & ~PAGE_MASK));
1088c2ecf20Sopenharmony_ci				int overhead = VXFS_DIRBLKOV(sbi, dbp);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci				pos += overhead;
1118c2ecf20Sopenharmony_ci				pg_ofs += overhead;
1128c2ecf20Sopenharmony_ci			}
1138c2ecf20Sopenharmony_ci			de = (struct vxfs_direct *)(kaddr + pg_ofs);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			if (!de->d_reclen) {
1168c2ecf20Sopenharmony_ci				pos += bsize - 1;
1178c2ecf20Sopenharmony_ci				pos &= ~(bsize - 1);
1188c2ecf20Sopenharmony_ci				break;
1198c2ecf20Sopenharmony_ci			}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci			pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
1228c2ecf20Sopenharmony_ci			pos += fs16_to_cpu(sbi, de->d_reclen);
1238c2ecf20Sopenharmony_ci			if (!de->d_ino)
1248c2ecf20Sopenharmony_ci				continue;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci			if (namelen != fs16_to_cpu(sbi, de->d_namelen))
1278c2ecf20Sopenharmony_ci				continue;
1288c2ecf20Sopenharmony_ci			if (!memcmp(name, de->d_name, namelen)) {
1298c2ecf20Sopenharmony_ci				*ppp = pp;
1308c2ecf20Sopenharmony_ci				de_exit = de;
1318c2ecf20Sopenharmony_ci				break;
1328c2ecf20Sopenharmony_ci			}
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci		if (!de_exit)
1358c2ecf20Sopenharmony_ci			vxfs_put_page(pp);
1368c2ecf20Sopenharmony_ci		else
1378c2ecf20Sopenharmony_ci			break;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return de_exit;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/**
1448c2ecf20Sopenharmony_ci * vxfs_inode_by_name - find inode number for dentry
1458c2ecf20Sopenharmony_ci * @dip:	directory to search in
1468c2ecf20Sopenharmony_ci * @dp:		dentry we search for
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci * Description:
1498c2ecf20Sopenharmony_ci *   vxfs_inode_by_name finds out the inode number of
1508c2ecf20Sopenharmony_ci *   the path component described by @dp in @dip.
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci * Returns:
1538c2ecf20Sopenharmony_ci *   The wanted inode number on success, else Zero.
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic ino_t
1568c2ecf20Sopenharmony_civxfs_inode_by_name(struct inode *dip, struct dentry *dp)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct vxfs_direct		*de;
1598c2ecf20Sopenharmony_ci	struct page			*pp;
1608c2ecf20Sopenharmony_ci	ino_t				ino = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	de = vxfs_find_entry(dip, dp, &pp);
1638c2ecf20Sopenharmony_ci	if (de) {
1648c2ecf20Sopenharmony_ci		ino = fs32_to_cpu(VXFS_SBI(dip->i_sb), de->d_ino);
1658c2ecf20Sopenharmony_ci		kunmap(pp);
1668c2ecf20Sopenharmony_ci		put_page(pp);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return (ino);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/**
1738c2ecf20Sopenharmony_ci * vxfs_lookup - lookup pathname component
1748c2ecf20Sopenharmony_ci * @dip:	dir in which we lookup
1758c2ecf20Sopenharmony_ci * @dp:		dentry we lookup
1768c2ecf20Sopenharmony_ci * @flags:	lookup flags
1778c2ecf20Sopenharmony_ci *
1788c2ecf20Sopenharmony_ci * Description:
1798c2ecf20Sopenharmony_ci *   vxfs_lookup tries to lookup the pathname component described
1808c2ecf20Sopenharmony_ci *   by @dp in @dip.
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Returns:
1838c2ecf20Sopenharmony_ci *   A NULL-pointer on success, else a negative error code encoded
1848c2ecf20Sopenharmony_ci *   in the return pointer.
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic struct dentry *
1878c2ecf20Sopenharmony_civxfs_lookup(struct inode *dip, struct dentry *dp, unsigned int flags)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct inode		*ip = NULL;
1908c2ecf20Sopenharmony_ci	ino_t			ino;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (dp->d_name.len > VXFS_NAMELEN)
1938c2ecf20Sopenharmony_ci		return ERR_PTR(-ENAMETOOLONG);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ino = vxfs_inode_by_name(dip, dp);
1968c2ecf20Sopenharmony_ci	if (ino)
1978c2ecf20Sopenharmony_ci		ip = vxfs_iget(dip->i_sb, ino);
1988c2ecf20Sopenharmony_ci	return d_splice_alias(ip, dp);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * vxfs_readdir - read a directory
2038c2ecf20Sopenharmony_ci * @fp:		the directory to read
2048c2ecf20Sopenharmony_ci * @retp:	return buffer
2058c2ecf20Sopenharmony_ci * @filler:	filldir callback
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * Description:
2088c2ecf20Sopenharmony_ci *   vxfs_readdir fills @retp with directory entries from @fp
2098c2ecf20Sopenharmony_ci *   using the VFS supplied callback @filler.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * Returns:
2128c2ecf20Sopenharmony_ci *   Zero.
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_cistatic int
2158c2ecf20Sopenharmony_civxfs_readdir(struct file *fp, struct dir_context *ctx)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct inode		*ip = file_inode(fp);
2188c2ecf20Sopenharmony_ci	struct super_block	*sbp = ip->i_sb;
2198c2ecf20Sopenharmony_ci	u_long			bsize = sbp->s_blocksize;
2208c2ecf20Sopenharmony_ci	loff_t			pos, limit;
2218c2ecf20Sopenharmony_ci	struct vxfs_sb_info	*sbi = VXFS_SBI(sbp);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (ctx->pos == 0) {
2248c2ecf20Sopenharmony_ci		if (!dir_emit_dot(fp, ctx))
2258c2ecf20Sopenharmony_ci			goto out;
2268c2ecf20Sopenharmony_ci		ctx->pos++;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	if (ctx->pos == 1) {
2298c2ecf20Sopenharmony_ci		if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR))
2308c2ecf20Sopenharmony_ci			goto out;
2318c2ecf20Sopenharmony_ci		ctx->pos++;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	limit = VXFS_DIRROUND(ip->i_size);
2358c2ecf20Sopenharmony_ci	if (ctx->pos > limit)
2368c2ecf20Sopenharmony_ci		goto out;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	pos = ctx->pos & ~3L;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	while (pos < limit) {
2418c2ecf20Sopenharmony_ci		struct page *pp;
2428c2ecf20Sopenharmony_ci		char *kaddr;
2438c2ecf20Sopenharmony_ci		int pg_ofs = pos & ~PAGE_MASK;
2448c2ecf20Sopenharmony_ci		int rc = 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci		pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT);
2478c2ecf20Sopenharmony_ci		if (IS_ERR(pp))
2488c2ecf20Sopenharmony_ci			return -ENOMEM;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		kaddr = (char *)page_address(pp);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		while (pg_ofs < PAGE_SIZE && pos < limit) {
2538c2ecf20Sopenharmony_ci			struct vxfs_direct *de;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci			if ((pos & (bsize - 1)) < 4) {
2568c2ecf20Sopenharmony_ci				struct vxfs_dirblk *dbp =
2578c2ecf20Sopenharmony_ci					(struct vxfs_dirblk *)
2588c2ecf20Sopenharmony_ci					 (kaddr + (pos & ~PAGE_MASK));
2598c2ecf20Sopenharmony_ci				int overhead = VXFS_DIRBLKOV(sbi, dbp);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci				pos += overhead;
2628c2ecf20Sopenharmony_ci				pg_ofs += overhead;
2638c2ecf20Sopenharmony_ci			}
2648c2ecf20Sopenharmony_ci			de = (struct vxfs_direct *)(kaddr + pg_ofs);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci			if (!de->d_reclen) {
2678c2ecf20Sopenharmony_ci				pos += bsize - 1;
2688c2ecf20Sopenharmony_ci				pos &= ~(bsize - 1);
2698c2ecf20Sopenharmony_ci				break;
2708c2ecf20Sopenharmony_ci			}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci			pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
2738c2ecf20Sopenharmony_ci			pos += fs16_to_cpu(sbi, de->d_reclen);
2748c2ecf20Sopenharmony_ci			if (!de->d_ino)
2758c2ecf20Sopenharmony_ci				continue;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci			rc = dir_emit(ctx, de->d_name,
2788c2ecf20Sopenharmony_ci					fs16_to_cpu(sbi, de->d_namelen),
2798c2ecf20Sopenharmony_ci					fs32_to_cpu(sbi, de->d_ino),
2808c2ecf20Sopenharmony_ci					DT_UNKNOWN);
2818c2ecf20Sopenharmony_ci			if (!rc) {
2828c2ecf20Sopenharmony_ci				/* the dir entry was not read, fix pos. */
2838c2ecf20Sopenharmony_ci				pos -= fs16_to_cpu(sbi, de->d_reclen);
2848c2ecf20Sopenharmony_ci				break;
2858c2ecf20Sopenharmony_ci			}
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci		vxfs_put_page(pp);
2888c2ecf20Sopenharmony_ci		if (!rc)
2898c2ecf20Sopenharmony_ci			break;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ctx->pos = pos | 2;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciout:
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
297