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