1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2024, Alibaba Cloud
4 */
5 #include <linux/backing-file.h>
6 #include <linux/xxhash.h>
7 #include <linux/mount.h>
8 #include <linux/security.h>
9 #include "internal.h"
10 #include "xattr.h"
11
12 static struct vfsmount *erofs_ishare_mnt;
13
erofs_ishare_iget5_eq(struct inode * inode,void * data)14 static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
15 {
16 struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint;
17 struct erofs_inode_fingerprint *fp2 = data;
18
19 return fp1->size == fp2->size &&
20 !memcmp(fp1->opaque, fp2->opaque, fp2->size);
21 }
22
erofs_ishare_iget5_set(struct inode * inode,void * data)23 static int erofs_ishare_iget5_set(struct inode *inode, void *data)
24 {
25 struct erofs_inode *vi = EROFS_I(inode);
26
27 vi->fingerprint = *(struct erofs_inode_fingerprint *)data;
28 INIT_LIST_HEAD(&vi->ishare_list);
29 spin_lock_init(&vi->ishare_lock);
30 return 0;
31 }
32
erofs_ishare_fill_inode(struct inode * inode)33 bool erofs_ishare_fill_inode(struct inode *inode)
34 {
35 static const struct file_operations empty_fops = {};
36 struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
37 const struct address_space_operations *aops;
38 struct erofs_inode *vi = EROFS_I(inode);
39 struct erofs_inode_fingerprint fp;
40 struct dentry *sd;
41 struct inode *si;
42
43 aops = erofs_get_aops(inode);
44 if (IS_ERR(aops))
45 return false;
46 if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
47 return false;
48
49 si = iget5_locked(erofs_ishare_mnt->mnt_sb,
50 xxh32(fp.opaque, fp.size, 0),
51 erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
52 if (si && (inode_state_read_once(si) & I_NEW)) {
53 si->i_fop = &empty_fops;
54 si->i_mapping->a_ops = aops;
55 si->i_mode = 0444 | S_IFREG;
56 si->i_size = inode->i_size;
57 unlock_new_inode(si);
58 } else {
59 kfree(fp.opaque);
60 if (!si || aops != si->i_mapping->a_ops) {
61 iput(si);
62 return false;
63 }
64 if (si->i_size != inode->i_size) {
65 erofs_warn(inode->i_sb, "i_size mismatch (%lld != %lld) for the same fingerprint",
66 inode->i_size, si->i_size);
67 iput(si);
68 return false;
69 }
70 }
71 sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */
72 if (IS_ERR(sd))
73 return false;
74 vi->sharedentry = sd;
75 INIT_LIST_HEAD(&vi->ishare_list);
76 spin_lock(&EROFS_I(si)->ishare_lock);
77 list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
78 spin_unlock(&EROFS_I(si)->ishare_lock);
79 return true;
80 }
81
erofs_ishare_free_inode(struct inode * inode)82 void erofs_ishare_free_inode(struct inode *inode)
83 {
84 struct erofs_inode *vi = EROFS_I(inode), *svi;
85
86 if (!vi->sharedentry)
87 return;
88 svi = EROFS_I(d_inode(vi->sharedentry));
89 spin_lock(&svi->ishare_lock);
90 list_del(&vi->ishare_list);
91 spin_unlock(&svi->ishare_lock);
92 dput(vi->sharedentry);
93 vi->sharedentry = NULL;
94 }
95
erofs_ishare_file_open(struct inode * inode,struct file * file)96 static int erofs_ishare_file_open(struct inode *inode, struct file *file)
97 {
98 struct path sharedpath = {
99 .mnt = erofs_ishare_mnt,
100 .dentry = EROFS_I(inode)->sharedentry,
101 };
102 struct file *rf;
103
104 if (file->f_flags & O_DIRECT)
105 return -EINVAL;
106
107 rf = backing_file_open(file, file->f_flags | O_NOATIME,
108 &sharedpath, current_cred());
109 if (IS_ERR(rf))
110 return PTR_ERR(rf);
111 file->private_data = rf;
112 return 0;
113 }
114
erofs_ishare_file_release(struct inode * inode,struct file * file)115 static int erofs_ishare_file_release(struct inode *inode, struct file *file)
116 {
117 fput(file->private_data);
118 file->private_data = NULL;
119 return 0;
120 }
121
erofs_ishare_file_read_iter(struct kiocb * iocb,struct iov_iter * to)122 static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb,
123 struct iov_iter *to)
124 {
125 struct file *realfile = iocb->ki_filp->private_data;
126 struct kiocb dedup_iocb;
127 ssize_t nread;
128
129 if (!iov_iter_count(to))
130 return 0;
131 kiocb_clone(&dedup_iocb, iocb, realfile);
132 nread = filemap_read(&dedup_iocb, to, 0);
133 iocb->ki_pos = dedup_iocb.ki_pos;
134 return nread;
135 }
136
erofs_ishare_mmap(struct file * file,struct vm_area_struct * vma)137 static int erofs_ishare_mmap(struct file *file, struct vm_area_struct *vma)
138 {
139 struct file *realfile = file->private_data;
140 int err;
141
142 vma_set_file(vma, realfile);
143
144 err = security_mmap_backing_file(vma, realfile, file);
145 if (err)
146 return err;
147
148 return generic_file_readonly_mmap(file, vma);
149 }
150
erofs_ishare_fadvise(struct file * file,loff_t offset,loff_t len,int advice)151 static int erofs_ishare_fadvise(struct file *file, loff_t offset,
152 loff_t len, int advice)
153 {
154 return vfs_fadvise(file->private_data, offset, len, advice);
155 }
156
157 const struct file_operations erofs_ishare_fops = {
158 .open = erofs_ishare_file_open,
159 .llseek = generic_file_llseek,
160 .read_iter = erofs_ishare_file_read_iter,
161 .mmap = erofs_ishare_mmap,
162 .release = erofs_ishare_file_release,
163 .get_unmapped_area = thp_get_unmapped_area,
164 .splice_read = filemap_splice_read,
165 .fadvise = erofs_ishare_fadvise,
166 };
167
erofs_real_inode(struct inode * inode,bool * need_iput)168 struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
169 {
170 struct erofs_inode *vi, *vi_share;
171 struct inode *realinode;
172
173 *need_iput = false;
174 if (inode->i_sb != erofs_ishare_mnt->mnt_sb)
175 return inode;
176
177 vi_share = EROFS_I(inode);
178 spin_lock(&vi_share->ishare_lock);
179 /* fetch any one as real inode */
180 DBG_BUGON(list_empty(&vi_share->ishare_list));
181 list_for_each_entry(vi, &vi_share->ishare_list, ishare_list) {
182 realinode = igrab(&vi->vfs_inode);
183 if (realinode) {
184 *need_iput = true;
185 break;
186 }
187 }
188 spin_unlock(&vi_share->ishare_lock);
189
190 DBG_BUGON(!realinode);
191 return realinode;
192 }
193
erofs_init_ishare(void)194 int __init erofs_init_ishare(void)
195 {
196 struct vfsmount *mnt;
197 int ret;
198
199 mnt = kern_mount(&erofs_anon_fs_type);
200 if (IS_ERR(mnt))
201 return PTR_ERR(mnt);
202 /* generic_fadvise() doesn't work if s_bdi == &noop_backing_dev_info */
203 ret = super_setup_bdi(mnt->mnt_sb);
204 if (ret)
205 kern_unmount(mnt);
206 else
207 erofs_ishare_mnt = mnt;
208 return ret;
209 }
210
erofs_exit_ishare(void)211 void erofs_exit_ishare(void)
212 {
213 kern_unmount(erofs_ishare_mnt);
214 }
215