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