• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * fs/hmdfs/file_merge.c
4  *
5  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
6  */
7 
8 #include "hmdfs_merge_view.h"
9 
10 #include <linux/file.h>
11 
12 #include "hmdfs.h"
13 #include "hmdfs_trace.h"
14 
15 struct hmdfs_iterate_callback_merge {
16 	struct dir_context ctx;
17 	struct dir_context *caller;
18 	/*
19 	 * Record the return value of 'caller->actor':
20 	 *
21 	 * -EINVAL, buffer is exhausted
22 	 * -EINTR, current task is pending
23 	 * -EFAULT, something is wrong
24 	 * 0, success and can do more
25 	 */
26 	int result;
27 	struct rb_root *root;
28 	uint64_t dev_id;
29 };
30 
31 struct hmdfs_cache_entry {
32 	struct rb_node rb_node;
33 	int name_len;
34 	char *name;
35 	int file_type;
36 };
37 
allocate_entry(const char * name,int namelen,int d_type)38 struct hmdfs_cache_entry *allocate_entry(const char *name, int namelen,
39 					 int d_type)
40 {
41 	struct hmdfs_cache_entry *data;
42 
43 	data = kmalloc(sizeof(*data), GFP_KERNEL);
44 	if (!data)
45 		return ERR_PTR(-ENOMEM);
46 
47 	data->name = kstrndup(name, namelen, GFP_KERNEL);
48 	if (!data->name) {
49 		kfree(data);
50 		return ERR_PTR(-ENOMEM);
51 	}
52 
53 	data->name_len = namelen;
54 	data->file_type = d_type;
55 
56 	return data;
57 }
58 
insert_filename(struct rb_root * root,struct hmdfs_cache_entry ** new_entry)59 int insert_filename(struct rb_root *root, struct hmdfs_cache_entry **new_entry)
60 {
61 	struct rb_node *parent = NULL;
62 	struct rb_node **new_node = &(root->rb_node);
63 	int cmp_res = 0;
64 	struct hmdfs_cache_entry *data = *new_entry;
65 
66 	while (*new_node) {
67 		struct hmdfs_cache_entry *entry = container_of(
68 			*new_node, struct hmdfs_cache_entry, rb_node);
69 		parent = *new_node;
70 
71 		if (data->name_len < entry->name_len)
72 			cmp_res = -1;
73 		else if (data->name_len > entry->name_len)
74 			cmp_res = 1;
75 		else
76 			cmp_res = strncmp(data->name, entry->name,
77 					  data->name_len);
78 
79 		if (!cmp_res) {
80 			kfree(data->name);
81 			kfree(data);
82 			*new_entry = entry;
83 			return entry->file_type;
84 		}
85 
86 		if (cmp_res < 0)
87 			new_node = &((*new_node)->rb_left);
88 		else if (cmp_res > 0)
89 			new_node = &((*new_node)->rb_right);
90 	}
91 
92 	rb_link_node(&data->rb_node, parent, new_node);
93 	rb_insert_color(&data->rb_node, root);
94 
95 	return 0;
96 }
97 
recursive_delete(struct rb_node * node)98 static void recursive_delete(struct rb_node *node)
99 {
100 	struct hmdfs_cache_entry *entry = NULL;
101 
102 	if (!node)
103 		return;
104 
105 	recursive_delete(node->rb_left);
106 	recursive_delete(node->rb_right);
107 
108 	entry = container_of(node, struct hmdfs_cache_entry, rb_node);
109 	kfree(entry->name);
110 	kfree(entry);
111 }
112 
destroy_tree(struct rb_root * root)113 static void destroy_tree(struct rb_root *root)
114 {
115 	if (!root)
116 		return;
117 	recursive_delete(root->rb_node);
118 	root->rb_node = NULL;
119 }
120 
delete_filename(struct rb_root * root,struct hmdfs_cache_entry * data)121 static void delete_filename(struct rb_root *root,
122 			    struct hmdfs_cache_entry *data)
123 {
124 	struct rb_node **node = &(root->rb_node);
125 	struct hmdfs_cache_entry *entry = NULL;
126 	int cmp_res = 0;
127 
128 	while (*node) {
129 		entry = container_of(*node, struct hmdfs_cache_entry, rb_node);
130 		if (data->name_len < entry->name_len)
131 			cmp_res = -1;
132 		else if (data->name_len > entry->name_len)
133 			cmp_res = 1;
134 		else
135 			cmp_res = strncmp(data->name, entry->name,
136 					  data->name_len);
137 
138 		if (!cmp_res)
139 			goto found;
140 
141 		if (cmp_res < 0)
142 			node = &((*node)->rb_left);
143 		else if (cmp_res > 0)
144 			node = &((*node)->rb_right);
145 	}
146 	return;
147 
148 found:
149 	rb_erase(*node, root);
150 	kfree(entry->name);
151 	kfree(entry);
152 }
153 
rename_conflicting_file(char * dentry_name,int * len,unsigned int dev_id)154 static void rename_conflicting_file(char *dentry_name, int *len,
155 				    unsigned int dev_id)
156 {
157 	int i = *len - 1;
158 	int dot_pos = -1;
159 	char *buffer;
160 
161 	buffer = kzalloc(DENTRY_NAME_MAX_LEN, GFP_KERNEL);
162 	if (!buffer)
163 		return;
164 
165 	while (i >= 0) {
166 		if (dentry_name[i] == '/')
167 			break;
168 		if (dentry_name[i] == '.') {
169 			// TODO: 这个修改同步到 CT01
170 			dot_pos = i;
171 			break;
172 		}
173 		i--;
174 	}
175 
176 	if (dot_pos == -1) {
177 		snprintf(dentry_name + *len, DENTRY_NAME_MAX_LEN - *len,
178 			 CONFLICTING_FILE_SUFFIX, dev_id);
179 		goto done;
180 	}
181 
182 	for (i = 0; i < *len - dot_pos; i++)
183 		buffer[i] = dentry_name[i + dot_pos];
184 
185 	buffer[i] = '\0';
186 	snprintf(dentry_name + dot_pos, DENTRY_NAME_MAX_LEN - dot_pos,
187 		 CONFLICTING_FILE_SUFFIX, dev_id);
188 	strcat(dentry_name, buffer);
189 
190 done:
191 	*len = strlen(dentry_name);
192 	kfree(buffer);
193 }
194 
rename_conflicting_directory(char * dentry_name,int * len)195 static void rename_conflicting_directory(char *dentry_name, int *len)
196 {
197 	snprintf(dentry_name + *len, DENTRY_NAME_MAX_LEN - *len,
198 		 CONFLICTING_DIR_SUFFIX);
199 	*len += strlen(CONFLICTING_DIR_SUFFIX);
200 }
201 
hmdfs_actor_merge(struct dir_context * ctx,const char * name,int namelen,loff_t offset,u64 ino,unsigned int d_type)202 static int hmdfs_actor_merge(struct dir_context *ctx, const char *name,
203 			     int namelen, loff_t offset, u64 ino,
204 			     unsigned int d_type)
205 {
206 	int ret = 0;
207 	int insert_res = 0;
208 	int max_devid_len = 2;
209 	char *dentry_name = NULL;
210 	int dentry_len = namelen;
211 	struct hmdfs_cache_entry *cache_entry = NULL;
212 	struct hmdfs_iterate_callback_merge *iterate_callback_merge = NULL;
213 	struct dir_context *org_ctx = NULL;
214 
215 	if (hmdfs_file_type(name) != HMDFS_TYPE_COMMON)
216 		return 0;
217 
218 	if (namelen > NAME_MAX)
219 		return -EINVAL;
220 	dentry_name = kzalloc(NAME_MAX + 1, GFP_KERNEL);
221 	if (!dentry_name)
222 		return -ENOMEM;
223 
224 	strncpy(dentry_name, name, dentry_len);
225 
226 	cache_entry = allocate_entry(dentry_name, dentry_len, d_type);
227 	if (IS_ERR(cache_entry)) {
228 		ret = PTR_ERR(cache_entry);
229 		goto done;
230 	}
231 
232 	iterate_callback_merge =
233 		container_of(ctx, struct hmdfs_iterate_callback_merge, ctx);
234 	insert_res =
235 		insert_filename(iterate_callback_merge->root, &cache_entry);
236 	if (d_type == DT_DIR && insert_res == DT_DIR) {
237 		goto done;
238 	} else if (d_type == DT_DIR && insert_res == DT_REG) {
239 		if (strlen(CONFLICTING_DIR_SUFFIX) > NAME_MAX - dentry_len) {
240 			ret = -ENAMETOOLONG;
241 			goto delete;
242 		}
243 		rename_conflicting_directory(dentry_name, &dentry_len);
244 		cache_entry->file_type = DT_DIR;
245 	} else if (d_type == DT_REG && insert_res > 0) {
246 		if (strlen(CONFLICTING_FILE_SUFFIX) + max_devid_len >
247 		    NAME_MAX - dentry_len) {
248 			ret = -ENAMETOOLONG;
249 			goto delete;
250 		}
251 		rename_conflicting_file(dentry_name, &dentry_len,
252 					iterate_callback_merge->dev_id);
253 	}
254 
255 	org_ctx = iterate_callback_merge->caller;
256 	ret = org_ctx->actor(org_ctx, dentry_name, dentry_len, org_ctx->pos,
257 			     ino, d_type);
258 	/*
259 	 * Record original return value, so that the caller can be aware of
260 	 * different situations.
261 	 */
262 	iterate_callback_merge->result = ret;
263 	ret = ret == 0 ? 0 : 1;
264 	if (ret && d_type == DT_DIR && insert_res == DT_REG &&
265 	    cache_entry->file_type == DT_DIR)
266 		cache_entry->file_type = DT_REG;
267 
268 delete:
269 	if (ret && !insert_res)
270 		delete_filename(iterate_callback_merge->root, cache_entry);
271 done:
272 	kfree(dentry_name);
273 	return ret;
274 }
275 
276 struct hmdfs_file_info *
get_next_hmdfs_file_info(struct hmdfs_file_info * fi_head,int device_id)277 get_next_hmdfs_file_info(struct hmdfs_file_info *fi_head, int device_id)
278 {
279 	struct hmdfs_file_info *fi_iter = NULL;
280 	struct hmdfs_file_info *fi_result = NULL;
281 
282 	mutex_lock(&fi_head->comrade_list_lock);
283 	list_for_each_entry_safe(fi_iter, fi_result, &(fi_head->comrade_list),
284 				  comrade_list) {
285 		if (fi_iter->device_id == device_id)
286 			break;
287 	}
288 	mutex_unlock(&fi_head->comrade_list_lock);
289 
290 	return fi_result != fi_head ? fi_result : NULL;
291 }
292 
get_hmdfs_file_info(struct hmdfs_file_info * fi_head,int device_id)293 struct hmdfs_file_info *get_hmdfs_file_info(struct hmdfs_file_info *fi_head,
294 					    int device_id)
295 {
296 	struct hmdfs_file_info *fi_iter = NULL;
297 
298 	mutex_lock(&fi_head->comrade_list_lock);
299 	list_for_each_entry(fi_iter, &(fi_head->comrade_list), comrade_list) {
300 		if (fi_iter->device_id == device_id) {
301 			mutex_unlock(&fi_head->comrade_list_lock);
302 			return fi_iter;
303 		}
304 	}
305 	mutex_unlock(&fi_head->comrade_list_lock);
306 
307 	return NULL;
308 }
309 
hmdfs_iterate_merge(struct file * file,struct dir_context * ctx)310 int hmdfs_iterate_merge(struct file *file, struct dir_context *ctx)
311 {
312 	int err = 0;
313 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
314 	struct hmdfs_file_info *fi_iter = NULL;
315 	struct file *lower_file_iter = NULL;
316 	loff_t start_pos = ctx->pos;
317 	unsigned long device_id = (unsigned long)((ctx->pos) << 1 >>
318 				  (POS_BIT_NUM - DEV_ID_BIT_NUM));
319 	struct hmdfs_iterate_callback_merge ctx_merge = {
320 		.ctx.actor = hmdfs_actor_merge,
321 		.caller = ctx,
322 		.root = &fi_head->root,
323 		.dev_id = device_id
324 	};
325 
326 	/* pos = -1 indicates that all devices have been traversed
327 	 * or an error has occurred.
328 	 */
329 	if (ctx->pos == -1)
330 		return 0;
331 
332 	fi_iter = get_hmdfs_file_info(fi_head, device_id);
333 	if (!fi_iter) {
334 		fi_iter = get_next_hmdfs_file_info(fi_head, device_id);
335 		// dev_id is changed, parameter is set 0 to get next file info
336 		if (fi_iter)
337 			ctx_merge.ctx.pos =
338 				hmdfs_set_pos(fi_iter->device_id, 0, 0);
339 	}
340 	while (fi_iter) {
341 		ctx_merge.dev_id = fi_iter->device_id;
342 		device_id = ctx_merge.dev_id;
343 		lower_file_iter = fi_iter->lower_file;
344 		lower_file_iter->f_pos = file->f_pos;
345 		err = iterate_dir(lower_file_iter, &ctx_merge.ctx);
346 		file->f_pos = lower_file_iter->f_pos;
347 		ctx->pos = file->f_pos;
348 
349 		if (err)
350 			goto done;
351 		/*
352 		 * ctx->actor return nonzero means buffer is exhausted or
353 		 * something is wrong, thus we should not continue.
354 		 */
355 		if (ctx_merge.result)
356 			goto done;
357 		fi_iter = get_next_hmdfs_file_info(fi_head, device_id);
358 		if (fi_iter) {
359 			file->f_pos = hmdfs_set_pos(fi_iter->device_id, 0, 0);
360 			ctx->pos = file->f_pos;
361 		}
362 	}
363 done:
364 	trace_hmdfs_iterate_merge(file->f_path.dentry, start_pos, ctx->pos,
365 				  err);
366 	return err;
367 }
368 
do_dir_open_merge(struct file * file,const struct cred * cred,struct hmdfs_file_info * fi_head)369 int do_dir_open_merge(struct file *file, const struct cred *cred,
370 		      struct hmdfs_file_info *fi_head)
371 {
372 	int ret = -EINVAL;
373 	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(file->f_path.dentry);
374 	struct hmdfs_dentry_comrade *comrade = NULL;
375 	struct hmdfs_file_info *fi = NULL;
376 	struct path lo_p = { .mnt = file->f_path.mnt };
377 	struct file *lower_file = NULL;
378 
379 	if (IS_ERR_OR_NULL(cred))
380 		return ret;
381 
382 	wait_event(dim->wait_queue, !has_merge_lookup_work(dim));
383 
384 	mutex_lock(&dim->comrade_list_lock);
385 	list_for_each_entry(comrade, &(dim->comrade_list), list) {
386 		fi = kzalloc(sizeof(*fi), GFP_KERNEL);
387 		if (!fi) {
388 			ret = ret ? -ENOMEM : 0;
389 			continue; // allow some dir to fail to open
390 		}
391 		lo_p.dentry = comrade->lo_d;
392 		// make sure that dentry will not be dentry_kill before open
393 		dget(lo_p.dentry);
394 		if (unlikely(d_is_negative(lo_p.dentry))) {
395 			hmdfs_info("dentry is negative, try again");
396 			kfree(fi);
397 			dput(lo_p.dentry);
398 			continue;  // skip this device
399 		}
400 		lower_file = dentry_open(&lo_p, file->f_flags, cred);
401 		dput(lo_p.dentry);
402 		if (IS_ERR(lower_file)) {
403 			kfree(fi);
404 			continue;
405 		}
406 		ret = 0;
407 		fi->device_id = comrade->dev_id;
408 		fi->lower_file = lower_file;
409 		mutex_lock(&fi_head->comrade_list_lock);
410 		list_add_tail(&fi->comrade_list, &fi_head->comrade_list);
411 		mutex_unlock(&fi_head->comrade_list_lock);
412 	}
413 	mutex_unlock(&dim->comrade_list_lock);
414 	return ret;
415 }
416 
hmdfs_dir_open_merge(struct inode * inode,struct file * file)417 int hmdfs_dir_open_merge(struct inode *inode, struct file *file)
418 {
419 	int ret = 0;
420 	struct hmdfs_file_info *fi = NULL;
421 
422 	fi = kzalloc(sizeof(*fi), GFP_KERNEL);
423 	if (!fi)
424 		return -ENOMEM;
425 
426 	file->private_data = fi;
427 	fi->root = RB_ROOT;
428 	mutex_init(&fi->comrade_list_lock);
429 	INIT_LIST_HEAD(&fi->comrade_list);
430 
431 	ret = do_dir_open_merge(file, hmdfs_sb(inode->i_sb)->cred, fi);
432 	if (ret)
433 		kfree(fi);
434 
435 	return ret;
436 }
437 
hmdfs_dir_release_merge(struct inode * inode,struct file * file)438 int hmdfs_dir_release_merge(struct inode *inode, struct file *file)
439 {
440 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
441 	struct hmdfs_file_info *fi_iter = NULL;
442 	struct hmdfs_file_info *fi_temp = NULL;
443 
444 	mutex_lock(&fi_head->comrade_list_lock);
445 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
446 				  comrade_list) {
447 		list_del_init(&(fi_iter->comrade_list));
448 		fput(fi_iter->lower_file);
449 		kfree(fi_iter);
450 	}
451 	mutex_unlock(&fi_head->comrade_list_lock);
452 	destroy_tree(&fi_head->root);
453 	file->private_data = NULL;
454 	kfree(fi_head);
455 
456 	return 0;
457 }
458 
hmdfs_dir_unlocked_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)459 long hmdfs_dir_unlocked_ioctl_merge(struct file *file, unsigned int cmd,
460 							unsigned long arg)
461 {
462 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
463 	struct hmdfs_file_info *fi_iter = NULL;
464 	struct hmdfs_file_info *fi_temp = NULL;
465 	struct file *lower_file = NULL;
466 	int error = -ENOTTY;
467 
468 	mutex_lock(&fi_head->comrade_list_lock);
469 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
470 				  comrade_list) {
471 		if (fi_iter->device_id == 0) {
472 			lower_file = fi_iter->lower_file;
473 			error = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
474 			break;
475 		}
476 	}
477 	mutex_unlock(&fi_head->comrade_list_lock);
478 	return error;
479 }
480 
hmdfs_dir_compat_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)481 long hmdfs_dir_compat_ioctl_merge(struct file *file, unsigned int cmd,
482 							unsigned long arg)
483 {
484 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
485 	struct hmdfs_file_info *fi_iter = NULL;
486 	struct hmdfs_file_info *fi_temp = NULL;
487 	struct file *lower_file = NULL;
488 	int error = -ENOTTY;
489 
490 	mutex_lock(&fi_head->comrade_list_lock);
491 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
492 				  comrade_list) {
493 		if (fi_iter->device_id == 0) {
494 			lower_file = fi_iter->lower_file;
495 			error = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
496 			break;
497 		}
498 	}
499 	mutex_unlock(&fi_head->comrade_list_lock);
500 	return error;
501 }
502 
503 const struct file_operations hmdfs_dir_fops_merge = {
504 	.owner = THIS_MODULE,
505 	.iterate = hmdfs_iterate_merge,
506 	.open = hmdfs_dir_open_merge,
507 	.release = hmdfs_dir_release_merge,
508 	.unlocked_ioctl = hmdfs_dir_unlocked_ioctl_merge,
509 	.compat_ioctl = hmdfs_dir_compat_ioctl_merge,
510 };
511 
hmdfs_merge_read_iter(struct kiocb * iocb,struct iov_iter * iter)512 static ssize_t hmdfs_merge_read_iter(struct kiocb *iocb, struct iov_iter *iter)
513 {
514 	return hmdfs_do_read_iter(iocb->ki_filp, iter, &iocb->ki_pos);
515 }
516 
hmdfs_merge_write_iter(struct kiocb * iocb,struct iov_iter * iter)517 ssize_t hmdfs_merge_write_iter(struct kiocb *iocb, struct iov_iter *iter)
518 {
519 	return hmdfs_do_write_iter(iocb->ki_filp, iter, &iocb->ki_pos);
520 }
521 
hmdfs_file_open_merge(struct inode * inode,struct file * file)522 int hmdfs_file_open_merge(struct inode *inode, struct file *file)
523 {
524 	int err = 0;
525 	struct file *lower_file = NULL;
526 	struct path lo_p = { .mnt = file->f_path.mnt };
527 	struct super_block *sb = inode->i_sb;
528 	const struct cred *cred = hmdfs_sb(sb)->cred;
529 	struct hmdfs_file_info *gfi = NULL;
530 	struct dentry *parent = NULL;
531 
532 	lo_p.dentry = hmdfs_get_fst_lo_d(file->f_path.dentry);
533 	if (!lo_p.dentry) {
534 		err = -EINVAL;
535 		goto out_err;
536 	}
537 
538 	gfi = kzalloc(sizeof(*gfi), GFP_KERNEL);
539 	if (!gfi) {
540 		err = -ENOMEM;
541 		goto out_err;
542 	}
543 
544 	parent = dget_parent(file->f_path.dentry);
545 	lower_file = dentry_open(&lo_p, file->f_flags, cred);
546 	if (IS_ERR(lower_file)) {
547 		err = PTR_ERR(lower_file);
548 		kfree(gfi);
549 	} else {
550 		gfi->lower_file = lower_file;
551 		file->private_data = gfi;
552 	}
553 	dput(parent);
554 out_err:
555 	dput(lo_p.dentry);
556 	return err;
557 }
558 
hmdfs_file_flush_merge(struct file * file,fl_owner_t id)559 int hmdfs_file_flush_merge(struct file *file, fl_owner_t id)
560 {
561 	struct hmdfs_file_info *gfi = hmdfs_f(file);
562 	struct file *lower_file = gfi->lower_file;
563 
564 	if (lower_file->f_op->flush)
565 		return lower_file->f_op->flush(lower_file, id);
566 
567 	return 0;
568 }
569 
570 /* Transparent transmission of parameters to device_view level,
571  * so file operations are same as device_view local operations.
572  */
573 const struct file_operations hmdfs_file_fops_merge = {
574 	.owner = THIS_MODULE,
575 	.llseek = hmdfs_file_llseek_local,
576 	.read_iter = hmdfs_merge_read_iter,
577 	.write_iter = hmdfs_merge_write_iter,
578 	.mmap = hmdfs_file_mmap_local,
579 	.open = hmdfs_file_open_merge,
580 	.flush = hmdfs_file_flush_merge,
581 	.release = hmdfs_file_release_local,
582 	.fsync = hmdfs_fsync_local,
583 	.splice_read = generic_file_splice_read,
584 	.splice_write = iter_file_splice_write,
585 };
586