162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/hmdfs/dentry.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/ctype.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "comm/connection.h"
1262306a36Sopenharmony_ci#include "hmdfs_dentryfile.h"
1362306a36Sopenharmony_ci#include "hmdfs_device_view.h"
1462306a36Sopenharmony_ci#include "hmdfs_merge_view.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciextern struct kmem_cache *hmdfs_dentry_cachep;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_civoid hmdfs_set_time(struct dentry *dentry, unsigned long time)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (d_info)
2362306a36Sopenharmony_ci		d_info->time = time;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciunsigned long hmdfs_get_time(struct dentry *dentry)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (d_info)
3162306a36Sopenharmony_ci		return (unsigned long)d_info->time;
3262306a36Sopenharmony_ci	return 0;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int hmdfs_d_remote_revalidate(struct hmdfs_peer *conn,
3662306a36Sopenharmony_ci				     struct dentry *target,
3762306a36Sopenharmony_ci				     struct dentry *parent)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	unsigned int timeout = hmdfs_sb(target->d_sb)->dcache_timeout;
4062306a36Sopenharmony_ci	unsigned long dentry_time = hmdfs_get_time(target);
4162306a36Sopenharmony_ci	struct clearcache_item *item;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	item = hmdfs_find_cache_item(conn->device_id, parent);
4462306a36Sopenharmony_ci	if (!item)
4562306a36Sopenharmony_ci		return 0;
4662306a36Sopenharmony_ci	kref_put(&item->ref, release_cache_item);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (cache_item_revalidate(READ_ONCE(conn->conn_time),
4962306a36Sopenharmony_ci				  dentry_time, timeout))
5062306a36Sopenharmony_ci		return 1;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic inline void lock_for_dname_cmp(struct dentry *dentry,
5662306a36Sopenharmony_ci				      struct dentry *lower_dentry)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	if (dentry < lower_dentry) {
5962306a36Sopenharmony_ci		spin_lock(&dentry->d_lock);
6062306a36Sopenharmony_ci		spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
6162306a36Sopenharmony_ci	} else {
6262306a36Sopenharmony_ci		spin_lock(&lower_dentry->d_lock);
6362306a36Sopenharmony_ci		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic inline void unlock_for_dname_cmp(struct dentry *dentry,
6862306a36Sopenharmony_ci					struct dentry *lower_dentry)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	spin_unlock(&dentry->d_lock);
7162306a36Sopenharmony_ci	spin_unlock(&lower_dentry->d_lock);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int hmdfs_dev_d_revalidate(struct dentry *direntry, unsigned int flags)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct inode *dinode = NULL;
7762306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_lock(&direntry->d_lock);
8062306a36Sopenharmony_ci	if (IS_ROOT(direntry)) {
8162306a36Sopenharmony_ci		spin_unlock(&direntry->d_lock);
8262306a36Sopenharmony_ci		return 1;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	spin_unlock(&direntry->d_lock);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	dinode = d_inode(direntry);
8762306a36Sopenharmony_ci	if (!dinode)
8862306a36Sopenharmony_ci		return 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	info = hmdfs_i(dinode);
9162306a36Sopenharmony_ci	if (info->inode_type == HMDFS_LAYER_SECOND_LOCAL ||
9262306a36Sopenharmony_ci	    info->inode_type == HMDFS_LAYER_FIRST_DEVICE) {
9362306a36Sopenharmony_ci		return 1;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci	if (info->conn && info->conn->status == NODE_STAT_ONLINE)
9662306a36Sopenharmony_ci		return 1;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int hmdfs_d_revalidate(struct dentry *direntry, unsigned int flags)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct inode *dinode = NULL;
10462306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
10562306a36Sopenharmony_ci	struct path lower_path, parent_lower_path;
10662306a36Sopenharmony_ci	struct dentry *parent_dentry = NULL;
10762306a36Sopenharmony_ci	struct dentry *parent_lower_dentry = NULL;
10862306a36Sopenharmony_ci	struct dentry *lower_cur_parent_dentry = NULL;
10962306a36Sopenharmony_ci	struct dentry *lower_dentry = NULL;
11062306a36Sopenharmony_ci	int ret;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
11362306a36Sopenharmony_ci		return -ECHILD;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL))
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	dinode = d_inode(direntry);
11962306a36Sopenharmony_ci	if (!dinode)
12062306a36Sopenharmony_ci		return 0;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* remote dentry timeout */
12362306a36Sopenharmony_ci	info = hmdfs_i(dinode);
12462306a36Sopenharmony_ci	parent_dentry = dget_parent(direntry);
12562306a36Sopenharmony_ci	if (info->conn) {
12662306a36Sopenharmony_ci		ret = hmdfs_d_remote_revalidate(info->conn, direntry,
12762306a36Sopenharmony_ci						parent_dentry);
12862306a36Sopenharmony_ci		dput(parent_dentry);
12962306a36Sopenharmony_ci		return ret;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	hmdfs_get_lower_path(direntry, &lower_path);
13362306a36Sopenharmony_ci	lower_dentry = lower_path.dentry;
13462306a36Sopenharmony_ci	lower_cur_parent_dentry = dget_parent(lower_dentry);
13562306a36Sopenharmony_ci	hmdfs_get_lower_path(parent_dentry, &parent_lower_path);
13662306a36Sopenharmony_ci	parent_lower_dentry = parent_lower_path.dentry;
13762306a36Sopenharmony_ci	if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
13862306a36Sopenharmony_ci		ret = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
13962306a36Sopenharmony_ci		if (ret == 0)
14062306a36Sopenharmony_ci			goto out;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spin_lock(&lower_dentry->d_lock);
14462306a36Sopenharmony_ci	if (d_unhashed(lower_dentry)) {
14562306a36Sopenharmony_ci		spin_unlock(&lower_dentry->d_lock);
14662306a36Sopenharmony_ci		ret = 0;
14762306a36Sopenharmony_ci		goto out;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	spin_unlock(&lower_dentry->d_lock);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (parent_lower_dentry != lower_cur_parent_dentry) {
15262306a36Sopenharmony_ci		ret = 0;
15362306a36Sopenharmony_ci		goto out;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = 1;
15762306a36Sopenharmony_ci	lock_for_dname_cmp(direntry, lower_dentry);
15862306a36Sopenharmony_ci	if (!qstr_case_eq(&direntry->d_name, &lower_dentry->d_name))
15962306a36Sopenharmony_ci		ret = 0;
16062306a36Sopenharmony_ci	unlock_for_dname_cmp(direntry, lower_dentry);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ciout:
16362306a36Sopenharmony_ci	hmdfs_put_lower_path(&parent_lower_path);
16462306a36Sopenharmony_ci	dput(lower_cur_parent_dentry);
16562306a36Sopenharmony_ci	hmdfs_put_lower_path(&lower_path);
16662306a36Sopenharmony_ci	dput(parent_dentry);
16762306a36Sopenharmony_ci	return ret;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void hmdfs_dev_d_release(struct dentry *dentry)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct clearcache_item *item;
17362306a36Sopenharmony_ci	if (!dentry || !dentry->d_fsdata)
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (hmdfs_d(dentry)->dentry_type) {
17762306a36Sopenharmony_ci	case HMDFS_LAYER_SECOND_LOCAL:
17862306a36Sopenharmony_ci		hmdfs_clear_cache_dents(dentry, false);
17962306a36Sopenharmony_ci		hmdfs_drop_remote_cache_dents(dentry);
18062306a36Sopenharmony_ci		path_put(&(hmdfs_d(dentry)->lower_path));
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	case HMDFS_LAYER_ZERO:
18362306a36Sopenharmony_ci		hmdfs_put_reset_lower_path(dentry);
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	case HMDFS_LAYER_FIRST_DEVICE:
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case HMDFS_LAYER_SECOND_REMOTE:
18862306a36Sopenharmony_ci		hmdfs_clear_cache_dents(dentry, false);
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case HMDFS_LAYER_SECOND_CLOUD:
19162306a36Sopenharmony_ci		item = hmdfs_find_cache_item(CLOUD_DEVICE, dentry);
19262306a36Sopenharmony_ci		if (item) {
19362306a36Sopenharmony_ci			/* cloud dentryfile didn't link to
19462306a36Sopenharmony_ci			   'struct cache_file_node', so close file here.
19562306a36Sopenharmony_ci			 */
19662306a36Sopenharmony_ci			filp_close(item->filp, NULL);
19762306a36Sopenharmony_ci			kref_put(&item->ref, release_cache_item);
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci		hmdfs_clear_cache_dents(dentry, false);
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	default:
20262306a36Sopenharmony_ci		hmdfs_err("Unexpected dentry type %d",
20362306a36Sopenharmony_ci			  hmdfs_d(dentry)->dentry_type);
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
20862306a36Sopenharmony_ci	dentry->d_fsdata = NULL;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void hmdfs_d_release(struct dentry *dentry)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	if (!dentry || !dentry->d_fsdata)
21462306a36Sopenharmony_ci		return;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	hmdfs_clear_cache_dents(dentry, false);
21762306a36Sopenharmony_ci	hmdfs_drop_remote_cache_dents(dentry);
21862306a36Sopenharmony_ci	hmdfs_put_reset_lower_path(dentry);
21962306a36Sopenharmony_ci	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
22062306a36Sopenharmony_ci	dentry->d_fsdata = NULL;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int hmdfs_cmp_ci(const struct dentry *dentry, unsigned int len,
22462306a36Sopenharmony_ci			const char *str, const struct qstr *name)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (name->len != len)
22962306a36Sopenharmony_ci		return 1;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (!sbi->s_case_sensitive) {
23262306a36Sopenharmony_ci		if (str_n_case_eq(name->name, str, len))
23362306a36Sopenharmony_ci			return 0;
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		if (!strncmp(name->name, str, len))
23662306a36Sopenharmony_ci			return 0;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	return 1;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int hmdfs_hash_ci(const struct dentry *dentry, struct qstr *qstr)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	const unsigned char *name = qstr->name;
24462306a36Sopenharmony_ci	unsigned int len = qstr->len;
24562306a36Sopenharmony_ci	unsigned long hash;
24662306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (sbi->s_case_sensitive)
24962306a36Sopenharmony_ci		return 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	hash = init_name_hash(dentry);
25262306a36Sopenharmony_ci	while (len--)
25362306a36Sopenharmony_ci		hash = partial_name_hash(tolower(*name++), hash);
25462306a36Sopenharmony_ci	qstr->hash = end_name_hash(hash);
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_civoid clear_comrades_locked(struct list_head *comrade_list)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *cc, *nc;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	WARN_ON(!comrade_list);
26362306a36Sopenharmony_ci	list_for_each_entry_safe(cc, nc, comrade_list, list) {
26462306a36Sopenharmony_ci		dput(cc->lo_d);
26562306a36Sopenharmony_ci		kfree(cc);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci	INIT_LIST_HEAD(comrade_list);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_civoid clear_comrades(struct dentry *dentry)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *cdi = hmdfs_dm(dentry);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	wait_event(cdi->wait_queue, !has_merge_lookup_work(cdi));
27562306a36Sopenharmony_ci	mutex_lock(&cdi->comrade_list_lock);
27662306a36Sopenharmony_ci	clear_comrades_locked(&cdi->comrade_list);
27762306a36Sopenharmony_ci	mutex_unlock(&cdi->comrade_list_lock);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * d_revalidate_merge - revalidate a merge dentry
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * Always return 0 to invalidate a dentry for fault-tolerance.
28462306a36Sopenharmony_ci * The cost is acceptable for a overlay filesystem.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic int d_revalidate_merge(struct dentry *direntry, unsigned int flags)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(direntry);
28962306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
29062306a36Sopenharmony_ci	struct dentry *parent_dentry = NULL;
29162306a36Sopenharmony_ci	struct dentry *lower_cur_parent_dentry = NULL;
29262306a36Sopenharmony_ci	struct inode *dinode = NULL;
29362306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
29462306a36Sopenharmony_ci	int ret = 1;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (flags & LOOKUP_RCU) {
29762306a36Sopenharmony_ci		return -ECHILD;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL)) {
30162306a36Sopenharmony_ci		return 0;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	dinode = d_inode(direntry);
30562306a36Sopenharmony_ci	if (!dinode)
30662306a36Sopenharmony_ci		return 0;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	info = hmdfs_i(dinode);
30962306a36Sopenharmony_ci	if (info->inode_type == HMDFS_LAYER_FIRST_MERGE_CLOUD)
31062306a36Sopenharmony_ci		return 1;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	parent_dentry = dget_parent(direntry);
31362306a36Sopenharmony_ci        mutex_lock(&dim->comrade_list_lock);
31462306a36Sopenharmony_ci	list_for_each_entry(comrade, &(dim->comrade_list), list) {
31562306a36Sopenharmony_ci		lower_cur_parent_dentry = dget_parent(comrade->lo_d);
31662306a36Sopenharmony_ci		if ((comrade->lo_d->d_flags & DCACHE_OP_REVALIDATE)) {
31762306a36Sopenharmony_ci			ret = comrade->lo_d->d_op->d_revalidate(
31862306a36Sopenharmony_ci				comrade->lo_d, flags);
31962306a36Sopenharmony_ci			if (ret == 0) {
32062306a36Sopenharmony_ci				dput(lower_cur_parent_dentry);
32162306a36Sopenharmony_ci				goto out;
32262306a36Sopenharmony_ci			}
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		dput(lower_cur_parent_dentry);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ciout:
32762306a36Sopenharmony_ci        mutex_unlock(&dim->comrade_list_lock);
32862306a36Sopenharmony_ci	dput(parent_dentry);
32962306a36Sopenharmony_ci	return ret;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void d_release_merge(struct dentry *dentry)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	if (!dentry || !dentry->d_fsdata)
33562306a36Sopenharmony_ci		return;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	clear_comrades(dentry);
33862306a36Sopenharmony_ci	kmem_cache_free(hmdfs_dentry_merge_cachep, dentry->d_fsdata);
33962306a36Sopenharmony_ci	dentry->d_fsdata = NULL;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciconst struct dentry_operations hmdfs_dops_merge = {
34362306a36Sopenharmony_ci	.d_revalidate = d_revalidate_merge,
34462306a36Sopenharmony_ci	.d_release = d_release_merge,
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciconst struct dentry_operations hmdfs_dev_dops = {
34862306a36Sopenharmony_ci	.d_revalidate = hmdfs_dev_d_revalidate,
34962306a36Sopenharmony_ci	.d_release = hmdfs_dev_d_release,
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciconst struct dentry_operations hmdfs_dops = {
35362306a36Sopenharmony_ci	.d_revalidate = hmdfs_d_revalidate,
35462306a36Sopenharmony_ci	.d_release = hmdfs_d_release,
35562306a36Sopenharmony_ci	.d_compare = hmdfs_cmp_ci,
35662306a36Sopenharmony_ci	.d_hash = hmdfs_hash_ci,
35762306a36Sopenharmony_ci};
358