| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (C) 2011 Novell Inc. |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 6 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 7 | | - * the Free Software Foundation. |
|---|
| 8 | 5 | */ |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | #include <linux/fs.h> |
|---|
| .. | .. |
|---|
| 21 | 18 | |
|---|
| 22 | 19 | static unsigned short ovl_redirect_max = 256; |
|---|
| 23 | 20 | module_param_named(redirect_max, ovl_redirect_max, ushort, 0644); |
|---|
| 24 | | -MODULE_PARM_DESC(ovl_redirect_max, |
|---|
| 21 | +MODULE_PARM_DESC(redirect_max, |
|---|
| 25 | 22 | "Maximum length of absolute redirect xattr value"); |
|---|
| 26 | 23 | |
|---|
| 27 | 24 | static int ovl_set_redirect(struct dentry *dentry, bool samedir); |
|---|
| .. | .. |
|---|
| 38 | 35 | dput(wdentry); |
|---|
| 39 | 36 | |
|---|
| 40 | 37 | if (err) { |
|---|
| 41 | | - pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n", |
|---|
| 38 | + pr_err("cleanup of '%pd2' failed (%i)\n", |
|---|
| 42 | 39 | wdentry, err); |
|---|
| 43 | 40 | } |
|---|
| 44 | 41 | |
|---|
| 45 | 42 | return err; |
|---|
| 46 | 43 | } |
|---|
| 47 | 44 | |
|---|
| 48 | | -static struct dentry *ovl_lookup_temp(struct dentry *workdir) |
|---|
| 45 | +struct dentry *ovl_lookup_temp(struct dentry *workdir) |
|---|
| 49 | 46 | { |
|---|
| 50 | 47 | struct dentry *temp; |
|---|
| 51 | 48 | char name[20]; |
|---|
| .. | .. |
|---|
| 56 | 53 | |
|---|
| 57 | 54 | temp = lookup_one_len(name, workdir, strlen(name)); |
|---|
| 58 | 55 | if (!IS_ERR(temp) && temp->d_inode) { |
|---|
| 59 | | - pr_err("overlayfs: workdir/%s already exists\n", name); |
|---|
| 56 | + pr_err("workdir/%s already exists\n", name); |
|---|
| 60 | 57 | dput(temp); |
|---|
| 61 | 58 | temp = ERR_PTR(-EIO); |
|---|
| 62 | 59 | } |
|---|
| .. | .. |
|---|
| 65 | 62 | } |
|---|
| 66 | 63 | |
|---|
| 67 | 64 | /* caller holds i_mutex on workdir */ |
|---|
| 68 | | -static struct dentry *ovl_whiteout(struct dentry *workdir) |
|---|
| 65 | +static struct dentry *ovl_whiteout(struct ovl_fs *ofs) |
|---|
| 69 | 66 | { |
|---|
| 70 | 67 | int err; |
|---|
| 71 | 68 | struct dentry *whiteout; |
|---|
| 69 | + struct dentry *workdir = ofs->workdir; |
|---|
| 72 | 70 | struct inode *wdir = workdir->d_inode; |
|---|
| 73 | 71 | |
|---|
| 74 | | - whiteout = ovl_lookup_temp(workdir); |
|---|
| 75 | | - if (IS_ERR(whiteout)) |
|---|
| 76 | | - return whiteout; |
|---|
| 72 | + if (!ofs->whiteout) { |
|---|
| 73 | + whiteout = ovl_lookup_temp(workdir); |
|---|
| 74 | + if (IS_ERR(whiteout)) |
|---|
| 75 | + goto out; |
|---|
| 77 | 76 | |
|---|
| 78 | | - err = ovl_do_whiteout(wdir, whiteout); |
|---|
| 79 | | - if (err) { |
|---|
| 80 | | - dput(whiteout); |
|---|
| 81 | | - whiteout = ERR_PTR(err); |
|---|
| 77 | + err = ovl_do_whiteout(wdir, whiteout); |
|---|
| 78 | + if (err) { |
|---|
| 79 | + dput(whiteout); |
|---|
| 80 | + whiteout = ERR_PTR(err); |
|---|
| 81 | + goto out; |
|---|
| 82 | + } |
|---|
| 83 | + ofs->whiteout = whiteout; |
|---|
| 82 | 84 | } |
|---|
| 83 | 85 | |
|---|
| 86 | + if (ofs->share_whiteout) { |
|---|
| 87 | + whiteout = ovl_lookup_temp(workdir); |
|---|
| 88 | + if (IS_ERR(whiteout)) |
|---|
| 89 | + goto out; |
|---|
| 90 | + |
|---|
| 91 | + err = ovl_do_link(ofs->whiteout, wdir, whiteout); |
|---|
| 92 | + if (!err) |
|---|
| 93 | + goto out; |
|---|
| 94 | + |
|---|
| 95 | + if (err != -EMLINK) { |
|---|
| 96 | + pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n", |
|---|
| 97 | + ofs->whiteout->d_inode->i_nlink, err); |
|---|
| 98 | + ofs->share_whiteout = false; |
|---|
| 99 | + } |
|---|
| 100 | + dput(whiteout); |
|---|
| 101 | + } |
|---|
| 102 | + whiteout = ofs->whiteout; |
|---|
| 103 | + ofs->whiteout = NULL; |
|---|
| 104 | +out: |
|---|
| 84 | 105 | return whiteout; |
|---|
| 85 | 106 | } |
|---|
| 86 | 107 | |
|---|
| 87 | 108 | /* Caller must hold i_mutex on both workdir and dir */ |
|---|
| 88 | | -int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir, |
|---|
| 109 | +int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir, |
|---|
| 89 | 110 | struct dentry *dentry) |
|---|
| 90 | 111 | { |
|---|
| 91 | | - struct inode *wdir = workdir->d_inode; |
|---|
| 112 | + struct inode *wdir = ofs->workdir->d_inode; |
|---|
| 92 | 113 | struct dentry *whiteout; |
|---|
| 93 | 114 | int err; |
|---|
| 94 | 115 | int flags = 0; |
|---|
| 95 | 116 | |
|---|
| 96 | | - whiteout = ovl_whiteout(workdir); |
|---|
| 117 | + whiteout = ovl_whiteout(ofs); |
|---|
| 97 | 118 | err = PTR_ERR(whiteout); |
|---|
| 98 | 119 | if (IS_ERR(whiteout)) |
|---|
| 99 | 120 | return err; |
|---|
| .. | .. |
|---|
| 136 | 157 | d = lookup_one_len(dentry->d_name.name, dentry->d_parent, |
|---|
| 137 | 158 | dentry->d_name.len); |
|---|
| 138 | 159 | if (IS_ERR(d)) { |
|---|
| 139 | | - pr_warn("overlayfs: failed lookup after mkdir (%pd2, err=%i).\n", |
|---|
| 160 | + pr_warn("failed lookup after mkdir (%pd2, err=%i).\n", |
|---|
| 140 | 161 | dentry, err); |
|---|
| 141 | 162 | return PTR_ERR(d); |
|---|
| 142 | 163 | } |
|---|
| .. | .. |
|---|
| 245 | 266 | |
|---|
| 246 | 267 | ovl_dir_modified(dentry->d_parent, false); |
|---|
| 247 | 268 | ovl_dentry_set_upper_alias(dentry); |
|---|
| 269 | + ovl_dentry_init_reval(dentry, newdentry); |
|---|
| 270 | + |
|---|
| 248 | 271 | if (!hardlink) { |
|---|
| 249 | 272 | /* |
|---|
| 250 | 273 | * ovl_obtain_alias() can be called after ovl_create_real() |
|---|
| .. | .. |
|---|
| 261 | 284 | inode = ovl_get_inode(dentry->d_sb, &oip); |
|---|
| 262 | 285 | if (IS_ERR(inode)) |
|---|
| 263 | 286 | return PTR_ERR(inode); |
|---|
| 287 | + if (inode == oip.newinode) |
|---|
| 288 | + ovl_set_flag(OVL_UPPERDATA, inode); |
|---|
| 264 | 289 | } else { |
|---|
| 265 | 290 | WARN_ON(ovl_inode_real(inode) != d_inode(newdentry)); |
|---|
| 266 | 291 | dput(newdentry); |
|---|
| .. | .. |
|---|
| 269 | 294 | |
|---|
| 270 | 295 | d_instantiate(dentry, inode); |
|---|
| 271 | 296 | if (inode != oip.newinode) { |
|---|
| 272 | | - pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n", |
|---|
| 297 | + pr_warn_ratelimited("newly created inode found in cache (%pd2)\n", |
|---|
| 273 | 298 | dentry); |
|---|
| 274 | 299 | } |
|---|
| 275 | 300 | |
|---|
| .. | .. |
|---|
| 367 | 392 | if (IS_ERR(opaquedir)) |
|---|
| 368 | 393 | goto out_unlock; |
|---|
| 369 | 394 | |
|---|
| 370 | | - err = ovl_copy_xattr(upper, opaquedir); |
|---|
| 395 | + err = ovl_copy_xattr(dentry->d_sb, upper, opaquedir); |
|---|
| 371 | 396 | if (err) |
|---|
| 372 | 397 | goto out_cleanup; |
|---|
| 373 | 398 | |
|---|
| .. | .. |
|---|
| 413 | 438 | if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl) |
|---|
| 414 | 439 | return 0; |
|---|
| 415 | 440 | |
|---|
| 416 | | - size = posix_acl_to_xattr(NULL, acl, NULL, 0); |
|---|
| 441 | + size = posix_acl_xattr_size(acl->a_count); |
|---|
| 417 | 442 | buffer = kmalloc(size, GFP_KERNEL); |
|---|
| 418 | 443 | if (!buffer) |
|---|
| 419 | 444 | return -ENOMEM; |
|---|
| 420 | 445 | |
|---|
| 421 | | - size = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); |
|---|
| 422 | | - err = size; |
|---|
| 446 | + err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); |
|---|
| 423 | 447 | if (err < 0) |
|---|
| 424 | 448 | goto out_free; |
|---|
| 425 | 449 | |
|---|
| .. | .. |
|---|
| 561 | 585 | goto out_revert_creds; |
|---|
| 562 | 586 | } |
|---|
| 563 | 587 | |
|---|
| 564 | | - err = -ENOMEM; |
|---|
| 565 | | - override_cred = prepare_creds(); |
|---|
| 566 | | - if (override_cred) { |
|---|
| 588 | + if (!attr->hardlink) { |
|---|
| 589 | + err = -ENOMEM; |
|---|
| 590 | + override_cred = prepare_creds(); |
|---|
| 591 | + if (!override_cred) |
|---|
| 592 | + goto out_revert_creds; |
|---|
| 593 | + /* |
|---|
| 594 | + * In the creation cases(create, mkdir, mknod, symlink), |
|---|
| 595 | + * ovl should transfer current's fs{u,g}id to underlying |
|---|
| 596 | + * fs. Because underlying fs want to initialize its new |
|---|
| 597 | + * inode owner using current's fs{u,g}id. And in this |
|---|
| 598 | + * case, the @inode is a new inode that is initialized |
|---|
| 599 | + * in inode_init_owner() to current's fs{u,g}id. So use |
|---|
| 600 | + * the inode's i_{u,g}id to override the cred's fs{u,g}id. |
|---|
| 601 | + * |
|---|
| 602 | + * But in the other hardlink case, ovl_link() does not |
|---|
| 603 | + * create a new inode, so just use the ovl mounter's |
|---|
| 604 | + * fs{u,g}id. |
|---|
| 605 | + */ |
|---|
| 567 | 606 | override_cred->fsuid = inode->i_uid; |
|---|
| 568 | 607 | override_cred->fsgid = inode->i_gid; |
|---|
| 569 | | - if (!attr->hardlink) { |
|---|
| 570 | | - err = security_dentry_create_files_as(dentry, |
|---|
| 571 | | - attr->mode, &dentry->d_name, |
|---|
| 572 | | - old_cred ? old_cred : current_cred(), |
|---|
| 573 | | - override_cred); |
|---|
| 574 | | - if (err) { |
|---|
| 575 | | - put_cred(override_cred); |
|---|
| 576 | | - goto out_revert_creds; |
|---|
| 577 | | - } |
|---|
| 608 | + err = security_dentry_create_files_as(dentry, |
|---|
| 609 | + attr->mode, &dentry->d_name, |
|---|
| 610 | + old_cred ? old_cred : current_cred(), |
|---|
| 611 | + override_cred); |
|---|
| 612 | + if (err) { |
|---|
| 613 | + put_cred(override_cred); |
|---|
| 614 | + goto out_revert_creds; |
|---|
| 578 | 615 | } |
|---|
| 579 | 616 | hold_cred = override_creds(override_cred); |
|---|
| 580 | 617 | put_cred(override_cred); |
|---|
| 581 | | - |
|---|
| 582 | | - if (!ovl_dentry_is_whiteout(dentry)) |
|---|
| 583 | | - err = ovl_create_upper(dentry, inode, attr); |
|---|
| 584 | | - else |
|---|
| 585 | | - err = ovl_create_over_whiteout(dentry, inode, attr); |
|---|
| 586 | 618 | } |
|---|
| 619 | + |
|---|
| 620 | + if (!ovl_dentry_is_whiteout(dentry)) |
|---|
| 621 | + err = ovl_create_upper(dentry, inode, attr); |
|---|
| 622 | + else |
|---|
| 623 | + err = ovl_create_over_whiteout(dentry, inode, attr); |
|---|
| 624 | + |
|---|
| 587 | 625 | out_revert_creds: |
|---|
| 588 | | - ovl_revert_creds(old_cred ?: hold_cred); |
|---|
| 626 | + ovl_revert_creds(dentry->d_sb, old_cred ?: hold_cred); |
|---|
| 589 | 627 | if (old_cred && hold_cred) |
|---|
| 590 | 628 | put_cred(hold_cred); |
|---|
| 591 | 629 | return err; |
|---|
| .. | .. |
|---|
| 663 | 701 | |
|---|
| 664 | 702 | old_cred = ovl_override_creds(dentry->d_sb); |
|---|
| 665 | 703 | err = ovl_set_redirect(dentry, false); |
|---|
| 666 | | - ovl_revert_creds(old_cred); |
|---|
| 704 | + ovl_revert_creds(dentry->d_sb, old_cred); |
|---|
| 667 | 705 | |
|---|
| 668 | 706 | return err; |
|---|
| 669 | 707 | } |
|---|
| .. | .. |
|---|
| 672 | 710 | struct dentry *new) |
|---|
| 673 | 711 | { |
|---|
| 674 | 712 | int err; |
|---|
| 675 | | - bool locked = false; |
|---|
| 676 | 713 | struct inode *inode; |
|---|
| 677 | 714 | |
|---|
| 678 | 715 | err = ovl_want_write(old); |
|---|
| .. | .. |
|---|
| 693 | 730 | goto out_drop_write; |
|---|
| 694 | 731 | } |
|---|
| 695 | 732 | |
|---|
| 696 | | - err = ovl_nlink_start(old, &locked); |
|---|
| 733 | + err = ovl_nlink_start(old); |
|---|
| 697 | 734 | if (err) |
|---|
| 698 | 735 | goto out_drop_write; |
|---|
| 699 | 736 | |
|---|
| .. | .. |
|---|
| 706 | 743 | if (err) |
|---|
| 707 | 744 | iput(inode); |
|---|
| 708 | 745 | |
|---|
| 709 | | - ovl_nlink_end(old, locked); |
|---|
| 746 | + ovl_nlink_end(old); |
|---|
| 710 | 747 | out_drop_write: |
|---|
| 711 | 748 | ovl_drop_write(old); |
|---|
| 712 | 749 | out: |
|---|
| .. | .. |
|---|
| 721 | 758 | static int ovl_remove_and_whiteout(struct dentry *dentry, |
|---|
| 722 | 759 | struct list_head *list) |
|---|
| 723 | 760 | { |
|---|
| 761 | + struct ovl_fs *ofs = OVL_FS(dentry->d_sb); |
|---|
| 724 | 762 | struct dentry *workdir = ovl_workdir(dentry); |
|---|
| 725 | 763 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); |
|---|
| 726 | 764 | struct dentry *upper; |
|---|
| .. | .. |
|---|
| 754 | 792 | goto out_dput_upper; |
|---|
| 755 | 793 | } |
|---|
| 756 | 794 | |
|---|
| 757 | | - err = ovl_cleanup_and_whiteout(workdir, d_inode(upperdir), upper); |
|---|
| 795 | + err = ovl_cleanup_and_whiteout(ofs, d_inode(upperdir), upper); |
|---|
| 758 | 796 | if (err) |
|---|
| 759 | 797 | goto out_d_drop; |
|---|
| 760 | 798 | |
|---|
| .. | .. |
|---|
| 828 | 866 | !ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry)); |
|---|
| 829 | 867 | } |
|---|
| 830 | 868 | |
|---|
| 869 | +static void ovl_drop_nlink(struct dentry *dentry) |
|---|
| 870 | +{ |
|---|
| 871 | + struct inode *inode = d_inode(dentry); |
|---|
| 872 | + struct dentry *alias; |
|---|
| 873 | + |
|---|
| 874 | + /* Try to find another, hashed alias */ |
|---|
| 875 | + spin_lock(&inode->i_lock); |
|---|
| 876 | + hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { |
|---|
| 877 | + if (alias != dentry && !d_unhashed(alias)) |
|---|
| 878 | + break; |
|---|
| 879 | + } |
|---|
| 880 | + spin_unlock(&inode->i_lock); |
|---|
| 881 | + |
|---|
| 882 | + /* |
|---|
| 883 | + * Changes to underlying layers may cause i_nlink to lose sync with |
|---|
| 884 | + * reality. In this case prevent the link count from going to zero |
|---|
| 885 | + * prematurely. |
|---|
| 886 | + */ |
|---|
| 887 | + if (inode->i_nlink > !!alias) |
|---|
| 888 | + drop_nlink(inode); |
|---|
| 889 | +} |
|---|
| 890 | + |
|---|
| 831 | 891 | static int ovl_do_remove(struct dentry *dentry, bool is_dir) |
|---|
| 832 | 892 | { |
|---|
| 833 | 893 | int err; |
|---|
| 834 | | - bool locked = false; |
|---|
| 835 | 894 | const struct cred *old_cred; |
|---|
| 836 | 895 | struct dentry *upperdentry; |
|---|
| 837 | 896 | bool lower_positive = ovl_lower_positive(dentry); |
|---|
| .. | .. |
|---|
| 852 | 911 | if (err) |
|---|
| 853 | 912 | goto out_drop_write; |
|---|
| 854 | 913 | |
|---|
| 855 | | - err = ovl_nlink_start(dentry, &locked); |
|---|
| 914 | + err = ovl_nlink_start(dentry); |
|---|
| 856 | 915 | if (err) |
|---|
| 857 | 916 | goto out_drop_write; |
|---|
| 858 | 917 | |
|---|
| .. | .. |
|---|
| 861 | 920 | err = ovl_remove_upper(dentry, is_dir, &list); |
|---|
| 862 | 921 | else |
|---|
| 863 | 922 | err = ovl_remove_and_whiteout(dentry, &list); |
|---|
| 864 | | - ovl_revert_creds(old_cred); |
|---|
| 923 | + ovl_revert_creds(dentry->d_sb, old_cred); |
|---|
| 865 | 924 | if (!err) { |
|---|
| 866 | 925 | if (is_dir) |
|---|
| 867 | 926 | clear_nlink(dentry->d_inode); |
|---|
| 868 | 927 | else |
|---|
| 869 | | - drop_nlink(dentry->d_inode); |
|---|
| 928 | + ovl_drop_nlink(dentry); |
|---|
| 870 | 929 | } |
|---|
| 871 | | - ovl_nlink_end(dentry, locked); |
|---|
| 930 | + ovl_nlink_end(dentry); |
|---|
| 872 | 931 | |
|---|
| 873 | 932 | /* |
|---|
| 874 | 933 | * Copy ctime |
|---|
| .. | .. |
|---|
| 1019 | 1078 | spin_unlock(&dentry->d_lock); |
|---|
| 1020 | 1079 | } else { |
|---|
| 1021 | 1080 | kfree(redirect); |
|---|
| 1022 | | - pr_warn_ratelimited("overlayfs: failed to set redirect (%i)\n", |
|---|
| 1081 | + pr_warn_ratelimited("failed to set redirect (%i)\n", |
|---|
| 1023 | 1082 | err); |
|---|
| 1024 | 1083 | /* Fall back to userspace copy-up */ |
|---|
| 1025 | 1084 | err = -EXDEV; |
|---|
| .. | .. |
|---|
| 1032 | 1091 | unsigned int flags) |
|---|
| 1033 | 1092 | { |
|---|
| 1034 | 1093 | int err; |
|---|
| 1035 | | - bool locked = false; |
|---|
| 1036 | 1094 | struct dentry *old_upperdir; |
|---|
| 1037 | 1095 | struct dentry *new_upperdir; |
|---|
| 1038 | 1096 | struct dentry *olddentry; |
|---|
| .. | .. |
|---|
| 1041 | 1099 | bool old_opaque; |
|---|
| 1042 | 1100 | bool new_opaque; |
|---|
| 1043 | 1101 | bool cleanup_whiteout = false; |
|---|
| 1102 | + bool update_nlink = false; |
|---|
| 1044 | 1103 | bool overwrite = !(flags & RENAME_EXCHANGE); |
|---|
| 1045 | 1104 | bool is_dir = d_is_dir(old); |
|---|
| 1046 | 1105 | bool new_is_dir = d_is_dir(new); |
|---|
| .. | .. |
|---|
| 1098 | 1157 | err = ovl_copy_up(new); |
|---|
| 1099 | 1158 | if (err) |
|---|
| 1100 | 1159 | goto out_drop_write; |
|---|
| 1101 | | - } else { |
|---|
| 1102 | | - err = ovl_nlink_start(new, &locked); |
|---|
| 1160 | + } else if (d_inode(new)) { |
|---|
| 1161 | + err = ovl_nlink_start(new); |
|---|
| 1103 | 1162 | if (err) |
|---|
| 1104 | 1163 | goto out_drop_write; |
|---|
| 1164 | + |
|---|
| 1165 | + update_nlink = true; |
|---|
| 1105 | 1166 | } |
|---|
| 1106 | 1167 | |
|---|
| 1107 | 1168 | old_cred = ovl_override_creds(old->d_sb); |
|---|
| .. | .. |
|---|
| 1213 | 1274 | if (new_is_dir) |
|---|
| 1214 | 1275 | clear_nlink(d_inode(new)); |
|---|
| 1215 | 1276 | else |
|---|
| 1216 | | - drop_nlink(d_inode(new)); |
|---|
| 1277 | + ovl_drop_nlink(new); |
|---|
| 1217 | 1278 | } |
|---|
| 1218 | 1279 | |
|---|
| 1219 | 1280 | ovl_dir_modified(old->d_parent, ovl_type_origin(old) || |
|---|
| .. | .. |
|---|
| 1233 | 1294 | out_unlock: |
|---|
| 1234 | 1295 | unlock_rename(new_upperdir, old_upperdir); |
|---|
| 1235 | 1296 | out_revert_creds: |
|---|
| 1236 | | - ovl_revert_creds(old_cred); |
|---|
| 1237 | | - ovl_nlink_end(new, locked); |
|---|
| 1297 | + ovl_revert_creds(old->d_sb, old_cred); |
|---|
| 1298 | + if (update_nlink) |
|---|
| 1299 | + ovl_nlink_end(new); |
|---|
| 1238 | 1300 | out_drop_write: |
|---|
| 1239 | 1301 | ovl_drop_write(old); |
|---|
| 1240 | 1302 | out: |
|---|