• 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 #include "authority/authentication.h"
15 
16 struct hmdfs_iterate_callback_merge {
17 	struct dir_context ctx;
18 	struct dir_context *caller;
19 	/*
20 	 * Record the return value of 'caller->actor':
21 	 *
22 	 * -EINVAL, buffer is exhausted
23 	 * -EINTR, current task is pending
24 	 * -EFAULT, something is wrong
25 	 * 0, success and can do more
26 	 */
27 	int result;
28 	struct rb_root *root;
29 	uint64_t dev_id;
30 };
31 
32 struct hmdfs_cache_entry {
33 	struct rb_node rb_node;
34 	int name_len;
35 	char *name;
36 	int file_type;
37 };
38 
39 struct hmdfs_user_info {
40 	char *local_path;
41 	char *distributed_path;
42 	char *bundle_name;
43 };
44 
allocate_entry(const char * name,int namelen,int d_type)45 struct hmdfs_cache_entry *allocate_entry(const char *name, int namelen,
46 					 int d_type)
47 {
48 	struct hmdfs_cache_entry *data;
49 
50 	data = kmalloc(sizeof(*data), GFP_KERNEL);
51 	if (!data)
52 		return ERR_PTR(-ENOMEM);
53 
54 	data->name = kstrndup(name, namelen, GFP_KERNEL);
55 	if (!data->name) {
56 		kfree(data);
57 		return ERR_PTR(-ENOMEM);
58 	}
59 
60 	data->name_len = namelen;
61 	data->file_type = d_type;
62 
63 	return data;
64 }
65 
insert_filename(struct rb_root * root,struct hmdfs_cache_entry ** new_entry)66 int insert_filename(struct rb_root *root, struct hmdfs_cache_entry **new_entry)
67 {
68 	struct rb_node *parent = NULL;
69 	struct rb_node **new_node = &(root->rb_node);
70 	int cmp_res = 0;
71 	struct hmdfs_cache_entry *data = *new_entry;
72 
73 	while (*new_node) {
74 		struct hmdfs_cache_entry *entry = container_of(
75 			*new_node, struct hmdfs_cache_entry, rb_node);
76 		parent = *new_node;
77 
78 		if (data->name_len < entry->name_len)
79 			cmp_res = -1;
80 		else if (data->name_len > entry->name_len)
81 			cmp_res = 1;
82 		else
83 			cmp_res = strncmp(data->name, entry->name,
84 					  data->name_len);
85 
86 		if (!cmp_res) {
87 			kfree(data->name);
88 			kfree(data);
89 			*new_entry = entry;
90 			return entry->file_type;
91 		}
92 
93 		if (cmp_res < 0)
94 			new_node = &((*new_node)->rb_left);
95 		else if (cmp_res > 0)
96 			new_node = &((*new_node)->rb_right);
97 	}
98 
99 	rb_link_node(&data->rb_node, parent, new_node);
100 	rb_insert_color(&data->rb_node, root);
101 
102 	return 0;
103 }
104 
recursive_delete(struct rb_node * node)105 static void recursive_delete(struct rb_node *node)
106 {
107 	struct hmdfs_cache_entry *entry = NULL;
108 
109 	if (!node)
110 		return;
111 
112 	recursive_delete(node->rb_left);
113 	recursive_delete(node->rb_right);
114 
115 	entry = container_of(node, struct hmdfs_cache_entry, rb_node);
116 	kfree(entry->name);
117 	kfree(entry);
118 }
119 
destroy_tree(struct rb_root * root)120 static void destroy_tree(struct rb_root *root)
121 {
122 	if (!root)
123 		return;
124 	recursive_delete(root->rb_node);
125 	root->rb_node = NULL;
126 }
127 
delete_filename(struct rb_root * root,struct hmdfs_cache_entry * data)128 static void delete_filename(struct rb_root *root,
129 			    struct hmdfs_cache_entry *data)
130 {
131 	struct rb_node **node = &(root->rb_node);
132 	struct hmdfs_cache_entry *entry = NULL;
133 	int cmp_res = 0;
134 
135 	while (*node) {
136 		entry = container_of(*node, struct hmdfs_cache_entry, rb_node);
137 		if (data->name_len < entry->name_len)
138 			cmp_res = -1;
139 		else if (data->name_len > entry->name_len)
140 			cmp_res = 1;
141 		else
142 			cmp_res = strncmp(data->name, entry->name,
143 					  data->name_len);
144 
145 		if (!cmp_res)
146 			goto found;
147 
148 		if (cmp_res < 0)
149 			node = &((*node)->rb_left);
150 		else if (cmp_res > 0)
151 			node = &((*node)->rb_right);
152 	}
153 	return;
154 
155 found:
156 	rb_erase(*node, root);
157 	kfree(entry->name);
158 	kfree(entry);
159 }
160 
rename_conflicting_file(char * dentry_name,int * len,unsigned int dev_id)161 static void rename_conflicting_file(char *dentry_name, int *len,
162 				    unsigned int dev_id)
163 {
164 	int i = *len - 1;
165 	int dot_pos = -1;
166 	char *buffer;
167 
168 	buffer = kzalloc(DENTRY_NAME_MAX_LEN, GFP_KERNEL);
169 	if (!buffer)
170 		return;
171 
172 	while (i >= 0) {
173 		if (dentry_name[i] == '/')
174 			break;
175 		if (dentry_name[i] == '.') {
176 			// TODO: 这个修改同步到 CT01
177 			dot_pos = i;
178 			break;
179 		}
180 		i--;
181 	}
182 
183 	if (dot_pos == -1) {
184 		snprintf(dentry_name + *len, DENTRY_NAME_MAX_LEN - *len,
185 			 CONFLICTING_FILE_SUFFIX, dev_id);
186 		goto done;
187 	}
188 
189 	for (i = 0; i < *len - dot_pos; i++)
190 		buffer[i] = dentry_name[i + dot_pos];
191 
192 	buffer[i] = '\0';
193 	snprintf(dentry_name + dot_pos, DENTRY_NAME_MAX_LEN - dot_pos,
194 		 CONFLICTING_FILE_SUFFIX, dev_id);
195 	strcat(dentry_name, buffer);
196 
197 done:
198 	*len = strlen(dentry_name);
199 	kfree(buffer);
200 }
201 
rename_conflicting_directory(char * dentry_name,int * len)202 static void rename_conflicting_directory(char *dentry_name, int *len)
203 {
204 	snprintf(dentry_name + *len, DENTRY_NAME_MAX_LEN - *len,
205 		 CONFLICTING_DIR_SUFFIX);
206 	*len += strlen(CONFLICTING_DIR_SUFFIX);
207 }
208 
hmdfs_actor_merge(struct dir_context * ctx,const char * name,int namelen,loff_t offset,u64 ino,unsigned int d_type)209 static int hmdfs_actor_merge(struct dir_context *ctx, const char *name,
210 			     int namelen, loff_t offset, u64 ino,
211 			     unsigned int d_type)
212 {
213 	int ret = 0;
214 	int insert_res = 0;
215 	int max_devid_len = 2;
216 	char *dentry_name = NULL;
217 	int dentry_len = namelen;
218 	struct hmdfs_cache_entry *cache_entry = NULL;
219 	struct hmdfs_iterate_callback_merge *iterate_callback_merge = NULL;
220 	struct dir_context *org_ctx = NULL;
221 
222 	if (hmdfs_file_type(name) != HMDFS_TYPE_COMMON)
223 		return 0;
224 
225 	if (namelen > NAME_MAX)
226 		return -EINVAL;
227 	dentry_name = kzalloc(NAME_MAX + 1, GFP_KERNEL);
228 	if (!dentry_name)
229 		return -ENOMEM;
230 
231 	strncpy(dentry_name, name, dentry_len);
232 
233 	cache_entry = allocate_entry(dentry_name, dentry_len, d_type);
234 	if (IS_ERR(cache_entry)) {
235 		ret = PTR_ERR(cache_entry);
236 		goto done;
237 	}
238 
239 	iterate_callback_merge =
240 		container_of(ctx, struct hmdfs_iterate_callback_merge, ctx);
241 	insert_res =
242 		insert_filename(iterate_callback_merge->root, &cache_entry);
243 	if (d_type == DT_DIR && insert_res == DT_DIR) {
244 		goto done;
245 	} else if (d_type == DT_DIR &&
246 		  (insert_res == DT_REG || insert_res == DT_LNK)) {
247 		if (strlen(CONFLICTING_DIR_SUFFIX) > NAME_MAX - dentry_len) {
248 			ret = -ENAMETOOLONG;
249 			goto delete;
250 		}
251 		rename_conflicting_directory(dentry_name, &dentry_len);
252 		cache_entry->file_type = DT_DIR;
253 	} else if ((d_type == DT_REG || d_type == DT_LNK) && insert_res > 0) {
254 		if (strlen(CONFLICTING_FILE_SUFFIX) + max_devid_len >
255 		    NAME_MAX - dentry_len) {
256 			ret = -ENAMETOOLONG;
257 			goto delete;
258 		}
259 		rename_conflicting_file(dentry_name, &dentry_len,
260 					iterate_callback_merge->dev_id);
261 	}
262 
263 	org_ctx = iterate_callback_merge->caller;
264 	ret = org_ctx->actor(org_ctx, dentry_name, dentry_len, org_ctx->pos,
265 			     ino, d_type);
266 	/*
267 	 * Record original return value, so that the caller can be aware of
268 	 * different situations.
269 	 */
270 	iterate_callback_merge->result = ret;
271 	ret = ret == 0 ? 0 : 1;
272 	if (ret && d_type == DT_DIR && cache_entry->file_type == DT_DIR &&
273 	   (insert_res == DT_REG || insert_res == DT_LNK))
274 		cache_entry->file_type = DT_REG;
275 
276 delete:
277 	if (ret && !insert_res)
278 		delete_filename(iterate_callback_merge->root, cache_entry);
279 done:
280 	kfree(dentry_name);
281 	return ret;
282 }
283 
284 struct hmdfs_file_info *
get_next_hmdfs_file_info(struct hmdfs_file_info * fi_head,int device_id)285 get_next_hmdfs_file_info(struct hmdfs_file_info *fi_head, int device_id)
286 {
287 	struct hmdfs_file_info *fi_iter = NULL;
288 	struct hmdfs_file_info *fi_result = NULL;
289 
290 	mutex_lock(&fi_head->comrade_list_lock);
291 	list_for_each_entry_safe(fi_iter, fi_result, &(fi_head->comrade_list),
292 				  comrade_list) {
293 		if (fi_iter->device_id == device_id)
294 			break;
295 	}
296 	mutex_unlock(&fi_head->comrade_list_lock);
297 
298 	return fi_result != fi_head ? fi_result : NULL;
299 }
300 
get_hmdfs_file_info(struct hmdfs_file_info * fi_head,int device_id)301 struct hmdfs_file_info *get_hmdfs_file_info(struct hmdfs_file_info *fi_head,
302 					    int device_id)
303 {
304 	struct hmdfs_file_info *fi_iter = NULL;
305 
306 	mutex_lock(&fi_head->comrade_list_lock);
307 	list_for_each_entry(fi_iter, &(fi_head->comrade_list), comrade_list) {
308 		if (fi_iter->device_id == device_id) {
309 			mutex_unlock(&fi_head->comrade_list_lock);
310 			return fi_iter;
311 		}
312 	}
313 	mutex_unlock(&fi_head->comrade_list_lock);
314 
315 	return NULL;
316 }
317 
hmdfs_iterate_merge(struct file * file,struct dir_context * ctx)318 int hmdfs_iterate_merge(struct file *file, struct dir_context *ctx)
319 {
320 	int err = 0;
321 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
322 	struct hmdfs_file_info *fi_iter = NULL;
323 	struct file *lower_file_iter = NULL;
324 	loff_t start_pos = ctx->pos;
325 	unsigned long device_id = (unsigned long)((ctx->pos) << 1 >>
326 				  (POS_BIT_NUM - DEV_ID_BIT_NUM));
327 	struct hmdfs_iterate_callback_merge ctx_merge = {
328 		.ctx.actor = hmdfs_actor_merge,
329 		.caller = ctx,
330 		.root = &fi_head->root,
331 		.dev_id = device_id
332 	};
333 
334 	/* pos = -1 indicates that all devices have been traversed
335 	 * or an error has occurred.
336 	 */
337 	if (ctx->pos == -1)
338 		return 0;
339 
340 	fi_iter = get_hmdfs_file_info(fi_head, device_id);
341 	if (!fi_iter) {
342 		fi_iter = get_next_hmdfs_file_info(fi_head, device_id);
343 		// dev_id is changed, parameter is set 0 to get next file info
344 		if (fi_iter)
345 			ctx_merge.ctx.pos =
346 				hmdfs_set_pos(fi_iter->device_id, 0, 0);
347 	}
348 	while (fi_iter) {
349 		ctx_merge.dev_id = fi_iter->device_id;
350 		device_id = ctx_merge.dev_id;
351 		lower_file_iter = fi_iter->lower_file;
352 		lower_file_iter->f_pos = file->f_pos;
353 		err = iterate_dir(lower_file_iter, &ctx_merge.ctx);
354 		file->f_pos = lower_file_iter->f_pos;
355 		ctx->pos = file->f_pos;
356 
357 		if (err)
358 			goto done;
359 		/*
360 		 * ctx->actor return nonzero means buffer is exhausted or
361 		 * something is wrong, thus we should not continue.
362 		 */
363 		if (ctx_merge.result)
364 			goto done;
365 		fi_iter = get_next_hmdfs_file_info(fi_head, device_id);
366 		if (fi_iter) {
367 			file->f_pos = hmdfs_set_pos(fi_iter->device_id, 0, 0);
368 			ctx->pos = file->f_pos;
369 		}
370 	}
371 done:
372 	trace_hmdfs_iterate_merge(file->f_path.dentry, start_pos, ctx->pos,
373 				  err);
374 	return err;
375 }
376 
do_dir_open_merge(struct file * file,const struct cred * cred,struct hmdfs_file_info * fi_head)377 int do_dir_open_merge(struct file *file, const struct cred *cred,
378 		      struct hmdfs_file_info *fi_head)
379 {
380 	int ret = -EINVAL;
381 	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(file->f_path.dentry);
382 	struct hmdfs_dentry_comrade *comrade = NULL;
383 	struct hmdfs_file_info *fi = NULL;
384 	struct path lo_p = { .mnt = file->f_path.mnt };
385 	struct file *lower_file = NULL;
386 
387 	if (IS_ERR_OR_NULL(cred))
388 		return ret;
389 
390 	wait_event(dim->wait_queue, !has_merge_lookup_work(dim));
391 
392 	mutex_lock(&dim->comrade_list_lock);
393 	list_for_each_entry(comrade, &(dim->comrade_list), list) {
394 		fi = kzalloc(sizeof(*fi), GFP_KERNEL);
395 		if (!fi) {
396 			ret = ret ? -ENOMEM : 0;
397 			continue; // allow some dir to fail to open
398 		}
399 		lo_p.dentry = comrade->lo_d;
400 		// make sure that dentry will not be dentry_kill before open
401 		dget(lo_p.dentry);
402 		if (unlikely(d_is_negative(lo_p.dentry))) {
403 			hmdfs_info("dentry is negative, try again");
404 			kfree(fi);
405 			dput(lo_p.dentry);
406 			continue;  // skip this device
407 		}
408 		lower_file = dentry_open(&lo_p, file->f_flags, cred);
409 		dput(lo_p.dentry);
410 		if (IS_ERR(lower_file)) {
411 			kfree(fi);
412 			continue;
413 		}
414 		ret = 0;
415 		fi->device_id = comrade->dev_id;
416 		fi->lower_file = lower_file;
417 		mutex_lock(&fi_head->comrade_list_lock);
418 		list_add_tail(&fi->comrade_list, &fi_head->comrade_list);
419 		mutex_unlock(&fi_head->comrade_list_lock);
420 	}
421 	mutex_unlock(&dim->comrade_list_lock);
422 	return ret;
423 }
424 
hmdfs_dir_open_merge(struct inode * inode,struct file * file)425 int hmdfs_dir_open_merge(struct inode *inode, struct file *file)
426 {
427 	int ret = 0;
428 	struct hmdfs_file_info *fi = NULL;
429 
430 	fi = kzalloc(sizeof(*fi), GFP_KERNEL);
431 	if (!fi)
432 		return -ENOMEM;
433 
434 	file->private_data = fi;
435 	fi->root = RB_ROOT;
436 	mutex_init(&fi->comrade_list_lock);
437 	INIT_LIST_HEAD(&fi->comrade_list);
438 
439 	ret = do_dir_open_merge(file, hmdfs_sb(inode->i_sb)->cred, fi);
440 	if (ret)
441 		kfree(fi);
442 
443 	return ret;
444 }
445 
hmdfs_dir_release_merge(struct inode * inode,struct file * file)446 int hmdfs_dir_release_merge(struct inode *inode, struct file *file)
447 {
448 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
449 	struct hmdfs_file_info *fi_iter = NULL;
450 	struct hmdfs_file_info *fi_temp = NULL;
451 
452 	mutex_lock(&fi_head->comrade_list_lock);
453 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
454 				  comrade_list) {
455 		list_del_init(&(fi_iter->comrade_list));
456 		fput(fi_iter->lower_file);
457 		kfree(fi_iter);
458 	}
459 	mutex_unlock(&fi_head->comrade_list_lock);
460 	destroy_tree(&fi_head->root);
461 	file->private_data = NULL;
462 	kfree(fi_head);
463 
464 	return 0;
465 }
466 
467 static long hmdfs_ioc_get_dst_path(struct file *filp, unsigned long arg);
468 
hmdfs_dir_unlocked_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)469 long hmdfs_dir_unlocked_ioctl_merge(struct file *file, unsigned int cmd,
470 							unsigned long arg)
471 {
472 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
473 	struct hmdfs_file_info *fi_iter = NULL;
474 	struct hmdfs_file_info *fi_temp = NULL;
475 	struct file *lower_file = NULL;
476 	int error = -ENOTTY;
477 
478 	if (cmd == HMDFS_IOC_GET_DST_PATH)
479 		return hmdfs_ioc_get_dst_path(file, arg);
480 	mutex_lock(&fi_head->comrade_list_lock);
481 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
482 				  comrade_list) {
483 		if (fi_iter->device_id == 0) {
484 			lower_file = fi_iter->lower_file;
485 			if (lower_file->f_op->unlocked_ioctl)
486 				error = lower_file->f_op->unlocked_ioctl(
487 					lower_file, cmd, arg);
488 			break;
489 		}
490 	}
491 	mutex_unlock(&fi_head->comrade_list_lock);
492 	return error;
493 }
494 
hmdfs_dir_compat_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)495 long hmdfs_dir_compat_ioctl_merge(struct file *file, unsigned int cmd,
496 							unsigned long arg)
497 {
498 	struct hmdfs_file_info *fi_head = hmdfs_f(file);
499 	struct hmdfs_file_info *fi_iter = NULL;
500 	struct hmdfs_file_info *fi_temp = NULL;
501 	struct file *lower_file = NULL;
502 	int error = -ENOTTY;
503 
504 	if (cmd == HMDFS_IOC_GET_DST_PATH)
505 		return hmdfs_ioc_get_dst_path(file, arg);
506 	mutex_lock(&fi_head->comrade_list_lock);
507 	list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
508 				  comrade_list) {
509 		if (fi_iter->device_id == 0) {
510 			lower_file = fi_iter->lower_file;
511 			if (lower_file->f_op->compat_ioctl)
512 				error = lower_file->f_op->compat_ioctl(
513 					lower_file, cmd, arg);
514 			break;
515 		}
516 	}
517 	mutex_unlock(&fi_head->comrade_list_lock);
518 	return error;
519 }
520 
521 const struct file_operations hmdfs_dir_fops_merge = {
522 	.owner = THIS_MODULE,
523 	.iterate = hmdfs_iterate_merge,
524 	.open = hmdfs_dir_open_merge,
525 	.release = hmdfs_dir_release_merge,
526 	.unlocked_ioctl = hmdfs_dir_unlocked_ioctl_merge,
527 	.compat_ioctl = hmdfs_dir_compat_ioctl_merge,
528 };
529 
hmdfs_merge_read_iter(struct kiocb * iocb,struct iov_iter * iter)530 static ssize_t hmdfs_merge_read_iter(struct kiocb *iocb, struct iov_iter *iter)
531 {
532 	return hmdfs_do_read_iter(iocb->ki_filp, iter, &iocb->ki_pos);
533 }
534 
hmdfs_merge_write_iter(struct kiocb * iocb,struct iov_iter * iter)535 ssize_t hmdfs_merge_write_iter(struct kiocb *iocb, struct iov_iter *iter)
536 {
537 	return hmdfs_do_write_iter(iocb->ki_filp, iter, &iocb->ki_pos);
538 }
539 
hmdfs_file_open_merge(struct inode * inode,struct file * file)540 int hmdfs_file_open_merge(struct inode *inode, struct file *file)
541 {
542 	int err = 0;
543 	struct file *lower_file = NULL;
544 	struct path lo_p = { .mnt = file->f_path.mnt };
545 	struct super_block *sb = inode->i_sb;
546 	const struct cred *cred = hmdfs_sb(sb)->cred;
547 	struct hmdfs_file_info *gfi = NULL;
548 	struct dentry *parent = NULL;
549 
550 	lo_p.dentry = hmdfs_get_fst_lo_d(file->f_path.dentry);
551 	if (!lo_p.dentry) {
552 		err = -EINVAL;
553 		goto out_err;
554 	}
555 
556 	gfi = kzalloc(sizeof(*gfi), GFP_KERNEL);
557 	if (!gfi) {
558 		err = -ENOMEM;
559 		goto out_err;
560 	}
561 
562 	parent = dget_parent(file->f_path.dentry);
563 	lower_file = dentry_open(&lo_p, file->f_flags, cred);
564 	if (IS_ERR(lower_file)) {
565 		err = PTR_ERR(lower_file);
566 		kfree(gfi);
567 	} else {
568 		gfi->lower_file = lower_file;
569 		file->private_data = gfi;
570 	}
571 	dput(parent);
572 out_err:
573 	dput(lo_p.dentry);
574 	return err;
575 }
576 
hmdfs_file_flush_merge(struct file * file,fl_owner_t id)577 int hmdfs_file_flush_merge(struct file *file, fl_owner_t id)
578 {
579 	struct hmdfs_file_info *gfi = hmdfs_f(file);
580 	struct file *lower_file = gfi->lower_file;
581 
582 	if (lower_file->f_op->flush)
583 		return lower_file->f_op->flush(lower_file, id);
584 
585 	return 0;
586 }
587 
hmdfs_ioc_get_writeopen_cnt(struct file * filp,unsigned long arg)588 static long hmdfs_ioc_get_writeopen_cnt(struct file *filp, unsigned long arg)
589 {
590 	struct hmdfs_file_info *gfi = hmdfs_f(filp);
591 	struct file *lower_file = gfi->lower_file;
592 	struct inode *lower_inode = file_inode(lower_file);
593 
594 	u32 wo_cnt = atomic_read(&(hmdfs_i(lower_inode))->write_opened);
595 
596 	return put_user(wo_cnt, (int __user *)arg);
597 }
598 
copy_string_from_user(unsigned long pos,unsigned long len,char ** data)599 static int copy_string_from_user(unsigned long pos, unsigned long len,
600 				char **data)
601 {
602 	char *tmp_data;
603 
604 	if (!access_ok((char *)pos, len))
605 		return -EFAULT;
606 
607 	tmp_data = kmalloc(len, GFP_KERNEL);
608 	if (!tmp_data)
609 		return -ENOMEM;
610 	*data = tmp_data;
611 
612 	if (copy_from_user(tmp_data, (char __user *)pos, len)){
613 		return -EFAULT;
614 	}
615 
616 	return 0;
617 }
618 
hmdfs_get_info_from_user(unsigned long pos,struct hmdfs_dst_info * hdi,struct hmdfs_user_info * data)619 static int hmdfs_get_info_from_user(unsigned long pos,
620 		struct hmdfs_dst_info *hdi, struct hmdfs_user_info *data)
621 {
622 	int ret = 0;
623 
624 	if (!access_ok((struct hmdfs_dst_info __user *)pos,
625 			sizeof(struct hmdfs_dst_info)))
626 		return -ENOMEM;
627 	if (copy_from_user(hdi, (struct hmdfs_dst_info __user *)pos,
628 			sizeof(struct hmdfs_dst_info)))
629 		return -EFAULT;
630 
631 	ret = copy_string_from_user(hdi->local_path_pos, hdi->local_path_len,
632 				    &data->local_path);
633 	if (ret != 0)
634 		return ret;
635 
636 	ret = copy_string_from_user(hdi->distributed_path_pos,
637 				    hdi->distributed_path_len,
638 				    &data->distributed_path);
639 	if (ret != 0)
640 		return ret;
641 
642 	ret = copy_string_from_user(hdi->bundle_name_pos, hdi->bundle_name_len,
643 				    &data->bundle_name);
644 	if (ret != 0)
645 		return ret;
646 
647 	return 0;
648 }
649 
change_cred(struct dentry * dentry,const char * bundle_name)650 static const struct cred *change_cred(struct dentry *dentry,
651 				      const char *bundle_name)
652 {
653 	int bid;
654 	struct cred *cred = NULL;
655 	const struct cred *old_cred = NULL;
656 
657 	cred = prepare_creds();
658 	if (!cred) {
659 		return NULL;
660 	}
661 	bid = get_bundle_uid(hmdfs_sb(dentry->d_sb), bundle_name);
662 	if (bid != 0) {
663 		cred->fsuid = KUIDT_INIT(bid);
664 		cred->fsgid = KGIDT_INIT(bid);
665 		old_cred = override_creds(cred);
666 	}
667 
668 	return old_cred;
669 }
670 
get_file_size(const char * path_value,uint64_t pos)671 static int get_file_size(const char *path_value, uint64_t pos)
672 {
673 	int ret;
674 	uint64_t size;
675 	struct path path;
676 	struct kstat buf;
677 
678 	ret = kern_path(path_value, 0, &path);
679 	if (ret)
680 		return ret;
681 	ret = vfs_getattr(&path, &buf, STATX_BASIC_STATS | STATX_BTIME, 0);
682 	path_put(&path);
683 	if (ret) {
684 		hmdfs_err("call vfs_getattr failed, err %d", ret);
685 		return ret;
686 	}
687 
688 	size = buf.size;
689 	ret = copy_to_user((uint64_t __user *)pos, &size, sizeof(uint64_t));
690 	return ret;
691 }
692 
create_link_file(struct hmdfs_user_info * data)693 static int create_link_file(struct hmdfs_user_info *data)
694 {
695 	int ret;
696 	struct dentry *dentry;
697 	struct path path;
698 
699 	ret = kern_path(data->distributed_path, 0, &path);
700 	if (ret == 0){
701 		path_put(&path);
702 		return ret;
703 	}
704 
705 	dentry = kern_path_create(AT_FDCWD, data->distributed_path, &path, 0);
706 	if (IS_ERR(dentry))
707 		return PTR_ERR(dentry);
708 	ret = vfs_symlink(path.dentry->d_inode, dentry, data->local_path);
709 	done_path_create(&path, dentry);
710 
711 	return ret;
712 }
713 
create_dir(const char * path_value,mode_t mode)714 static int create_dir(const char *path_value, mode_t mode)
715 {
716 	int err = 0;
717 	struct path path;
718 	struct dentry *dentry;
719 
720 	dentry = kern_path_create(AT_FDCWD, path_value, &path, LOOKUP_DIRECTORY);
721 	if(PTR_ERR(dentry) == -EEXIST)
722 		return 0;
723 	if (IS_ERR(dentry))
724 		return PTR_ERR(dentry);
725 
726 	err = vfs_mkdir(d_inode(path.dentry), dentry, mode);
727 	if (err && err != -EEXIST)
728 		hmdfs_err("vfs_mkdir failed, err = %d", err);
729 	done_path_create(&path, dentry);
730 
731 	return err;
732 }
733 
create_dir_recursive(const char * path_value,mode_t mode)734 static int create_dir_recursive(const char *path_value, mode_t mode)
735 {
736 	int err = 0;
737 	char *tmp_path = kstrdup(path_value, GFP_KERNEL);
738 	char *p = tmp_path;
739 
740 	if (!tmp_path)
741 		return -ENOMEM;
742 
743 	if (*p == '/')
744 		p++;
745 
746 	while (*p) {
747 		if (*p == '/') {
748 			*p = '\0';
749 			err = create_dir(tmp_path, mode);
750 			if (err != 0)
751 				break;
752 			*p = '/';
753 		}
754 		p++;
755 	}
756 
757 	kfree(tmp_path);
758 	return err;
759 }
760 
hmdfs_ioc_get_dst_path(struct file * filp,unsigned long arg)761 static long hmdfs_ioc_get_dst_path(struct file *filp, unsigned long arg)
762 {
763 	int ret = 0;
764 	const struct cred *old_cred;
765 	struct hmdfs_dst_info hdi;
766 	struct hmdfs_user_info *data;
767 
768 	data = kzalloc(sizeof(*data), GFP_KERNEL);
769 	if (!data) {
770 		ret = -ENOMEM;
771 		goto err_free_data;
772 	}
773 
774 	ret = hmdfs_get_info_from_user(arg, &hdi, data);
775 	if (ret != 0)
776 		goto err_free_all;
777 
778 	old_cred = change_cred(filp->f_path.dentry, data->bundle_name);
779 	if (!old_cred) {
780 		ret = -EACCES;
781 		goto err_free_all;
782 	}
783 
784 	ret = create_dir_recursive(data->distributed_path, DIR_MODE);
785 	if (ret != 0)
786 		goto err_revert;
787 
788 	ret = create_link_file(data);
789 	if (ret != 0 && ret != -EEXIST)
790 		goto err_revert;
791 
792 	ret = get_file_size(data->local_path, hdi.size);
793 
794 err_revert:
795 	revert_creds(old_cred);
796 err_free_all:
797 	kfree(data->local_path);
798 	kfree(data->distributed_path);
799 	kfree(data->bundle_name);
800 err_free_data:
801 	kfree(data);
802 	return ret;
803 }
804 
hmdfs_file_ioctl_merge(struct file * filp,unsigned int cmd,unsigned long arg)805 static long hmdfs_file_ioctl_merge(struct file *filp, unsigned int cmd, unsigned long arg)
806 {
807 	switch (cmd) {
808 	case HMDFS_IOC_GET_WRITEOPEN_CNT:
809 		return hmdfs_ioc_get_writeopen_cnt(filp, arg);
810 	case HMDFS_IOC_GET_DST_PATH:
811 		return hmdfs_ioc_get_dst_path(filp, arg);
812 	default:
813 		return -ENOTTY;
814 	}
815 }
816 
817 /* Transparent transmission of parameters to device_view level,
818  * so file operations are same as device_view local operations.
819  */
820 const struct file_operations hmdfs_file_fops_merge = {
821 	.owner = THIS_MODULE,
822 	.llseek = hmdfs_file_llseek_local,
823 	.read_iter = hmdfs_merge_read_iter,
824 	.write_iter = hmdfs_merge_write_iter,
825 	.mmap = hmdfs_file_mmap_local,
826 	.open = hmdfs_file_open_merge,
827 	.flush = hmdfs_file_flush_merge,
828 	.release = hmdfs_file_release_local,
829 	.fsync = hmdfs_fsync_local,
830 	.unlocked_ioctl	= hmdfs_file_ioctl_merge,
831 	.compat_ioctl = hmdfs_file_ioctl_merge,
832 	.splice_read = generic_file_splice_read,
833 	.splice_write = iter_file_splice_write,
834 };
835