xref: /kernel/linux/linux-6.6/fs/hmdfs/inode_merge.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/hmdfs/inode_merge.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "hmdfs_merge_view.h"
962306a36Sopenharmony_ci#include <linux/atomic.h>
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/fs_stack.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/mount.h>
1562306a36Sopenharmony_ci#include <linux/namei.h>
1662306a36Sopenharmony_ci#include <linux/rwsem.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include "authority/authentication.h"
2062306a36Sopenharmony_ci#include "hmdfs_trace.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct kmem_cache *hmdfs_dentry_merge_cachep;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct dentry *hmdfs_get_fst_lo_d(struct dentry *dentry)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(dentry);
2762306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
2862306a36Sopenharmony_ci	struct dentry *d = NULL;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	mutex_lock(&dim->comrade_list_lock);
3162306a36Sopenharmony_ci	comrade = list_first_entry_or_null(&dim->comrade_list,
3262306a36Sopenharmony_ci					   struct hmdfs_dentry_comrade, list);
3362306a36Sopenharmony_ci	if (comrade)
3462306a36Sopenharmony_ci		d = dget(comrade->lo_d);
3562306a36Sopenharmony_ci	mutex_unlock(&dim->comrade_list_lock);
3662306a36Sopenharmony_ci	return d;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct dentry *hmdfs_get_lo_d(struct dentry *dentry, int dev_id)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(dentry);
4262306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
4362306a36Sopenharmony_ci	struct dentry *d = NULL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	mutex_lock(&dim->comrade_list_lock);
4662306a36Sopenharmony_ci	list_for_each_entry(comrade, &dim->comrade_list, list) {
4762306a36Sopenharmony_ci		if (comrade->dev_id == dev_id) {
4862306a36Sopenharmony_ci			d = dget(comrade->lo_d);
4962306a36Sopenharmony_ci			break;
5062306a36Sopenharmony_ci		}
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	mutex_unlock(&dim->comrade_list_lock);
5362306a36Sopenharmony_ci	return d;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_civoid update_inode_attr(struct inode *inode, struct dentry *child_dentry)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct inode *li = NULL;
5962306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *cdi = hmdfs_dm(child_dentry);
6062306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
6162306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *fst_comrade = NULL;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	mutex_lock(&cdi->comrade_list_lock);
6462306a36Sopenharmony_ci	fst_comrade = list_first_entry(&cdi->comrade_list,
6562306a36Sopenharmony_ci				       struct hmdfs_dentry_comrade, list);
6662306a36Sopenharmony_ci	list_for_each_entry(comrade, &cdi->comrade_list, list) {
6762306a36Sopenharmony_ci		li = d_inode(comrade->lo_d);
6862306a36Sopenharmony_ci		if (!li)
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (comrade == fst_comrade) {
7262306a36Sopenharmony_ci			inode->i_atime = li->i_atime;
7362306a36Sopenharmony_ci			inode->__i_ctime = li->__i_ctime;
7462306a36Sopenharmony_ci			inode->i_mtime = li->i_mtime;
7562306a36Sopenharmony_ci			inode->i_size = li->i_size;
7662306a36Sopenharmony_ci			continue;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (hmdfs_time_compare(&inode->i_mtime, &li->i_mtime) < 0)
8062306a36Sopenharmony_ci			inode->i_mtime = li->i_mtime;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	mutex_unlock(&cdi->comrade_list_lock);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciint get_num_comrades(struct dentry *dentry)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct list_head *pos;
8862306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(dentry);
8962306a36Sopenharmony_ci	int count = 0;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	mutex_lock(&dim->comrade_list_lock);
9262306a36Sopenharmony_ci	list_for_each(pos, &dim->comrade_list)
9362306a36Sopenharmony_ci		count++;
9462306a36Sopenharmony_ci	mutex_unlock(&dim->comrade_list_lock);
9562306a36Sopenharmony_ci	return count;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct inode *fill_inode_merge(struct super_block *sb,
9962306a36Sopenharmony_ci				      struct inode *parent_inode,
10062306a36Sopenharmony_ci				      struct dentry *child_dentry,
10162306a36Sopenharmony_ci				      struct dentry *lo_d_dentry)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int ret = 0;
10462306a36Sopenharmony_ci	struct dentry *fst_lo_d = NULL;
10562306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
10662306a36Sopenharmony_ci	struct inode *inode = NULL;
10762306a36Sopenharmony_ci	umode_t mode;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (lo_d_dentry) {
11062306a36Sopenharmony_ci		fst_lo_d = lo_d_dentry;
11162306a36Sopenharmony_ci		dget(fst_lo_d);
11262306a36Sopenharmony_ci	} else {
11362306a36Sopenharmony_ci		fst_lo_d = hmdfs_get_fst_lo_d(child_dentry);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	if (!fst_lo_d) {
11662306a36Sopenharmony_ci		inode = ERR_PTR(-EINVAL);
11762306a36Sopenharmony_ci		goto out;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	if (hmdfs_i(parent_inode)->inode_type == HMDFS_LAYER_ZERO)
12062306a36Sopenharmony_ci		inode = hmdfs_iget_locked_root(sb, HMDFS_ROOT_MERGE, NULL,
12162306a36Sopenharmony_ci					       NULL);
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		inode = hmdfs_iget5_locked_merge(sb, fst_lo_d);
12462306a36Sopenharmony_ci	if (!inode) {
12562306a36Sopenharmony_ci		hmdfs_err("iget5_locked get inode NULL");
12662306a36Sopenharmony_ci		inode = ERR_PTR(-ENOMEM);
12762306a36Sopenharmony_ci		goto out;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	if (!(inode->i_state & I_NEW))
13062306a36Sopenharmony_ci		goto out;
13162306a36Sopenharmony_ci	info = hmdfs_i(inode);
13262306a36Sopenharmony_ci	if (hmdfs_i(parent_inode)->inode_type == HMDFS_LAYER_ZERO)
13362306a36Sopenharmony_ci		info->inode_type = HMDFS_LAYER_FIRST_MERGE;
13462306a36Sopenharmony_ci	else
13562306a36Sopenharmony_ci		info->inode_type = HMDFS_LAYER_OTHER_MERGE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	inode->i_uid = KUIDT_INIT((uid_t)1000);
13862306a36Sopenharmony_ci	inode->i_gid = KGIDT_INIT((gid_t)1000);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	update_inode_attr(inode, child_dentry);
14162306a36Sopenharmony_ci	mode = d_inode(fst_lo_d)->i_mode;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (S_ISREG(mode)) {
14462306a36Sopenharmony_ci		inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
14562306a36Sopenharmony_ci		inode->i_op = &hmdfs_file_iops_merge;
14662306a36Sopenharmony_ci		inode->i_fop = &hmdfs_file_fops_merge;
14762306a36Sopenharmony_ci		set_nlink(inode, 1);
14862306a36Sopenharmony_ci	} else if (S_ISDIR(mode)) {
14962306a36Sopenharmony_ci		inode->i_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IXOTH;
15062306a36Sopenharmony_ci		inode->i_op = &hmdfs_dir_iops_merge;
15162306a36Sopenharmony_ci		inode->i_fop = &hmdfs_dir_fops_merge;
15262306a36Sopenharmony_ci		set_nlink(inode, get_num_comrades(child_dentry) + 2);
15362306a36Sopenharmony_ci	} else {
15462306a36Sopenharmony_ci		ret = -EIO;
15562306a36Sopenharmony_ci		goto bad_inode;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	unlock_new_inode(inode);
15962306a36Sopenharmony_ciout:
16062306a36Sopenharmony_ci	dput(fst_lo_d);
16162306a36Sopenharmony_ci	return inode;
16262306a36Sopenharmony_cibad_inode:
16362306a36Sopenharmony_ci	iget_failed(inode);
16462306a36Sopenharmony_ci	return ERR_PTR(ret);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistruct hmdfs_dentry_comrade *alloc_comrade(struct dentry *lo_d, int dev_id)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	// 文件只有一个 comrade,考虑 {comrade, list + list lock}
17262306a36Sopenharmony_ci	comrade = kzalloc(sizeof(*comrade), GFP_KERNEL);
17362306a36Sopenharmony_ci	if (unlikely(!comrade))
17462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	comrade->lo_d = lo_d;
17762306a36Sopenharmony_ci	comrade->dev_id = dev_id;
17862306a36Sopenharmony_ci	dget(lo_d);
17962306a36Sopenharmony_ci	return comrade;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_civoid link_comrade(struct list_head *onstack_comrades_head,
18362306a36Sopenharmony_ci		  struct hmdfs_dentry_comrade *comrade)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *c = NULL;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	list_for_each_entry(c, onstack_comrades_head, list) {
18862306a36Sopenharmony_ci		if (likely(c->dev_id != comrade->dev_id))
18962306a36Sopenharmony_ci			continue;
19062306a36Sopenharmony_ci		hmdfs_err("Redundant comrade of device %llu", c->dev_id);
19162306a36Sopenharmony_ci		dput(comrade->lo_d);
19262306a36Sopenharmony_ci		kfree(comrade);
19362306a36Sopenharmony_ci		WARN_ON(1);
19462306a36Sopenharmony_ci		return;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (comrade_is_local(comrade))
19862306a36Sopenharmony_ci		list_add(&comrade->list, onstack_comrades_head);
19962306a36Sopenharmony_ci	else
20062306a36Sopenharmony_ci		list_add_tail(&comrade->list, onstack_comrades_head);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/**
20462306a36Sopenharmony_ci * assign_comrades_unlocked - assign a child dentry with comrades
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * We tend to setup a local list of all the comrades we found and place the
20762306a36Sopenharmony_ci * list onto the dentry_info to achieve atomicity.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_civoid assign_comrades_unlocked(struct dentry *child_dentry,
21062306a36Sopenharmony_ci			      struct list_head *onstack_comrades_head)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *cdi = hmdfs_dm(child_dentry);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mutex_lock(&cdi->comrade_list_lock);
21562306a36Sopenharmony_ci	WARN_ON(!list_empty(&cdi->comrade_list));
21662306a36Sopenharmony_ci	list_splice_init(onstack_comrades_head, &cdi->comrade_list);
21762306a36Sopenharmony_ci	mutex_unlock(&cdi->comrade_list_lock);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistruct hmdfs_dentry_comrade *lookup_comrade(struct path lower_path,
22162306a36Sopenharmony_ci					    const char *d_name,
22262306a36Sopenharmony_ci					    int dev_id,
22362306a36Sopenharmony_ci					    unsigned int flags)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct path path;
22662306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
22762306a36Sopenharmony_ci	int err;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	err = vfs_path_lookup(lower_path.dentry, lower_path.mnt, d_name, flags,
23062306a36Sopenharmony_ci			      &path);
23162306a36Sopenharmony_ci	if (err)
23262306a36Sopenharmony_ci		return ERR_PTR(err);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	comrade = alloc_comrade(path.dentry, dev_id);
23562306a36Sopenharmony_ci	path_put(&path);
23662306a36Sopenharmony_ci	return comrade;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/**
24062306a36Sopenharmony_ci * conf_name_trans_nop - do nothing but copy
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * WARNING: always check before translation
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_cistatic char *conf_name_trans_nop(struct dentry *d)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	return kstrndup(d->d_name.name, d->d_name.len, GFP_KERNEL);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/**
25062306a36Sopenharmony_ci * conf_name_trans_dir - conflicted name translation for directory
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * WARNING: always check before translation
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic char *conf_name_trans_dir(struct dentry *d)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int len = d->d_name.len - strlen(CONFLICTING_DIR_SUFFIX);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return kstrndup(d->d_name.name, len, GFP_KERNEL);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/**
26262306a36Sopenharmony_ci * conf_name_trans_reg - conflicted name translation for regular file
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci * WARNING: always check before translation
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_cistatic char *conf_name_trans_reg(struct dentry *d, int *dev_id)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int dot_pos, start_cpy_pos, num_len, i;
26962306a36Sopenharmony_ci	int len = d->d_name.len;
27062306a36Sopenharmony_ci	char *name = kstrndup(d->d_name.name, d->d_name.len, GFP_KERNEL);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (unlikely(!name))
27362306a36Sopenharmony_ci		return NULL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	// find the last dot if possible
27662306a36Sopenharmony_ci	for (dot_pos = len - 1; dot_pos >= 0; dot_pos--) {
27762306a36Sopenharmony_ci		if (name[dot_pos] == '.')
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	if (dot_pos == -1)
28162306a36Sopenharmony_ci		dot_pos = len;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	// retrieve the conf sn (i.e. dev_id)
28462306a36Sopenharmony_ci	num_len = 0;
28562306a36Sopenharmony_ci	for (i = dot_pos - 1; i >= 0; i--) {
28662306a36Sopenharmony_ci		if (name[i] >= '0' && name[i] <= '9')
28762306a36Sopenharmony_ci			num_len++;
28862306a36Sopenharmony_ci		else
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	*dev_id = 0;
29362306a36Sopenharmony_ci	for (i = 0; i < num_len; i++)
29462306a36Sopenharmony_ci		*dev_id = *dev_id * 10 + name[dot_pos - num_len + i] - '0';
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	// move the file suffix( '\0' included) right after the file name
29762306a36Sopenharmony_ci	start_cpy_pos =
29862306a36Sopenharmony_ci		dot_pos - num_len - strlen(CONFLICTING_FILE_CONST_SUFFIX);
29962306a36Sopenharmony_ci	memmove(name + start_cpy_pos, name + dot_pos, len - dot_pos + 1);
30062306a36Sopenharmony_ci	return name;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciint check_filename(const char *name, int len)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int cmp_res = 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (len >= strlen(CONFLICTING_DIR_SUFFIX)) {
30862306a36Sopenharmony_ci		cmp_res = strncmp(name + len - strlen(CONFLICTING_DIR_SUFFIX),
30962306a36Sopenharmony_ci				  CONFLICTING_DIR_SUFFIX,
31062306a36Sopenharmony_ci				  strlen(CONFLICTING_DIR_SUFFIX));
31162306a36Sopenharmony_ci		if (cmp_res == 0)
31262306a36Sopenharmony_ci			return DT_DIR;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (len >= strlen(CONFLICTING_FILE_CONST_SUFFIX)) {
31662306a36Sopenharmony_ci		int dot_pos, start_cmp_pos, num_len, i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		for (dot_pos = len - 1; dot_pos >= 0; dot_pos--) {
31962306a36Sopenharmony_ci			if (name[dot_pos] == '.')
32062306a36Sopenharmony_ci				break;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci		if (dot_pos == -1)
32362306a36Sopenharmony_ci			dot_pos = len;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		num_len = 0;
32662306a36Sopenharmony_ci		for (i = dot_pos - 1; i >= 0; i--) {
32762306a36Sopenharmony_ci			if (name[i] >= '0' && name[i] <= '9')
32862306a36Sopenharmony_ci				num_len++;
32962306a36Sopenharmony_ci			else
33062306a36Sopenharmony_ci				break;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		start_cmp_pos = dot_pos - num_len -
33462306a36Sopenharmony_ci				strlen(CONFLICTING_FILE_CONST_SUFFIX);
33562306a36Sopenharmony_ci		cmp_res = strncmp(name + start_cmp_pos,
33662306a36Sopenharmony_ci				  CONFLICTING_FILE_CONST_SUFFIX,
33762306a36Sopenharmony_ci				  strlen(CONFLICTING_FILE_CONST_SUFFIX));
33862306a36Sopenharmony_ci		if (cmp_res == 0)
33962306a36Sopenharmony_ci			return DT_REG;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic struct hmdfs_dentry_comrade *merge_lookup_comrade(
34662306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi, const char *name, int devid,
34762306a36Sopenharmony_ci	unsigned int flags)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci	struct path root, path;
35162306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
35262306a36Sopenharmony_ci	const struct cred *old_cred = hmdfs_override_creds(sbi->cred);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	err = kern_path(sbi->real_dst, LOOKUP_DIRECTORY, &root);
35562306a36Sopenharmony_ci	if (err) {
35662306a36Sopenharmony_ci		comrade = ERR_PTR(err);
35762306a36Sopenharmony_ci		goto out;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	err = vfs_path_lookup(root.dentry, root.mnt, name, flags, &path);
36162306a36Sopenharmony_ci	if (err) {
36262306a36Sopenharmony_ci		comrade = ERR_PTR(err);
36362306a36Sopenharmony_ci		goto root_put;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	comrade = alloc_comrade(path.dentry, devid);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	path_put(&path);
36962306a36Sopenharmony_ciroot_put:
37062306a36Sopenharmony_ci	path_put(&root);
37162306a36Sopenharmony_ciout:
37262306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
37362306a36Sopenharmony_ci	return comrade;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cibool is_valid_comrade(struct hmdfs_dentry_info_merge *mdi, umode_t mode)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	if (mdi->type == DT_UNKNOWN) {
37962306a36Sopenharmony_ci		mdi->type = S_ISDIR(mode) ? DT_DIR : DT_REG;
38062306a36Sopenharmony_ci		return true;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (mdi->type == DT_DIR && S_ISDIR(mode)) {
38462306a36Sopenharmony_ci		return true;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (mdi->type == DT_REG && list_empty(&mdi->comrade_list) &&
38862306a36Sopenharmony_ci		!S_ISDIR(mode)) {
38962306a36Sopenharmony_ci		return true;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return false;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void merge_lookup_work_func(struct work_struct *work)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct merge_lookup_work *ml_work;
39862306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade;
39962306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *mdi;
40062306a36Sopenharmony_ci	int found = false;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ml_work = container_of(work, struct merge_lookup_work, work);
40362306a36Sopenharmony_ci	mdi = container_of(ml_work->wait_queue,	struct hmdfs_dentry_info_merge,
40462306a36Sopenharmony_ci		wait_queue);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	trace_hmdfs_merge_lookup_work_enter(ml_work);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	comrade = merge_lookup_comrade(ml_work->sbi, ml_work->name,
40962306a36Sopenharmony_ci		ml_work->devid, ml_work->flags);
41062306a36Sopenharmony_ci	if (IS_ERR(comrade)) {
41162306a36Sopenharmony_ci		mutex_lock(&mdi->work_lock);
41262306a36Sopenharmony_ci		goto out;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	mutex_lock(&mdi->work_lock);
41662306a36Sopenharmony_ci	mutex_lock(&mdi->comrade_list_lock);
41762306a36Sopenharmony_ci	if (!is_valid_comrade(mdi, hmdfs_cm(comrade))) {
41862306a36Sopenharmony_ci		destroy_comrade(comrade);
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		found = true;
42162306a36Sopenharmony_ci		link_comrade(&mdi->comrade_list, comrade);
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	mutex_unlock(&mdi->comrade_list_lock);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ciout:
42662306a36Sopenharmony_ci	if (--mdi->work_count == 0 || found)
42762306a36Sopenharmony_ci		wake_up_all(ml_work->wait_queue);
42862306a36Sopenharmony_ci	mutex_unlock(&mdi->work_lock);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	trace_hmdfs_merge_lookup_work_exit(ml_work, found);
43162306a36Sopenharmony_ci	kfree(ml_work->name);
43262306a36Sopenharmony_ci	kfree(ml_work);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ciint merge_lookup_async(struct hmdfs_dentry_info_merge *mdi,
43662306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi, int devid, const char *name,
43762306a36Sopenharmony_ci	unsigned int flags)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int err = -ENOMEM;
44062306a36Sopenharmony_ci	struct merge_lookup_work *ml_work;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	ml_work = kmalloc(sizeof(*ml_work), GFP_KERNEL);
44362306a36Sopenharmony_ci	if (!ml_work)
44462306a36Sopenharmony_ci		goto out;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	ml_work->name = kstrdup(name, GFP_KERNEL);
44762306a36Sopenharmony_ci	if (!ml_work->name) {
44862306a36Sopenharmony_ci		kfree(ml_work);
44962306a36Sopenharmony_ci		goto out;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	ml_work->devid = devid;
45362306a36Sopenharmony_ci	ml_work->flags = flags;
45462306a36Sopenharmony_ci	ml_work->sbi = sbi;
45562306a36Sopenharmony_ci	ml_work->wait_queue = &mdi->wait_queue;
45662306a36Sopenharmony_ci	INIT_WORK(&ml_work->work, merge_lookup_work_func);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	schedule_work(&ml_work->work);
45962306a36Sopenharmony_ci	++mdi->work_count;
46062306a36Sopenharmony_ci	err = 0;
46162306a36Sopenharmony_ciout:
46262306a36Sopenharmony_ci	return err;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cichar *hmdfs_get_real_dname(struct dentry *dentry, int *devid, int *type)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	char *rname;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	*type = check_filename(dentry->d_name.name, dentry->d_name.len);
47062306a36Sopenharmony_ci	if (*type == DT_REG)
47162306a36Sopenharmony_ci		rname = conf_name_trans_reg(dentry, devid);
47262306a36Sopenharmony_ci	else if (*type == DT_DIR)
47362306a36Sopenharmony_ci		rname = conf_name_trans_dir(dentry);
47462306a36Sopenharmony_ci	else
47562306a36Sopenharmony_ci		rname = conf_name_trans_nop(dentry);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return rname;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int lookup_merge_normal(struct dentry *dentry, unsigned int flags)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	int ret = -ENOMEM;
48362306a36Sopenharmony_ci	int err = 0;
48462306a36Sopenharmony_ci	int devid = -1;
48562306a36Sopenharmony_ci	struct dentry *pdentry = dget_parent(dentry);
48662306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *mdi = hmdfs_dm(dentry);
48762306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
48862306a36Sopenharmony_ci	struct hmdfs_peer *peer;
48962306a36Sopenharmony_ci	char *rname, *ppath, *cpath;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	rname = hmdfs_get_real_dname(dentry, &devid, &mdi->type);
49262306a36Sopenharmony_ci	if (unlikely(!rname)) {
49362306a36Sopenharmony_ci		goto out;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ppath = hmdfs_merge_get_dentry_relative_path(pdentry);
49762306a36Sopenharmony_ci	if (unlikely(!ppath)) {
49862306a36Sopenharmony_ci		hmdfs_err("failed to get parent relative path");
49962306a36Sopenharmony_ci		goto out_rname;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	cpath = kzalloc(PATH_MAX, GFP_KERNEL);
50362306a36Sopenharmony_ci	if (unlikely(!cpath)) {
50462306a36Sopenharmony_ci		hmdfs_err("failed to get child device_view path");
50562306a36Sopenharmony_ci		goto out_ppath;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	mutex_lock(&mdi->work_lock);
50962306a36Sopenharmony_ci	mutex_lock(&sbi->connections.node_lock);
51062306a36Sopenharmony_ci	if (mdi->type != DT_REG || devid == 0) {
51162306a36Sopenharmony_ci		snprintf(cpath, PATH_MAX, "device_view/local%s/%s", ppath,
51262306a36Sopenharmony_ci			rname);
51362306a36Sopenharmony_ci		err = merge_lookup_async(mdi, sbi, 0, cpath, flags);
51462306a36Sopenharmony_ci		if (err)
51562306a36Sopenharmony_ci			hmdfs_err("failed to create local lookup work");
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	list_for_each_entry(peer, &sbi->connections.node_list, list) {
51962306a36Sopenharmony_ci		if (mdi->type == DT_REG && peer->device_id != devid)
52062306a36Sopenharmony_ci			continue;
52162306a36Sopenharmony_ci		snprintf(cpath, PATH_MAX, "device_view/%s%s/%s", peer->cid,
52262306a36Sopenharmony_ci			ppath, rname);
52362306a36Sopenharmony_ci		err = merge_lookup_async(mdi, sbi, peer->device_id, cpath,
52462306a36Sopenharmony_ci			flags);
52562306a36Sopenharmony_ci		if (err)
52662306a36Sopenharmony_ci			hmdfs_err("failed to create remote lookup work");
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	mutex_unlock(&sbi->connections.node_lock);
52962306a36Sopenharmony_ci	mutex_unlock(&mdi->work_lock);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	wait_event(mdi->wait_queue, is_merge_lookup_end(mdi));
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	ret = -ENOENT;
53462306a36Sopenharmony_ci	if (!is_comrade_list_empty(mdi))
53562306a36Sopenharmony_ci		ret = 0;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	kfree(cpath);
53862306a36Sopenharmony_ciout_ppath:
53962306a36Sopenharmony_ci	kfree(ppath);
54062306a36Sopenharmony_ciout_rname:
54162306a36Sopenharmony_ci	kfree(rname);
54262306a36Sopenharmony_ciout:
54362306a36Sopenharmony_ci	dput(pdentry);
54462306a36Sopenharmony_ci	return ret;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/**
54862306a36Sopenharmony_ci * do_lookup_merge_root - lookup the root of the merge view(root/merge_view)
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * It's common for a network filesystem to incur various of faults, so we
55162306a36Sopenharmony_ci * intent to show mercy for faults here, except faults reported by the local.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int do_lookup_merge_root(struct path path_dev,
55462306a36Sopenharmony_ci				struct dentry *child_dentry, unsigned int flags)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(child_dentry->d_sb);
55762306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade;
55862306a36Sopenharmony_ci	const int buf_len =
55962306a36Sopenharmony_ci		max((int)HMDFS_CID_SIZE + 1, (int)sizeof(DEVICE_VIEW_LOCAL));
56062306a36Sopenharmony_ci	char *buf = kzalloc(buf_len, GFP_KERNEL);
56162306a36Sopenharmony_ci	struct hmdfs_peer *peer;
56262306a36Sopenharmony_ci	LIST_HEAD(head);
56362306a36Sopenharmony_ci	int ret;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (!buf)
56662306a36Sopenharmony_ci		return -ENOMEM;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	// lookup real_dst/device_view/local
56962306a36Sopenharmony_ci	memcpy(buf, DEVICE_VIEW_LOCAL, sizeof(DEVICE_VIEW_LOCAL));
57062306a36Sopenharmony_ci	comrade = lookup_comrade(path_dev, buf, HMDFS_DEVID_LOCAL, flags);
57162306a36Sopenharmony_ci	if (IS_ERR(comrade)) {
57262306a36Sopenharmony_ci		ret = PTR_ERR(comrade);
57362306a36Sopenharmony_ci		goto out;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	link_comrade(&head, comrade);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	// lookup real_dst/device_view/cidxx
57862306a36Sopenharmony_ci	mutex_lock(&sbi->connections.node_lock);
57962306a36Sopenharmony_ci	list_for_each_entry(peer, &sbi->connections.node_list, list) {
58062306a36Sopenharmony_ci		mutex_unlock(&sbi->connections.node_lock);
58162306a36Sopenharmony_ci		memcpy(buf, peer->cid, HMDFS_CID_SIZE);
58262306a36Sopenharmony_ci		comrade = lookup_comrade(path_dev, buf, peer->device_id, flags);
58362306a36Sopenharmony_ci		if (IS_ERR(comrade))
58462306a36Sopenharmony_ci			continue;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		link_comrade(&head, comrade);
58762306a36Sopenharmony_ci		mutex_lock(&sbi->connections.node_lock);
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	mutex_unlock(&sbi->connections.node_lock);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	assign_comrades_unlocked(child_dentry, &head);
59262306a36Sopenharmony_ci	ret = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ciout:
59562306a36Sopenharmony_ci	kfree(buf);
59662306a36Sopenharmony_ci	return ret;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci// mkdir -p
60062306a36Sopenharmony_civoid lock_root_inode_shared(struct inode *root, bool *locked, bool *down)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct rw_semaphore *sem = &root->i_rwsem;
60362306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
60462306a36Sopenharmony_ci#define RWSEM_READER_OWNED     (1UL << 0)
60562306a36Sopenharmony_ci#define RWSEM_RD_NONSPINNABLE  (1UL << 1)
60662306a36Sopenharmony_ci#define RWSEM_WR_NONSPINNABLE  (1UL << 2)
60762306a36Sopenharmony_ci#define RWSEM_NONSPINNABLE     (RWSEM_RD_NONSPINNABLE | RWSEM_WR_NONSPINNABLE)
60862306a36Sopenharmony_ci#define RWSEM_OWNER_FLAGS_MASK (RWSEM_READER_OWNED | RWSEM_NONSPINNABLE)
60962306a36Sopenharmony_ci	struct task_struct *sem_owner =
61062306a36Sopenharmony_ci		(struct task_struct *)(atomic_long_read(&sem->owner) &
61162306a36Sopenharmony_ci				       ~RWSEM_OWNER_FLAGS_MASK);
61262306a36Sopenharmony_ci#else
61362306a36Sopenharmony_ci	struct task_struct *sem_owner = sem->owner;
61462306a36Sopenharmony_ci#endif
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	*locked = false;
61762306a36Sopenharmony_ci	*down = false;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (sem_owner != current)
62062306a36Sopenharmony_ci		return;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	// It's us that takes the wsem
62362306a36Sopenharmony_ci	if (!inode_trylock_shared(root)) {
62462306a36Sopenharmony_ci		downgrade_write(sem);
62562306a36Sopenharmony_ci		*down = true;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	*locked = true;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_civoid restore_root_inode_sem(struct inode *root, bool locked, bool down)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	if (!locked)
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	inode_unlock_shared(root);
63662306a36Sopenharmony_ci	if (down)
63762306a36Sopenharmony_ci		inode_lock(root);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int lookup_merge_root(struct inode *root_inode,
64162306a36Sopenharmony_ci			     struct dentry *child_dentry, unsigned int flags)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(child_dentry->d_sb);
64462306a36Sopenharmony_ci	struct path path_dev;
64562306a36Sopenharmony_ci	int ret = -ENOENT;
64662306a36Sopenharmony_ci	int buf_len;
64762306a36Sopenharmony_ci	char *buf = NULL;
64862306a36Sopenharmony_ci	bool locked, down;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	// consider additional one slash and one '\0'
65162306a36Sopenharmony_ci	buf_len = strlen(sbi->real_dst) + 1 + sizeof(DEVICE_VIEW_ROOT);
65262306a36Sopenharmony_ci	if (buf_len > PATH_MAX)
65362306a36Sopenharmony_ci		return -ENAMETOOLONG;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	buf = kmalloc(buf_len, GFP_KERNEL);
65662306a36Sopenharmony_ci	if (unlikely(!buf))
65762306a36Sopenharmony_ci		return -ENOMEM;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	sprintf(buf, "%s/%s", sbi->real_dst, DEVICE_VIEW_ROOT);
66062306a36Sopenharmony_ci	lock_root_inode_shared(root_inode, &locked, &down);
66162306a36Sopenharmony_ci	ret = hmdfs_get_path_in_sb(child_dentry->d_sb, buf, LOOKUP_DIRECTORY,
66262306a36Sopenharmony_ci				   &path_dev);
66362306a36Sopenharmony_ci	if (ret)
66462306a36Sopenharmony_ci		goto free_buf;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	ret = do_lookup_merge_root(path_dev, child_dentry, flags);
66762306a36Sopenharmony_ci	path_put(&path_dev);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cifree_buf:
67062306a36Sopenharmony_ci	kfree(buf);
67162306a36Sopenharmony_ci	restore_root_inode_sem(root_inode, locked, down);
67262306a36Sopenharmony_ci	return ret;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ciint init_hmdfs_dentry_info_merge(struct hmdfs_sb_info *sbi,
67662306a36Sopenharmony_ci	struct dentry *dentry)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *mdi = NULL;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	mdi = kmem_cache_zalloc(hmdfs_dentry_merge_cachep, GFP_NOFS);
68162306a36Sopenharmony_ci	if (!mdi)
68262306a36Sopenharmony_ci		return -ENOMEM;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	mdi->ctime = jiffies;
68562306a36Sopenharmony_ci	mdi->type = DT_UNKNOWN;
68662306a36Sopenharmony_ci	mdi->work_count = 0;
68762306a36Sopenharmony_ci	mutex_init(&mdi->work_lock);
68862306a36Sopenharmony_ci	init_waitqueue_head(&mdi->wait_queue);
68962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mdi->comrade_list);
69062306a36Sopenharmony_ci	mutex_init(&mdi->comrade_list_lock);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	d_set_d_op(dentry, &hmdfs_dops_merge);
69362306a36Sopenharmony_ci	dentry->d_fsdata = mdi;
69462306a36Sopenharmony_ci	return 0;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci// do this in a map-reduce manner
69862306a36Sopenharmony_cistruct dentry *hmdfs_lookup_merge(struct inode *parent_inode,
69962306a36Sopenharmony_ci				  struct dentry *child_dentry,
70062306a36Sopenharmony_ci				  unsigned int flags)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	bool create = flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET);
70362306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = hmdfs_sb(child_dentry->d_sb);
70462306a36Sopenharmony_ci	struct hmdfs_inode_info *pii = hmdfs_i(parent_inode);
70562306a36Sopenharmony_ci	struct inode *child_inode = NULL;
70662306a36Sopenharmony_ci	struct dentry *ret_dentry = NULL;
70762306a36Sopenharmony_ci	int err = 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/*
71062306a36Sopenharmony_ci	 * Internal flags like LOOKUP_CREATE should not pass to device view.
71162306a36Sopenharmony_ci	 * LOOKUP_REVAL is needed because dentry cache in hmdfs might be stale
71262306a36Sopenharmony_ci	 * after rename in lower fs. LOOKUP_DIRECTORY is not needed because
71362306a36Sopenharmony_ci	 * merge_view can do the judgement that whether result is directory or
71462306a36Sopenharmony_ci	 * not.
71562306a36Sopenharmony_ci	 */
71662306a36Sopenharmony_ci	flags = flags & LOOKUP_REVAL;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	child_dentry->d_fsdata = NULL;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (child_dentry->d_name.len > NAME_MAX) {
72162306a36Sopenharmony_ci		err = -ENAMETOOLONG;
72262306a36Sopenharmony_ci		goto out;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	err = init_hmdfs_dentry_info_merge(sbi, child_dentry);
72662306a36Sopenharmony_ci	if (unlikely(err))
72762306a36Sopenharmony_ci		goto out;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (pii->inode_type == HMDFS_LAYER_ZERO) {
73062306a36Sopenharmony_ci		hmdfs_dm(child_dentry)->dentry_type = HMDFS_LAYER_FIRST_MERGE;
73162306a36Sopenharmony_ci		err = lookup_merge_root(parent_inode, child_dentry, flags);
73262306a36Sopenharmony_ci	} else {
73362306a36Sopenharmony_ci		hmdfs_dm(child_dentry)->dentry_type = HMDFS_LAYER_OTHER_MERGE;
73462306a36Sopenharmony_ci		err = lookup_merge_normal(child_dentry, flags);
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (!err) {
73862306a36Sopenharmony_ci		struct hmdfs_inode_info *info = NULL;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		child_inode = fill_inode_merge(parent_inode->i_sb, parent_inode,
74162306a36Sopenharmony_ci					       child_dentry, NULL);
74262306a36Sopenharmony_ci		if (IS_ERR(child_inode)) {
74362306a36Sopenharmony_ci			err = PTR_ERR(child_inode);
74462306a36Sopenharmony_ci			goto out;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci		info = hmdfs_i(child_inode);
74762306a36Sopenharmony_ci		if (info->inode_type == HMDFS_LAYER_FIRST_MERGE)
74862306a36Sopenharmony_ci			hmdfs_root_inode_perm_init(child_inode);
74962306a36Sopenharmony_ci		else
75062306a36Sopenharmony_ci			check_and_fixup_ownership_remote(parent_inode,
75162306a36Sopenharmony_ci							 child_inode,
75262306a36Sopenharmony_ci							 child_dentry);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		ret_dentry = d_splice_alias(child_inode, child_dentry);
75562306a36Sopenharmony_ci		if (IS_ERR(ret_dentry)) {
75662306a36Sopenharmony_ci			clear_comrades(child_dentry);
75762306a36Sopenharmony_ci			err = PTR_ERR(ret_dentry);
75862306a36Sopenharmony_ci			goto out;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci		if (ret_dentry)
76162306a36Sopenharmony_ci			child_dentry = ret_dentry;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		goto out;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if ((err == -ENOENT) && create)
76762306a36Sopenharmony_ci		err = 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ciout:
77062306a36Sopenharmony_ci	return err ? ERR_PTR(err) : ret_dentry;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ciint hmdfs_getattr_merge(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat,
77462306a36Sopenharmony_ci			       u32 request_mask, unsigned int flags)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	int ret;
77762306a36Sopenharmony_ci	struct path lower_path = {
77862306a36Sopenharmony_ci		.dentry = hmdfs_get_fst_lo_d(path->dentry),
77962306a36Sopenharmony_ci		.mnt = path->mnt,
78062306a36Sopenharmony_ci	};
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (unlikely(!lower_path.dentry)) {
78362306a36Sopenharmony_ci		hmdfs_err("Fatal! No comrades");
78462306a36Sopenharmony_ci		ret = -EINVAL;
78562306a36Sopenharmony_ci		goto out;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = vfs_getattr(&lower_path, stat, request_mask, flags);
78962306a36Sopenharmony_ciout:
79062306a36Sopenharmony_ci	dput(lower_path.dentry);
79162306a36Sopenharmony_ci	return ret;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ciint hmdfs_setattr_merge(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *ia)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
79762306a36Sopenharmony_ci	struct dentry *lower_dentry = hmdfs_get_fst_lo_d(dentry);
79862306a36Sopenharmony_ci	struct inode *lower_inode = NULL;
79962306a36Sopenharmony_ci	struct iattr lower_ia;
80062306a36Sopenharmony_ci	unsigned int ia_valid = ia->ia_valid;
80162306a36Sopenharmony_ci	int err = 0;
80262306a36Sopenharmony_ci	kuid_t tmp_uid;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (!lower_dentry) {
80562306a36Sopenharmony_ci		WARN_ON(1);
80662306a36Sopenharmony_ci		err = -EINVAL;
80762306a36Sopenharmony_ci		goto out;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	lower_inode = d_inode(lower_dentry);
81162306a36Sopenharmony_ci	memcpy(&lower_ia, ia, sizeof(lower_ia));
81262306a36Sopenharmony_ci	if (ia_valid & ATTR_FILE)
81362306a36Sopenharmony_ci		lower_ia.ia_file = hmdfs_f(ia->ia_file)->lower_file;
81462306a36Sopenharmony_ci	lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	inode_lock(lower_inode);
81762306a36Sopenharmony_ci	tmp_uid = hmdfs_override_inode_uid(lower_inode);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	err = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL);
82062306a36Sopenharmony_ci	i_size_write(inode, i_size_read(lower_inode));
82162306a36Sopenharmony_ci	inode->i_atime = lower_inode->i_atime;
82262306a36Sopenharmony_ci	inode->i_mtime = lower_inode->i_mtime;
82362306a36Sopenharmony_ci	inode->__i_ctime = lower_inode->__i_ctime;
82462306a36Sopenharmony_ci	hmdfs_revert_inode_uid(lower_inode, tmp_uid);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	inode_unlock(lower_inode);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ciout:
82962306a36Sopenharmony_ci	dput(lower_dentry);
83062306a36Sopenharmony_ci	return err;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ciconst struct inode_operations hmdfs_file_iops_merge = {
83462306a36Sopenharmony_ci	.getattr = hmdfs_getattr_merge,
83562306a36Sopenharmony_ci	.setattr = hmdfs_setattr_merge,
83662306a36Sopenharmony_ci	.permission = hmdfs_permission,
83762306a36Sopenharmony_ci};
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ciint do_mkdir_merge(struct inode *parent_inode, struct dentry *child_dentry,
84062306a36Sopenharmony_ci		   umode_t mode, struct inode *lo_i_parent,
84162306a36Sopenharmony_ci		   struct dentry *lo_d_child)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	int ret = 0;
84462306a36Sopenharmony_ci	struct super_block *sb = parent_inode->i_sb;
84562306a36Sopenharmony_ci	struct inode *child_inode = NULL;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	ret = vfs_mkdir(&nop_mnt_idmap, lo_i_parent, lo_d_child, mode);
84862306a36Sopenharmony_ci	if (ret)
84962306a36Sopenharmony_ci		goto out;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	child_inode =
85262306a36Sopenharmony_ci		fill_inode_merge(sb, parent_inode, child_dentry, lo_d_child);
85362306a36Sopenharmony_ci	if (IS_ERR(child_inode)) {
85462306a36Sopenharmony_ci		ret = PTR_ERR(child_inode);
85562306a36Sopenharmony_ci		goto out;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci	check_and_fixup_ownership_remote(parent_inode, child_inode,
85862306a36Sopenharmony_ci					 child_dentry);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	d_add(child_dentry, child_inode);
86162306a36Sopenharmony_ci	/* nlink should be increased with the joining of children */
86262306a36Sopenharmony_ci	set_nlink(parent_inode, 2);
86362306a36Sopenharmony_ciout:
86462306a36Sopenharmony_ci	return ret;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ciint do_create_merge(struct inode *parent_inode, struct dentry *child_dentry,
86862306a36Sopenharmony_ci		    umode_t mode, bool want_excl, struct inode *lo_i_parent,
86962306a36Sopenharmony_ci		    struct dentry *lo_d_child)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	int ret = 0;
87262306a36Sopenharmony_ci	struct super_block *sb = parent_inode->i_sb;
87362306a36Sopenharmony_ci	struct inode *child_inode = NULL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ret = vfs_create(&nop_mnt_idmap, lo_i_parent, lo_d_child, mode, want_excl);
87662306a36Sopenharmony_ci	if (ret)
87762306a36Sopenharmony_ci		goto out;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	child_inode =
88062306a36Sopenharmony_ci		fill_inode_merge(sb, parent_inode, child_dentry, lo_d_child);
88162306a36Sopenharmony_ci	if (IS_ERR(child_inode)) {
88262306a36Sopenharmony_ci		ret = PTR_ERR(child_inode);
88362306a36Sopenharmony_ci		goto out;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci	check_and_fixup_ownership_remote(parent_inode, child_inode,
88662306a36Sopenharmony_ci					 child_dentry);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	d_add(child_dentry, child_inode);
88962306a36Sopenharmony_ci	/* nlink should be increased with the joining of children */
89062306a36Sopenharmony_ci	set_nlink(parent_inode, 2);
89162306a36Sopenharmony_ciout:
89262306a36Sopenharmony_ci	return ret;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ciint hmdfs_do_ops_merge(struct inode *i_parent, struct dentry *d_child,
89662306a36Sopenharmony_ci		       struct dentry *lo_d_child, struct path path,
89762306a36Sopenharmony_ci		       struct hmdfs_recursive_para *rec_op_para)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	int ret = 0;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (rec_op_para->is_last) {
90262306a36Sopenharmony_ci		switch (rec_op_para->opcode) {
90362306a36Sopenharmony_ci		case F_MKDIR_MERGE:
90462306a36Sopenharmony_ci			ret = do_mkdir_merge(i_parent, d_child,
90562306a36Sopenharmony_ci					     rec_op_para->mode,
90662306a36Sopenharmony_ci					     d_inode(path.dentry), lo_d_child);
90762306a36Sopenharmony_ci			break;
90862306a36Sopenharmony_ci		case F_CREATE_MERGE:
90962306a36Sopenharmony_ci			ret = do_create_merge(i_parent, d_child,
91062306a36Sopenharmony_ci					      rec_op_para->mode,
91162306a36Sopenharmony_ci					      rec_op_para->want_excl,
91262306a36Sopenharmony_ci					      d_inode(path.dentry), lo_d_child);
91362306a36Sopenharmony_ci			break;
91462306a36Sopenharmony_ci		default:
91562306a36Sopenharmony_ci			ret = -EINVAL;
91662306a36Sopenharmony_ci			break;
91762306a36Sopenharmony_ci		}
91862306a36Sopenharmony_ci	} else {
91962306a36Sopenharmony_ci		ret = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), lo_d_child,
92062306a36Sopenharmony_ci				rec_op_para->mode);
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci	if (ret)
92362306a36Sopenharmony_ci		hmdfs_err("vfs_ops failed, ops %d, err = %d",
92462306a36Sopenharmony_ci			  rec_op_para->opcode, ret);
92562306a36Sopenharmony_ci	return ret;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ciint hmdfs_create_lower_dentry(struct inode *i_parent, struct dentry *d_child,
92962306a36Sopenharmony_ci			      struct dentry *lo_d_parent, bool is_dir,
93062306a36Sopenharmony_ci			      struct hmdfs_recursive_para *rec_op_para)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = i_parent->i_sb->s_fs_info;
93362306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *new_comrade = NULL;
93462306a36Sopenharmony_ci	struct dentry *lo_d_child = NULL;
93562306a36Sopenharmony_ci	char *path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
93662306a36Sopenharmony_ci	char *absolute_path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
93762306a36Sopenharmony_ci	char *path_name = NULL;
93862306a36Sopenharmony_ci	struct path path = { .mnt = NULL, .dentry = NULL };
93962306a36Sopenharmony_ci	int ret = 0;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (unlikely(!path_buf || !absolute_path_buf)) {
94262306a36Sopenharmony_ci		ret = -ENOMEM;
94362306a36Sopenharmony_ci		goto out;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	path_name = dentry_path_raw(lo_d_parent, path_buf, PATH_MAX);
94762306a36Sopenharmony_ci	if (IS_ERR(path_name)) {
94862306a36Sopenharmony_ci		ret = PTR_ERR(path_name);
94962306a36Sopenharmony_ci		goto out;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci	if ((strlen(sbi->real_dst) + strlen(path_name) +
95262306a36Sopenharmony_ci	     strlen(d_child->d_name.name) + 2) > PATH_MAX) {
95362306a36Sopenharmony_ci		ret = -ENAMETOOLONG;
95462306a36Sopenharmony_ci		goto out;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	sprintf(absolute_path_buf, "%s%s/%s", sbi->real_dst, path_name,
95862306a36Sopenharmony_ci		d_child->d_name.name);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (is_dir)
96162306a36Sopenharmony_ci		lo_d_child = kern_path_create(AT_FDCWD, absolute_path_buf,
96262306a36Sopenharmony_ci					      &path, LOOKUP_DIRECTORY);
96362306a36Sopenharmony_ci	else
96462306a36Sopenharmony_ci		lo_d_child = kern_path_create(AT_FDCWD, absolute_path_buf,
96562306a36Sopenharmony_ci					      &path, 0);
96662306a36Sopenharmony_ci	if (IS_ERR(lo_d_child)) {
96762306a36Sopenharmony_ci		ret = PTR_ERR(lo_d_child);
96862306a36Sopenharmony_ci		goto out;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	// to ensure link_comrade after vfs_mkdir succeed
97162306a36Sopenharmony_ci	ret = hmdfs_do_ops_merge(i_parent, d_child, lo_d_child, path,
97262306a36Sopenharmony_ci				 rec_op_para);
97362306a36Sopenharmony_ci	if (ret)
97462306a36Sopenharmony_ci		goto out_put;
97562306a36Sopenharmony_ci	new_comrade = alloc_comrade(lo_d_child, HMDFS_DEVID_LOCAL);
97662306a36Sopenharmony_ci	if (IS_ERR(new_comrade)) {
97762306a36Sopenharmony_ci		ret = PTR_ERR(new_comrade);
97862306a36Sopenharmony_ci		goto out_put;
97962306a36Sopenharmony_ci	} else {
98062306a36Sopenharmony_ci		link_comrade_unlocked(d_child, new_comrade);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	update_inode_attr(d_inode(d_child), d_child);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ciout_put:
98662306a36Sopenharmony_ci	done_path_create(&path, lo_d_child);
98762306a36Sopenharmony_ciout:
98862306a36Sopenharmony_ci	kfree(absolute_path_buf);
98962306a36Sopenharmony_ci	kfree(path_buf);
99062306a36Sopenharmony_ci	return ret;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic int create_lo_d_parent_recur(struct dentry *d_parent,
99462306a36Sopenharmony_ci				    struct dentry *d_child, umode_t mode,
99562306a36Sopenharmony_ci				    struct hmdfs_recursive_para *rec_op_para)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct dentry *lo_d_parent, *d_pparent;
99862306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *pmdi = NULL;
99962306a36Sopenharmony_ci	int ret = 0;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	pmdi = hmdfs_dm(d_parent);
100262306a36Sopenharmony_ci	wait_event(pmdi->wait_queue, !has_merge_lookup_work(pmdi));
100362306a36Sopenharmony_ci	lo_d_parent = hmdfs_get_lo_d(d_parent, HMDFS_DEVID_LOCAL);
100462306a36Sopenharmony_ci	if (!lo_d_parent) {
100562306a36Sopenharmony_ci		d_pparent = dget_parent(d_parent);
100662306a36Sopenharmony_ci		ret = create_lo_d_parent_recur(d_pparent, d_parent,
100762306a36Sopenharmony_ci					       d_inode(d_parent)->i_mode,
100862306a36Sopenharmony_ci					       rec_op_para);
100962306a36Sopenharmony_ci		dput(d_pparent);
101062306a36Sopenharmony_ci		if (ret)
101162306a36Sopenharmony_ci			goto out;
101262306a36Sopenharmony_ci		lo_d_parent = hmdfs_get_lo_d(d_parent, HMDFS_DEVID_LOCAL);
101362306a36Sopenharmony_ci		if (!lo_d_parent) {
101462306a36Sopenharmony_ci			ret = -ENOENT;
101562306a36Sopenharmony_ci			goto out;
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci	rec_op_para->is_last = false;
101962306a36Sopenharmony_ci	rec_op_para->mode = mode;
102062306a36Sopenharmony_ci	ret = hmdfs_create_lower_dentry(d_inode(d_parent), d_child, lo_d_parent,
102162306a36Sopenharmony_ci					true, rec_op_para);
102262306a36Sopenharmony_ciout:
102362306a36Sopenharmony_ci	dput(lo_d_parent);
102462306a36Sopenharmony_ci	return ret;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ciint create_lo_d_child(struct inode *i_parent, struct dentry *d_child,
102862306a36Sopenharmony_ci		      bool is_dir, struct hmdfs_recursive_para *rec_op_para)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct dentry *d_pparent, *lo_d_parent, *lo_d_child;
103162306a36Sopenharmony_ci	struct dentry *d_parent = dget_parent(d_child);
103262306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *pmdi = hmdfs_dm(d_parent);
103362306a36Sopenharmony_ci	int ret = 0;
103462306a36Sopenharmony_ci	mode_t d_child_mode = rec_op_para->mode;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	wait_event(pmdi->wait_queue, !has_merge_lookup_work(pmdi));
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	lo_d_parent = hmdfs_get_lo_d(d_parent, HMDFS_DEVID_LOCAL);
103962306a36Sopenharmony_ci	if (!lo_d_parent) {
104062306a36Sopenharmony_ci		d_pparent = dget_parent(d_parent);
104162306a36Sopenharmony_ci		ret = create_lo_d_parent_recur(d_pparent, d_parent,
104262306a36Sopenharmony_ci					       d_inode(d_parent)->i_mode,
104362306a36Sopenharmony_ci					       rec_op_para);
104462306a36Sopenharmony_ci		dput(d_pparent);
104562306a36Sopenharmony_ci		if (unlikely(ret)) {
104662306a36Sopenharmony_ci			lo_d_child = ERR_PTR(ret);
104762306a36Sopenharmony_ci			goto out;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci		lo_d_parent = hmdfs_get_lo_d(d_parent, HMDFS_DEVID_LOCAL);
105062306a36Sopenharmony_ci		if (!lo_d_parent) {
105162306a36Sopenharmony_ci			lo_d_child = ERR_PTR(-ENOENT);
105262306a36Sopenharmony_ci			goto out;
105362306a36Sopenharmony_ci		}
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci	rec_op_para->is_last = true;
105662306a36Sopenharmony_ci	rec_op_para->mode = d_child_mode;
105762306a36Sopenharmony_ci	ret = hmdfs_create_lower_dentry(i_parent, d_child, lo_d_parent, is_dir,
105862306a36Sopenharmony_ci					rec_op_para);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ciout:
106162306a36Sopenharmony_ci	dput(d_parent);
106262306a36Sopenharmony_ci	dput(lo_d_parent);
106362306a36Sopenharmony_ci	return ret;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_civoid hmdfs_init_recursive_para(struct hmdfs_recursive_para *rec_op_para,
106762306a36Sopenharmony_ci			       int opcode, mode_t mode, bool want_excl,
106862306a36Sopenharmony_ci			       const char *name)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	rec_op_para->is_last = true;
107162306a36Sopenharmony_ci	rec_op_para->opcode = opcode;
107262306a36Sopenharmony_ci	rec_op_para->mode = mode;
107362306a36Sopenharmony_ci	rec_op_para->want_excl = want_excl;
107462306a36Sopenharmony_ci	rec_op_para->name = name;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ciint hmdfs_mkdir_merge(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	int ret = 0;
108062306a36Sopenharmony_ci	struct hmdfs_recursive_para *rec_op_para = NULL;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	// confict_name  & file_type is checked by hmdfs_mkdir_local
108362306a36Sopenharmony_ci	if (hmdfs_file_type(dentry->d_name.name) != HMDFS_TYPE_COMMON) {
108462306a36Sopenharmony_ci		ret = -EACCES;
108562306a36Sopenharmony_ci		goto out;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci	rec_op_para = kmalloc(sizeof(*rec_op_para), GFP_KERNEL);
108862306a36Sopenharmony_ci	if (!rec_op_para) {
108962306a36Sopenharmony_ci		ret = -ENOMEM;
109062306a36Sopenharmony_ci		goto out;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	hmdfs_init_recursive_para(rec_op_para, F_MKDIR_MERGE, mode, false,
109462306a36Sopenharmony_ci				  NULL);
109562306a36Sopenharmony_ci	ret = create_lo_d_child(dir, dentry, true, rec_op_para);
109662306a36Sopenharmony_ciout:
109762306a36Sopenharmony_ci	hmdfs_trace_merge(trace_hmdfs_mkdir_merge, dir, dentry, ret);
109862306a36Sopenharmony_ci	if (ret)
109962306a36Sopenharmony_ci		d_drop(dentry);
110062306a36Sopenharmony_ci	kfree(rec_op_para);
110162306a36Sopenharmony_ci	return ret;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ciint hmdfs_create_merge(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode,
110562306a36Sopenharmony_ci		       bool want_excl)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct hmdfs_recursive_para *rec_op_para = NULL;
110862306a36Sopenharmony_ci	int ret = 0;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	rec_op_para = kmalloc(sizeof(*rec_op_para), GFP_KERNEL);
111162306a36Sopenharmony_ci	if (!rec_op_para) {
111262306a36Sopenharmony_ci		ret = -ENOMEM;
111362306a36Sopenharmony_ci		goto out;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci	hmdfs_init_recursive_para(rec_op_para, F_CREATE_MERGE, mode, want_excl,
111662306a36Sopenharmony_ci				  NULL);
111762306a36Sopenharmony_ci	// confict_name  & file_type is checked by hmdfs_create_local
111862306a36Sopenharmony_ci	ret = create_lo_d_child(dir, dentry, false, rec_op_para);
111962306a36Sopenharmony_ciout:
112062306a36Sopenharmony_ci	hmdfs_trace_merge(trace_hmdfs_create_merge, dir, dentry, ret);
112162306a36Sopenharmony_ci	if (ret)
112262306a36Sopenharmony_ci		d_drop(dentry);
112362306a36Sopenharmony_ci	kfree(rec_op_para);
112462306a36Sopenharmony_ci	return ret;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ciint do_rmdir_merge(struct inode *dir, struct dentry *dentry)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	int ret = 0;
113062306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(dentry);
113162306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
113262306a36Sopenharmony_ci	struct dentry *lo_d = NULL;
113362306a36Sopenharmony_ci	struct dentry *lo_d_dir = NULL;
113462306a36Sopenharmony_ci	struct inode *lo_i_dir = NULL;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	wait_event(dim->wait_queue, !has_merge_lookup_work(dim));
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	mutex_lock(&dim->comrade_list_lock);
113962306a36Sopenharmony_ci	list_for_each_entry(comrade, &(dim->comrade_list), list) {
114062306a36Sopenharmony_ci		lo_d = comrade->lo_d;
114162306a36Sopenharmony_ci		lo_d_dir = lock_parent(lo_d);
114262306a36Sopenharmony_ci		lo_i_dir = d_inode(lo_d_dir);
114362306a36Sopenharmony_ci		ret = vfs_rmdir(&nop_mnt_idmap, lo_i_dir, lo_d);
114462306a36Sopenharmony_ci		unlock_dir(lo_d_dir);
114562306a36Sopenharmony_ci		if (ret)
114662306a36Sopenharmony_ci			break;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci	mutex_unlock(&dim->comrade_list_lock);
114962306a36Sopenharmony_ci	hmdfs_trace_merge(trace_hmdfs_rmdir_merge, dir, dentry, ret);
115062306a36Sopenharmony_ci	return ret;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ciint hmdfs_rmdir_merge(struct inode *dir, struct dentry *dentry)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	int ret = 0;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (hmdfs_file_type(dentry->d_name.name) != HMDFS_TYPE_COMMON) {
115862306a36Sopenharmony_ci		ret = -EACCES;
115962306a36Sopenharmony_ci		goto out;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	ret = do_rmdir_merge(dir, dentry);
116362306a36Sopenharmony_ci	if (ret) {
116462306a36Sopenharmony_ci		hmdfs_err("rm dir failed:%d", ret);
116562306a36Sopenharmony_ci		goto out;
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	hmdfs_update_meta(dir);
116962306a36Sopenharmony_ci	d_drop(dentry);
117062306a36Sopenharmony_ciout:
117162306a36Sopenharmony_ci	hmdfs_trace_merge(trace_hmdfs_rmdir_merge, dir, dentry, ret);
117262306a36Sopenharmony_ci	return ret;
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ciint do_unlink_merge(struct inode *dir, struct dentry *dentry)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	int ret = 0;
117862306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(dentry);
117962306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL;
118062306a36Sopenharmony_ci	struct dentry *lo_d = NULL;
118162306a36Sopenharmony_ci	struct dentry *lo_d_dir = NULL;
118262306a36Sopenharmony_ci	struct dentry *lo_d_lookup = NULL;
118362306a36Sopenharmony_ci	struct inode *lo_i_dir = NULL;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	wait_event(dim->wait_queue, !has_merge_lookup_work(dim));
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	mutex_lock(&dim->comrade_list_lock);
118862306a36Sopenharmony_ci	list_for_each_entry(comrade, &(dim->comrade_list), list) {
118962306a36Sopenharmony_ci		lo_d = comrade->lo_d;
119062306a36Sopenharmony_ci		dget(lo_d);
119162306a36Sopenharmony_ci		lo_d_dir = lock_parent(lo_d);
119262306a36Sopenharmony_ci		/* lo_d could be unhashed, need to lookup again here */
119362306a36Sopenharmony_ci		lo_d_lookup = lookup_one_len(lo_d->d_name.name, lo_d_dir,
119462306a36Sopenharmony_ci					     strlen(lo_d->d_name.name));
119562306a36Sopenharmony_ci		if (IS_ERR(lo_d_lookup)) {
119662306a36Sopenharmony_ci			ret = PTR_ERR(lo_d_lookup);
119762306a36Sopenharmony_ci			hmdfs_err("lookup_one_len failed, err = %d", ret);
119862306a36Sopenharmony_ci			unlock_dir(lo_d_dir);
119962306a36Sopenharmony_ci			dput(lo_d);
120062306a36Sopenharmony_ci			break;
120162306a36Sopenharmony_ci		}
120262306a36Sopenharmony_ci		lo_i_dir = d_inode(lo_d_dir);
120362306a36Sopenharmony_ci		ret = vfs_unlink(&nop_mnt_idmap, lo_i_dir, lo_d_lookup, NULL);
120462306a36Sopenharmony_ci		dput(lo_d_lookup);
120562306a36Sopenharmony_ci		unlock_dir(lo_d_dir);
120662306a36Sopenharmony_ci		dput(lo_d);
120762306a36Sopenharmony_ci		if (ret)
120862306a36Sopenharmony_ci			break;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci	mutex_unlock(&dim->comrade_list_lock);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return ret;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ciint hmdfs_unlink_merge(struct inode *dir, struct dentry *dentry)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	int ret = 0;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (hmdfs_file_type(dentry->d_name.name) != HMDFS_TYPE_COMMON) {
122062306a36Sopenharmony_ci		ret = -EACCES;
122162306a36Sopenharmony_ci		goto out;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	ret = do_unlink_merge(dir, dentry);
122562306a36Sopenharmony_ci	if (ret) {
122662306a36Sopenharmony_ci		hmdfs_err("unlink failed:%d", ret);
122762306a36Sopenharmony_ci		goto out;
122862306a36Sopenharmony_ci	} else {
122962306a36Sopenharmony_ci		hmdfs_update_meta(dir);
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	d_drop(dentry);
123362306a36Sopenharmony_ciout:
123462306a36Sopenharmony_ci	return ret;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciint do_rename_merge(struct inode *old_dir, struct dentry *old_dentry,
123862306a36Sopenharmony_ci		    struct inode *new_dir, struct dentry *new_dentry,
123962306a36Sopenharmony_ci		    unsigned int flags)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	int ret = 0;
124262306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = (old_dir->i_sb)->s_fs_info;
124362306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(old_dentry);
124462306a36Sopenharmony_ci	struct hmdfs_dentry_comrade *comrade = NULL, *new_comrade = NULL;
124562306a36Sopenharmony_ci	struct path lo_p_new = { .mnt = NULL, .dentry = NULL };
124662306a36Sopenharmony_ci	struct inode *lo_i_old_dir = NULL, *lo_i_new_dir = NULL;
124762306a36Sopenharmony_ci	struct dentry *lo_d_old_dir = NULL, *lo_d_old = NULL,
124862306a36Sopenharmony_ci		      *lo_d_new_dir = NULL, *lo_d_new = NULL;
124962306a36Sopenharmony_ci	struct dentry *d_new_dir = NULL;
125062306a36Sopenharmony_ci	char *path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
125162306a36Sopenharmony_ci	char *abs_path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
125262306a36Sopenharmony_ci	char *path_name = NULL;
125362306a36Sopenharmony_ci	struct hmdfs_dentry_info_merge *pmdi = NULL;
125462306a36Sopenharmony_ci	struct renamedata rename_data;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (flags & ~RENAME_NOREPLACE) {
125762306a36Sopenharmony_ci		ret = -EINVAL;
125862306a36Sopenharmony_ci		goto out;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	if (unlikely(!path_buf || !abs_path_buf)) {
126262306a36Sopenharmony_ci		ret = -ENOMEM;
126362306a36Sopenharmony_ci		goto out;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	wait_event(dim->wait_queue, !has_merge_lookup_work(dim));
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	list_for_each_entry(comrade, &dim->comrade_list, list) {
126962306a36Sopenharmony_ci		lo_d_old = comrade->lo_d;
127062306a36Sopenharmony_ci		d_new_dir = d_find_alias(new_dir);
127162306a36Sopenharmony_ci		pmdi = hmdfs_dm(d_new_dir);
127262306a36Sopenharmony_ci		wait_event(pmdi->wait_queue, !has_merge_lookup_work(pmdi));
127362306a36Sopenharmony_ci		lo_d_new_dir = hmdfs_get_lo_d(d_new_dir, comrade->dev_id);
127462306a36Sopenharmony_ci		dput(d_new_dir);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		if (!lo_d_new_dir)
127762306a36Sopenharmony_ci			continue;
127862306a36Sopenharmony_ci		path_name = dentry_path_raw(lo_d_new_dir, path_buf, PATH_MAX);
127962306a36Sopenharmony_ci		dput(lo_d_new_dir);
128062306a36Sopenharmony_ci		if (IS_ERR(path_name)) {
128162306a36Sopenharmony_ci			ret = PTR_ERR(path_name);
128262306a36Sopenharmony_ci			continue;
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		if (strlen(sbi->real_dst) + strlen(path_name) +
128662306a36Sopenharmony_ci		    strlen(new_dentry->d_name.name) + 2 > PATH_MAX) {
128762306a36Sopenharmony_ci			ret = -ENAMETOOLONG;
128862306a36Sopenharmony_ci			goto out;
128962306a36Sopenharmony_ci		}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci		snprintf(abs_path_buf, PATH_MAX, "%s%s/%s", sbi->real_dst,
129262306a36Sopenharmony_ci			 path_name, new_dentry->d_name.name);
129362306a36Sopenharmony_ci		if (S_ISDIR(d_inode(old_dentry)->i_mode))
129462306a36Sopenharmony_ci			lo_d_new = kern_path_create(AT_FDCWD, abs_path_buf,
129562306a36Sopenharmony_ci						    &lo_p_new,
129662306a36Sopenharmony_ci						    LOOKUP_DIRECTORY);
129762306a36Sopenharmony_ci		else
129862306a36Sopenharmony_ci			lo_d_new = kern_path_create(AT_FDCWD, abs_path_buf,
129962306a36Sopenharmony_ci						    &lo_p_new, 0);
130062306a36Sopenharmony_ci		if (IS_ERR(lo_d_new)) {
130162306a36Sopenharmony_ci			ret = PTR_ERR(lo_d_new);
130262306a36Sopenharmony_ci			goto out;
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		lo_d_new_dir = dget_parent(lo_d_new);
130662306a36Sopenharmony_ci		lo_i_new_dir = d_inode(lo_d_new_dir);
130762306a36Sopenharmony_ci		lo_d_old_dir = dget_parent(lo_d_old);
130862306a36Sopenharmony_ci		lo_i_old_dir = d_inode(lo_d_old_dir);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		rename_data.old_mnt_idmap = &nop_mnt_idmap;
131162306a36Sopenharmony_ci		rename_data.old_dir = lo_i_old_dir;
131262306a36Sopenharmony_ci		rename_data.old_dentry = lo_d_old;
131362306a36Sopenharmony_ci		rename_data.new_mnt_idmap  = &nop_mnt_idmap;
131462306a36Sopenharmony_ci		rename_data.new_dir = lo_i_new_dir;
131562306a36Sopenharmony_ci		rename_data.new_dentry = lo_d_new;
131662306a36Sopenharmony_ci		rename_data.flags = flags;
131762306a36Sopenharmony_ci		ret = vfs_rename(&rename_data);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		new_comrade = alloc_comrade(lo_p_new.dentry, comrade->dev_id);
132062306a36Sopenharmony_ci		if (IS_ERR(new_comrade)) {
132162306a36Sopenharmony_ci			ret = PTR_ERR(new_comrade);
132262306a36Sopenharmony_ci			goto no_comrade;
132362306a36Sopenharmony_ci		}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci		link_comrade_unlocked(new_dentry, new_comrade);
132662306a36Sopenharmony_cino_comrade:
132762306a36Sopenharmony_ci		done_path_create(&lo_p_new, lo_d_new);
132862306a36Sopenharmony_ci		dput(lo_d_old_dir);
132962306a36Sopenharmony_ci		dput(lo_d_new_dir);
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ciout:
133262306a36Sopenharmony_ci	kfree(abs_path_buf);
133362306a36Sopenharmony_ci	kfree(path_buf);
133462306a36Sopenharmony_ci	return ret;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ciint hmdfs_rename_merge(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry,
133862306a36Sopenharmony_ci		       struct inode *new_dir, struct dentry *new_dentry,
133962306a36Sopenharmony_ci		       unsigned int flags)
134062306a36Sopenharmony_ci{
134162306a36Sopenharmony_ci	char *old_dir_buf = NULL;
134262306a36Sopenharmony_ci	char *new_dir_buf = NULL;
134362306a36Sopenharmony_ci	char *old_dir_path = NULL;
134462306a36Sopenharmony_ci	char *new_dir_path = NULL;
134562306a36Sopenharmony_ci	struct dentry *old_dir_dentry = NULL;
134662306a36Sopenharmony_ci	struct dentry *new_dir_dentry = NULL;
134762306a36Sopenharmony_ci	int ret = 0;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (hmdfs_file_type(old_dentry->d_name.name) != HMDFS_TYPE_COMMON ||
135062306a36Sopenharmony_ci	    hmdfs_file_type(new_dentry->d_name.name) != HMDFS_TYPE_COMMON) {
135162306a36Sopenharmony_ci		ret = -EACCES;
135262306a36Sopenharmony_ci		goto rename_out;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (hmdfs_i(old_dir)->inode_type != hmdfs_i(new_dir)->inode_type) {
135662306a36Sopenharmony_ci		hmdfs_err("in different view");
135762306a36Sopenharmony_ci		ret = -EPERM;
135862306a36Sopenharmony_ci		goto rename_out;
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	old_dir_buf = kmalloc(PATH_MAX, GFP_KERNEL);
136262306a36Sopenharmony_ci	new_dir_buf = kmalloc(PATH_MAX, GFP_KERNEL);
136362306a36Sopenharmony_ci	if (!old_dir_buf || !new_dir_buf) {
136462306a36Sopenharmony_ci		ret = -ENOMEM;
136562306a36Sopenharmony_ci		goto rename_out;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	new_dir_dentry = d_find_alias(new_dir);
136962306a36Sopenharmony_ci	if (!new_dir_dentry) {
137062306a36Sopenharmony_ci		ret = -EINVAL;
137162306a36Sopenharmony_ci		goto rename_out;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	old_dir_dentry = d_find_alias(old_dir);
137562306a36Sopenharmony_ci	if (!old_dir_dentry) {
137662306a36Sopenharmony_ci		ret = -EINVAL;
137762306a36Sopenharmony_ci		dput(new_dir_dentry);
137862306a36Sopenharmony_ci		goto rename_out;
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	old_dir_path = dentry_path_raw(old_dir_dentry, old_dir_buf, PATH_MAX);
138262306a36Sopenharmony_ci	new_dir_path = dentry_path_raw(new_dir_dentry, new_dir_buf, PATH_MAX);
138362306a36Sopenharmony_ci	dput(new_dir_dentry);
138462306a36Sopenharmony_ci	dput(old_dir_dentry);
138562306a36Sopenharmony_ci	if (strcmp(old_dir_path, new_dir_path)) {
138662306a36Sopenharmony_ci		ret = -EPERM;
138762306a36Sopenharmony_ci		goto rename_out;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	trace_hmdfs_rename_merge(old_dir, old_dentry, new_dir, new_dentry,
139162306a36Sopenharmony_ci				 flags);
139262306a36Sopenharmony_ci	ret = do_rename_merge(old_dir, old_dentry, new_dir, new_dentry, flags);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (ret != 0)
139562306a36Sopenharmony_ci		d_drop(new_dentry);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (S_ISREG(old_dentry->d_inode->i_mode) && !ret)
139862306a36Sopenharmony_ci		d_invalidate(old_dentry);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cirename_out:
140162306a36Sopenharmony_ci	kfree(old_dir_buf);
140262306a36Sopenharmony_ci	kfree(new_dir_buf);
140362306a36Sopenharmony_ci	return ret;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ciconst struct inode_operations hmdfs_dir_iops_merge = {
140762306a36Sopenharmony_ci	.lookup = hmdfs_lookup_merge,
140862306a36Sopenharmony_ci	.mkdir = hmdfs_mkdir_merge,
140962306a36Sopenharmony_ci	.create = hmdfs_create_merge,
141062306a36Sopenharmony_ci	.rmdir = hmdfs_rmdir_merge,
141162306a36Sopenharmony_ci	.unlink = hmdfs_unlink_merge,
141262306a36Sopenharmony_ci	.rename = hmdfs_rename_merge,
141362306a36Sopenharmony_ci	.permission = hmdfs_permission,
141462306a36Sopenharmony_ci};
1415