1// SPDX-License-Identifier: GPL-2.0
2/*
3 * fs/hmdfs/hmdfs_dentryfile_cloud.c
4 *
5 * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
6 */
7
8#include "hmdfs_dentryfile_cloud.h"
9
10#include <linux/slab.h>
11
12void hmdfs_init_dcache_lookup_ctx_cloud(
13	struct hmdfs_dcache_lookup_ctx_cloud *ctx, struct hmdfs_sb_info *sbi,
14	const struct qstr *qstr, struct file *filp)
15{
16	ctx->sbi = sbi;
17	ctx->name = qstr;
18	ctx->filp = filp;
19	ctx->bidx = 0;
20	ctx->page = NULL;
21	ctx->insense_de = NULL;
22	ctx->insense_bidx = 0;
23	ctx->insense_page = NULL;
24}
25
26static struct hmdfs_dentry_group_cloud *find_dentry_page(struct hmdfs_sb_info *sbi,
27					    pgoff_t index, struct file *filp)
28{
29	int size;
30	struct hmdfs_dentry_group_cloud *dentry_blk = NULL;
31	loff_t pos = get_dentry_group_pos(index);
32	int err;
33
34	dentry_blk = kmalloc(sizeof(*dentry_blk), GFP_KERNEL);
35	if (!dentry_blk)
36		return NULL;
37
38	err = hmdfs_wlock_file(filp, pos, DENTRYGROUP_SIZE);
39	if (err) {
40		hmdfs_err("lock file pos %lld failed %d", pos, err);
41		kfree(dentry_blk);
42		return NULL;
43	}
44
45	size = kernel_read(filp, dentry_blk, (size_t)DENTRYGROUP_SIZE,
46			       &pos);
47	if (size != DENTRYGROUP_SIZE) {
48		hmdfs_err("read pos %lld failed %d", pos, size);
49		hmdfs_unlock_file(filp, pos, DENTRYGROUP_SIZE);
50		kfree(dentry_blk);
51		dentry_blk = NULL;
52	}
53
54	return dentry_blk;
55}
56
57static struct hmdfs_dentry_cloud *
58find_in_block(struct hmdfs_dentry_group_cloud *dentry_blk, __u32 namehash,
59	      const struct qstr *qstr, struct hmdfs_dentry_cloud **insense_de,
60	      bool case_sense)
61{
62	struct hmdfs_dentry_cloud *de;
63	unsigned long bit_pos = 0;
64	int max_len = 0;
65
66	while (bit_pos < DENTRY_PER_GROUP_CLOUD) {
67		if (!test_bit_le(bit_pos, dentry_blk->bitmap)) {
68			bit_pos++;
69			max_len++;
70			continue;
71		}
72		de = &dentry_blk->nsl[bit_pos];
73		if (unlikely(!de->namelen)) {
74			bit_pos++;
75			continue;
76		}
77
78		if (le32_to_cpu(de->hash) == namehash &&
79		    le16_to_cpu(de->namelen) == qstr->len &&
80		    !memcmp(qstr->name, dentry_blk->filename[bit_pos],
81			    le16_to_cpu(de->namelen)))
82			goto found;
83		if (!(*insense_de) && !case_sense &&
84		    le32_to_cpu(de->hash) == namehash &&
85		    le16_to_cpu(de->namelen) == qstr->len &&
86		    str_n_case_eq(qstr->name, dentry_blk->filename[bit_pos],
87				  le16_to_cpu(de->namelen)))
88			*insense_de = de;
89		max_len = 0;
90		bit_pos += get_dentry_slots(le16_to_cpu(de->namelen));
91	}
92	de = NULL;
93found:
94	return de;
95}
96
97static struct hmdfs_dentry_cloud *
98hmdfs_in_level(struct dentry *child_dentry, unsigned int level,
99	       struct hmdfs_dcache_lookup_ctx_cloud *ctx)
100{
101	unsigned long nbucket;
102	unsigned long bidx, end_block;
103	struct hmdfs_dentry_cloud *de = NULL;
104	struct hmdfs_dentry_cloud *tmp_insense_de = NULL;
105	struct hmdfs_dentry_group_cloud *dentry_blk;
106
107	nbucket = get_bucket_by_level(level);
108	if (!nbucket)
109		return de;
110
111	bidx = get_bucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
112	end_block = bidx + BUCKET_BLOCKS;
113
114	for (; bidx < end_block; bidx++) {
115		dentry_blk = find_dentry_page(ctx->sbi, bidx, ctx->filp);
116		if (!dentry_blk)
117			break;
118
119		de = find_in_block(dentry_blk, ctx->hash, ctx->name,
120				   &tmp_insense_de, ctx->sbi->s_case_sensitive);
121		if (!de && !(ctx->insense_de) && tmp_insense_de) {
122			ctx->insense_de = tmp_insense_de;
123			ctx->insense_page = dentry_blk;
124			ctx->insense_bidx = bidx;
125		} else if (!de) {
126			hmdfs_unlock_file(ctx->filp, get_dentry_group_pos(bidx),
127					  DENTRYGROUP_SIZE);
128			kfree(dentry_blk);
129		} else {
130			ctx->page = dentry_blk;
131			break;
132		}
133	}
134	ctx->bidx = bidx;
135	return de;
136}
137
138struct hmdfs_dentry_cloud *
139hmdfs_find_dentry_cloud(struct dentry *child_dentry,
140			struct hmdfs_dcache_lookup_ctx_cloud *ctx)
141{
142	struct hmdfs_dentry_cloud *de = NULL;
143	unsigned int max_depth;
144	unsigned int level;
145
146	if (!ctx->filp)
147		return NULL;
148
149	ctx->hash = hmdfs_dentry_hash(ctx->name, ctx->sbi->s_case_sensitive);
150	max_depth = get_max_depth(ctx->filp);
151	for (level = 0; level < max_depth; level++) {
152		de = hmdfs_in_level(child_dentry, level, ctx);
153		if (de) {
154			if (ctx->insense_page) {
155				hmdfs_unlock_file(ctx->filp,
156					get_dentry_group_pos(ctx->insense_bidx),
157					DENTRYGROUP_SIZE);
158				kfree(ctx->insense_page);
159				ctx->insense_page = NULL;
160			}
161			return de;
162		}
163	}
164	if (ctx->insense_de) {
165		ctx->bidx = ctx->insense_bidx;
166		ctx->page = ctx->insense_page;
167		ctx->insense_bidx = 0;
168		ctx->insense_page = NULL;
169	}
170	return ctx->insense_de;
171}
172