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