• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * fs/hmdfs/dentry.c
4  *
5  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
6  */
7 
8 #include <linux/ctype.h>
9 #include <linux/slab.h>
10 
11 #include "comm/connection.h"
12 #include "hmdfs_dentryfile.h"
13 #include "hmdfs_device_view.h"
14 #include "hmdfs_merge_view.h"
15 
16 extern struct kmem_cache *hmdfs_dentry_cachep;
17 
hmdfs_set_time(struct dentry * dentry,unsigned long time)18 void hmdfs_set_time(struct dentry *dentry, unsigned long time)
19 {
20 	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
21 
22 	if (d_info)
23 		d_info->time = time;
24 }
25 
hmdfs_get_time(struct dentry * dentry)26 unsigned long hmdfs_get_time(struct dentry *dentry)
27 {
28 	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
29 
30 	if (d_info)
31 		return (unsigned long)d_info->time;
32 	return 0;
33 }
34 
hmdfs_d_remote_revalidate(struct hmdfs_peer * conn,struct dentry * target,struct dentry * parent)35 static int hmdfs_d_remote_revalidate(struct hmdfs_peer *conn,
36 				     struct dentry *target,
37 				     struct dentry *parent)
38 {
39 	unsigned int timeout = hmdfs_sb(target->d_sb)->dcache_timeout;
40 	unsigned long dentry_time = hmdfs_get_time(target);
41 	struct clearcache_item *item;
42 
43 	item = hmdfs_find_cache_item(conn->device_id, parent);
44 	if (!item)
45 		return 0;
46 	kref_put(&item->ref, release_cache_item);
47 
48 	if (cache_item_revalidate(READ_ONCE(conn->conn_time),
49 				  dentry_time, timeout))
50 		return 1;
51 
52 	return 0;
53 }
54 
lock_for_dname_cmp(struct dentry * dentry,struct dentry * lower_dentry)55 static inline void lock_for_dname_cmp(struct dentry *dentry,
56 				      struct dentry *lower_dentry)
57 {
58 	if (dentry < lower_dentry) {
59 		spin_lock(&dentry->d_lock);
60 		spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
61 	} else {
62 		spin_lock(&lower_dentry->d_lock);
63 		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
64 	}
65 }
66 
unlock_for_dname_cmp(struct dentry * dentry,struct dentry * lower_dentry)67 static inline void unlock_for_dname_cmp(struct dentry *dentry,
68 					struct dentry *lower_dentry)
69 {
70 	spin_unlock(&dentry->d_lock);
71 	spin_unlock(&lower_dentry->d_lock);
72 }
73 
hmdfs_dev_d_revalidate(struct dentry * direntry,unsigned int flags)74 static int hmdfs_dev_d_revalidate(struct dentry *direntry, unsigned int flags)
75 {
76 	struct inode *dinode = NULL;
77 	struct hmdfs_inode_info *info = NULL;
78 
79 	spin_lock(&direntry->d_lock);
80 	if (IS_ROOT(direntry)) {
81 		spin_unlock(&direntry->d_lock);
82 		return 1;
83 	}
84 	spin_unlock(&direntry->d_lock);
85 
86 	dinode = d_inode(direntry);
87 	if (!dinode)
88 		return 0;
89 
90 	info = hmdfs_i(dinode);
91 	if (info->inode_type == HMDFS_LAYER_SECOND_LOCAL ||
92 	    info->inode_type == HMDFS_LAYER_FIRST_DEVICE) {
93 		return 1;
94 	}
95 	if (info->conn && info->conn->status == NODE_STAT_ONLINE)
96 		return 1;
97 
98 	return 0;
99 }
100 
hmdfs_d_revalidate(struct dentry * direntry,unsigned int flags)101 static int hmdfs_d_revalidate(struct dentry *direntry, unsigned int flags)
102 {
103 	struct inode *dinode = NULL;
104 	struct hmdfs_inode_info *info = NULL;
105 	struct path lower_path, parent_lower_path;
106 	struct dentry *parent_dentry = NULL;
107 	struct dentry *parent_lower_dentry = NULL;
108 	struct dentry *lower_cur_parent_dentry = NULL;
109 	struct dentry *lower_dentry = NULL;
110 	int ret;
111 
112 	if (flags & LOOKUP_RCU)
113 		return -ECHILD;
114 
115 	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL))
116 		return 0;
117 
118 	dinode = d_inode(direntry);
119 	if (!dinode)
120 		return 0;
121 
122 	/* remote dentry timeout */
123 	info = hmdfs_i(dinode);
124 	parent_dentry = dget_parent(direntry);
125 	if (info->conn) {
126 		ret = hmdfs_d_remote_revalidate(info->conn, direntry,
127 						parent_dentry);
128 		dput(parent_dentry);
129 		return ret;
130 	}
131 
132 	hmdfs_get_lower_path(direntry, &lower_path);
133 	lower_dentry = lower_path.dentry;
134 	lower_cur_parent_dentry = dget_parent(lower_dentry);
135 	hmdfs_get_lower_path(parent_dentry, &parent_lower_path);
136 	parent_lower_dentry = parent_lower_path.dentry;
137 	if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
138 		ret = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
139 		if (ret == 0)
140 			goto out;
141 	}
142 
143 	spin_lock(&lower_dentry->d_lock);
144 	if (d_unhashed(lower_dentry)) {
145 		spin_unlock(&lower_dentry->d_lock);
146 		ret = 0;
147 		goto out;
148 	}
149 	spin_unlock(&lower_dentry->d_lock);
150 
151 	if (parent_lower_dentry != lower_cur_parent_dentry) {
152 		ret = 0;
153 		goto out;
154 	}
155 
156 	ret = 1;
157 	lock_for_dname_cmp(direntry, lower_dentry);
158 	if (!qstr_case_eq(&direntry->d_name, &lower_dentry->d_name))
159 		ret = 0;
160 	unlock_for_dname_cmp(direntry, lower_dentry);
161 
162 out:
163 	hmdfs_put_lower_path(&parent_lower_path);
164 	dput(lower_cur_parent_dentry);
165 	hmdfs_put_lower_path(&lower_path);
166 	dput(parent_dentry);
167 	return ret;
168 }
169 
hmdfs_dev_d_release(struct dentry * dentry)170 static void hmdfs_dev_d_release(struct dentry *dentry)
171 {
172 	if (!dentry || !dentry->d_fsdata)
173 		return;
174 
175 	switch (hmdfs_d(dentry)->dentry_type) {
176 	case HMDFS_LAYER_SECOND_LOCAL:
177 		hmdfs_clear_cache_dents(dentry, false);
178 		hmdfs_drop_remote_cache_dents(dentry);
179 		path_put(&(hmdfs_d(dentry)->lower_path));
180 		break;
181 	case HMDFS_LAYER_ZERO:
182 		hmdfs_put_reset_lower_path(dentry);
183 		break;
184 	case HMDFS_LAYER_FIRST_DEVICE:
185 		break;
186 	case HMDFS_LAYER_SECOND_REMOTE:
187 		hmdfs_clear_cache_dents(dentry, false);
188 		break;
189 	default:
190 		hmdfs_err("Unexpected dentry type %d",
191 			  hmdfs_d(dentry)->dentry_type);
192 		return;
193 	}
194 
195 	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
196 	dentry->d_fsdata = NULL;
197 }
198 
hmdfs_d_release(struct dentry * dentry)199 static void hmdfs_d_release(struct dentry *dentry)
200 {
201 	if (!dentry || !dentry->d_fsdata)
202 		return;
203 
204 	hmdfs_clear_cache_dents(dentry, false);
205 	hmdfs_drop_remote_cache_dents(dentry);
206 	hmdfs_put_reset_lower_path(dentry);
207 	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
208 	dentry->d_fsdata = NULL;
209 }
210 
hmdfs_cmp_ci(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * name)211 static int hmdfs_cmp_ci(const struct dentry *dentry, unsigned int len,
212 			const char *str, const struct qstr *name)
213 {
214 	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
215 
216 	if (name->len != len)
217 		return 1;
218 
219 	if (!sbi->s_case_sensitive) {
220 		if (str_n_case_eq(name->name, str, len))
221 			return 0;
222 	} else {
223 		if (!strncmp(name->name, str, len))
224 			return 0;
225 	}
226 	return 1;
227 }
228 
hmdfs_hash_ci(const struct dentry * dentry,struct qstr * qstr)229 static int hmdfs_hash_ci(const struct dentry *dentry, struct qstr *qstr)
230 {
231 	const unsigned char *name = qstr->name;
232 	unsigned int len = qstr->len;
233 	unsigned long hash;
234 	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
235 
236 	if (sbi->s_case_sensitive)
237 		return 0;
238 
239 	hash = init_name_hash(dentry);
240 	while (len--)
241 		hash = partial_name_hash(tolower(*name++), hash);
242 	qstr->hash = end_name_hash(hash);
243 	return 0;
244 }
245 
clear_comrades_locked(struct list_head * comrade_list)246 void clear_comrades_locked(struct list_head *comrade_list)
247 {
248 	struct hmdfs_dentry_comrade *cc, *nc;
249 
250 	WARN_ON(!comrade_list);
251 	list_for_each_entry_safe(cc, nc, comrade_list, list) {
252 		dput(cc->lo_d);
253 		kfree(cc);
254 	}
255 	INIT_LIST_HEAD(comrade_list);
256 }
257 
clear_comrades(struct dentry * dentry)258 void clear_comrades(struct dentry *dentry)
259 {
260 	struct hmdfs_dentry_info_merge *cdi = hmdfs_dm(dentry);
261 
262 	wait_event(cdi->wait_queue, !has_merge_lookup_work(cdi));
263 	mutex_lock(&cdi->comrade_list_lock);
264 	clear_comrades_locked(&cdi->comrade_list);
265 	mutex_unlock(&cdi->comrade_list_lock);
266 }
267 
268 /**
269  * d_revalidate_merge - revalidate a merge dentry
270  *
271  * Always return 0 to invalidate a dentry for fault-tolerance.
272  * The cost is acceptable for a overlay filesystem.
273  */
d_revalidate_merge(struct dentry * direntry,unsigned int flags)274 static int d_revalidate_merge(struct dentry *direntry, unsigned int flags)
275 {
276 	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(direntry);
277 	struct hmdfs_dentry_comrade *comrade = NULL;
278 	struct dentry *parent_dentry = NULL;
279 	struct dentry *lower_cur_parent_dentry = NULL;
280 	int ret = 1;
281 
282 	if (flags & LOOKUP_RCU) {
283 		return -ECHILD;
284 	}
285 
286 	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL)) {
287 		return 0;
288 	}
289 
290 	parent_dentry = dget_parent(direntry);
291         mutex_lock(&dim->comrade_list_lock);
292 	list_for_each_entry(comrade, &(dim->comrade_list), list) {
293 		lower_cur_parent_dentry = dget_parent(comrade->lo_d);
294 		if ((comrade->lo_d->d_flags & DCACHE_OP_REVALIDATE)) {
295 			ret = comrade->lo_d->d_op->d_revalidate(
296 				comrade->lo_d, flags);
297 			if (ret == 0) {
298 				dput(lower_cur_parent_dentry);
299 				goto out;
300 			}
301 		}
302 		dput(lower_cur_parent_dentry);
303 	}
304 out:
305         mutex_unlock(&dim->comrade_list_lock);
306 	dput(parent_dentry);
307 	return ret;
308 }
309 
d_release_merge(struct dentry * dentry)310 static void d_release_merge(struct dentry *dentry)
311 {
312 	if (!dentry || !dentry->d_fsdata)
313 		return;
314 
315 	clear_comrades(dentry);
316 	kmem_cache_free(hmdfs_dentry_merge_cachep, dentry->d_fsdata);
317 	dentry->d_fsdata = NULL;
318 }
319 
320 const struct dentry_operations hmdfs_dops_merge = {
321 	.d_revalidate = d_revalidate_merge,
322 	.d_release = d_release_merge,
323 };
324 
325 const struct dentry_operations hmdfs_dev_dops = {
326 	.d_revalidate = hmdfs_dev_d_revalidate,
327 	.d_release = hmdfs_dev_d_release,
328 };
329 
330 const struct dentry_operations hmdfs_dops = {
331 	.d_revalidate = hmdfs_d_revalidate,
332 	.d_release = hmdfs_d_release,
333 	.d_compare = hmdfs_cmp_ci,
334 	.d_hash = hmdfs_hash_ci,
335 };
336