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