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 mutex_lock(&dim->comrade_list_lock);
383 list_for_each_entry(comrade, &(dim->comrade_list), list) {
384 fi = kzalloc(sizeof(*fi), GFP_KERNEL);
385 if (!fi) {
386 ret = ret ? -ENOMEM : 0;
387 continue; // allow some dir to fail to open
388 }
389 lo_p.dentry = comrade->lo_d;
390 // make sure that dentry will not be dentry_kill before open
391 dget(lo_p.dentry);
392 if (unlikely(d_is_negative(lo_p.dentry))) {
393 hmdfs_info("dentry is negative, try again");
394 kfree(fi);
395 dput(lo_p.dentry);
396 continue; // skip this device
397 }
398 lower_file = dentry_open(&lo_p, file->f_flags, cred);
399 dput(lo_p.dentry);
400 if (IS_ERR(lower_file)) {
401 kfree(fi);
402 continue;
403 }
404 ret = 0;
405 fi->device_id = comrade->dev_id;
406 fi->lower_file = lower_file;
407 mutex_lock(&fi_head->comrade_list_lock);
408 list_add_tail(&fi->comrade_list, &fi_head->comrade_list);
409 mutex_unlock(&fi_head->comrade_list_lock);
410 }
411 mutex_unlock(&dim->comrade_list_lock);
412 return ret;
413 }
414
hmdfs_dir_open_merge(struct inode * inode,struct file * file)415 int hmdfs_dir_open_merge(struct inode *inode, struct file *file)
416 {
417 int ret = 0;
418 struct hmdfs_file_info *fi = NULL;
419
420 fi = kzalloc(sizeof(*fi), GFP_KERNEL);
421 if (!fi)
422 return -ENOMEM;
423
424 file->private_data = fi;
425 fi->root = RB_ROOT;
426 mutex_init(&fi->comrade_list_lock);
427 INIT_LIST_HEAD(&fi->comrade_list);
428
429 ret = do_dir_open_merge(file, hmdfs_sb(inode->i_sb)->cred, fi);
430 if (ret)
431 kfree(fi);
432
433 return ret;
434 }
435
hmdfs_dir_release_merge(struct inode * inode,struct file * file)436 int hmdfs_dir_release_merge(struct inode *inode, struct file *file)
437 {
438 struct hmdfs_file_info *fi_head = hmdfs_f(file);
439 struct hmdfs_file_info *fi_iter = NULL;
440 struct hmdfs_file_info *fi_temp = NULL;
441
442 mutex_lock(&fi_head->comrade_list_lock);
443 list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
444 comrade_list) {
445 list_del_init(&(fi_iter->comrade_list));
446 fput(fi_iter->lower_file);
447 kfree(fi_iter);
448 }
449 mutex_unlock(&fi_head->comrade_list_lock);
450 destroy_tree(&fi_head->root);
451 file->private_data = NULL;
452 kfree(fi_head);
453
454 return 0;
455 }
456
hmdfs_dir_unlocked_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)457 long hmdfs_dir_unlocked_ioctl_merge(struct file *file, unsigned int cmd,
458 unsigned long arg)
459 {
460 struct hmdfs_file_info *fi_head = hmdfs_f(file);
461 struct hmdfs_file_info *fi_iter = NULL;
462 struct hmdfs_file_info *fi_temp = NULL;
463 struct file *lower_file = NULL;
464 int error = -ENOTTY;
465
466 mutex_lock(&fi_head->comrade_list_lock);
467 list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
468 comrade_list) {
469 if (fi_iter->device_id == 0) {
470 lower_file = fi_iter->lower_file;
471 error = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
472 break;
473 }
474 }
475 mutex_unlock(&fi_head->comrade_list_lock);
476 return error;
477 }
478
hmdfs_dir_compat_ioctl_merge(struct file * file,unsigned int cmd,unsigned long arg)479 long hmdfs_dir_compat_ioctl_merge(struct file *file, unsigned int cmd,
480 unsigned long arg)
481 {
482 struct hmdfs_file_info *fi_head = hmdfs_f(file);
483 struct hmdfs_file_info *fi_iter = NULL;
484 struct hmdfs_file_info *fi_temp = NULL;
485 struct file *lower_file = NULL;
486 int error = -ENOTTY;
487
488 mutex_lock(&fi_head->comrade_list_lock);
489 list_for_each_entry_safe(fi_iter, fi_temp, &(fi_head->comrade_list),
490 comrade_list) {
491 if (fi_iter->device_id == 0) {
492 lower_file = fi_iter->lower_file;
493 error = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
494 break;
495 }
496 }
497 mutex_unlock(&fi_head->comrade_list_lock);
498 return error;
499 }
500
501 const struct file_operations hmdfs_dir_fops_merge = {
502 .owner = THIS_MODULE,
503 .iterate = hmdfs_iterate_merge,
504 .open = hmdfs_dir_open_merge,
505 .release = hmdfs_dir_release_merge,
506 .unlocked_ioctl = hmdfs_dir_unlocked_ioctl_merge,
507 .compat_ioctl = hmdfs_dir_compat_ioctl_merge,
508 };
509
hmdfs_merge_read_iter(struct kiocb * iocb,struct iov_iter * iter)510 static ssize_t hmdfs_merge_read_iter(struct kiocb *iocb, struct iov_iter *iter)
511 {
512 return hmdfs_do_read_iter(iocb->ki_filp, iter, &iocb->ki_pos);
513 }
514
hmdfs_merge_write_iter(struct kiocb * iocb,struct iov_iter * iter)515 ssize_t hmdfs_merge_write_iter(struct kiocb *iocb, struct iov_iter *iter)
516 {
517 return hmdfs_do_write_iter(iocb->ki_filp, iter, &iocb->ki_pos);
518 }
519
hmdfs_file_open_merge(struct inode * inode,struct file * file)520 int hmdfs_file_open_merge(struct inode *inode, struct file *file)
521 {
522 int err = 0;
523 struct file *lower_file = NULL;
524 struct path lo_p = { .mnt = file->f_path.mnt };
525 struct super_block *sb = inode->i_sb;
526 const struct cred *cred = hmdfs_sb(sb)->cred;
527 struct hmdfs_file_info *gfi = NULL;
528 struct dentry *parent = NULL;
529
530 lo_p.dentry = hmdfs_get_fst_lo_d(file->f_path.dentry);
531 if (!lo_p.dentry) {
532 err = -EINVAL;
533 goto out_err;
534 }
535
536 gfi = kzalloc(sizeof(*gfi), GFP_KERNEL);
537 if (!gfi) {
538 err = -ENOMEM;
539 goto out_err;
540 }
541
542 parent = dget_parent(file->f_path.dentry);
543 lower_file = dentry_open(&lo_p, file->f_flags, cred);
544 if (IS_ERR(lower_file)) {
545 err = PTR_ERR(lower_file);
546 kfree(gfi);
547 } else {
548 gfi->lower_file = lower_file;
549 file->private_data = gfi;
550 }
551 dput(parent);
552 out_err:
553 dput(lo_p.dentry);
554 return err;
555 }
556
hmdfs_file_flush_merge(struct file * file,fl_owner_t id)557 int hmdfs_file_flush_merge(struct file *file, fl_owner_t id)
558 {
559 struct hmdfs_file_info *gfi = hmdfs_f(file);
560 struct file *lower_file = gfi->lower_file;
561
562 if (lower_file->f_op->flush)
563 return lower_file->f_op->flush(lower_file, id);
564
565 return 0;
566 }
567
568 /* Transparent transmission of parameters to device_view level,
569 * so file operations are same as device_view local operations.
570 */
571 const struct file_operations hmdfs_file_fops_merge = {
572 .owner = THIS_MODULE,
573 .llseek = hmdfs_file_llseek_local,
574 .read_iter = hmdfs_merge_read_iter,
575 .write_iter = hmdfs_merge_write_iter,
576 .mmap = hmdfs_file_mmap_local,
577 .open = hmdfs_file_open_merge,
578 .flush = hmdfs_file_flush_merge,
579 .release = hmdfs_file_release_local,
580 .fsync = hmdfs_fsync_local,
581 };
582