1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS filesystem symbolic link handling
3 *
4 * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
6 */
7
8 #include <linux/kernel.h>
9 #include <linux/fs.h>
10 #include <linux/namei.h>
11 #include <linux/pagemap.h>
12 #include <linux/iov_iter.h>
13 #include "internal.h"
14
afs_put_symlink(struct afs_symlink * symlink)15 static void afs_put_symlink(struct afs_symlink *symlink)
16 {
17 if (refcount_dec_and_test(&symlink->ref))
18 kfree_rcu(symlink, rcu);
19 }
20
afs_replace_symlink(struct afs_vnode * vnode,struct afs_symlink * symlink)21 static void afs_replace_symlink(struct afs_vnode *vnode, struct afs_symlink *symlink)
22 {
23 struct afs_symlink *old;
24
25 old = rcu_replace_pointer(vnode->symlink, symlink,
26 lockdep_is_held(&vnode->validate_lock));
27 if (old)
28 afs_put_symlink(old);
29 }
30
31 /*
32 * In the event that a third-party update of a symlink occurs, dispose of the
33 * copy of the old contents. Called under ->validate_lock.
34 */
afs_invalidate_symlink(struct afs_vnode * vnode)35 void afs_invalidate_symlink(struct afs_vnode *vnode)
36 {
37 afs_replace_symlink(vnode, NULL);
38 }
39
40 /*
41 * Dispose of a symlink copy during inode deletion.
42 */
afs_evict_symlink(struct afs_vnode * vnode)43 void afs_evict_symlink(struct afs_vnode *vnode)
44 {
45 struct afs_symlink *old;
46
47 old = rcu_replace_pointer(vnode->symlink, NULL, true);
48 if (old)
49 afs_put_symlink(old);
50
51 }
52
53 /*
54 * Set up a locally created symlink inode for immediate write to the cache.
55 */
afs_init_new_symlink(struct afs_vnode * vnode,struct afs_operation * op)56 void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op)
57 {
58 struct afs_symlink *symlink = op->create.symlink;
59 size_t dsize = 0;
60 size_t size = strlen(symlink->content) + 1;
61 char *p;
62
63 rcu_assign_pointer(vnode->symlink, symlink);
64 op->create.symlink = NULL;
65
66 if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs)))
67 return;
68
69 if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size,
70 mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0)
71 return;
72
73 vnode->directory_size = dsize;
74 p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
75 memcpy(p, symlink->content, size);
76 kunmap_local(p);
77 netfs_single_mark_inode_dirty(&vnode->netfs.inode);
78 }
79
80 /*
81 * Read a symlink in a single download.
82 */
afs_do_read_symlink(struct afs_vnode * vnode)83 static ssize_t afs_do_read_symlink(struct afs_vnode *vnode)
84 {
85 struct afs_symlink *symlink;
86 struct iov_iter iter;
87 ssize_t ret;
88 loff_t i_size;
89
90 i_size = i_size_read(&vnode->netfs.inode);
91 if (i_size > PAGE_SIZE - 1) {
92 trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big);
93 return -EFBIG;
94 }
95
96 if (!vnode->directory) {
97 size_t cur_size = 0;
98
99 ret = netfs_alloc_folioq_buffer(NULL,
100 &vnode->directory, &cur_size, PAGE_SIZE,
101 mapping_gfp_mask(vnode->netfs.inode.i_mapping));
102 vnode->directory_size = PAGE_SIZE - 1;
103 if (ret < 0)
104 return ret;
105 }
106
107 iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0, PAGE_SIZE);
108
109 /* AFS requires us to perform the read of a symlink as a single unit to
110 * avoid issues with the content being changed between reads.
111 */
112 ret = netfs_read_single(&vnode->netfs.inode, NULL, &iter);
113 if (ret >= 0) {
114 i_size = ret;
115 if (i_size > PAGE_SIZE - 1) {
116 trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big);
117 return -EFBIG;
118 }
119 vnode->directory_size = i_size;
120
121 /* Copy the symlink. */
122 symlink = kmalloc_flex(struct afs_symlink, content, i_size + 1);
123 if (!symlink)
124 return -ENOMEM;
125
126 refcount_set(&symlink->ref, 1);
127 symlink->content[i_size] = 0;
128
129 const char *s = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
130
131 memcpy(symlink->content, s, i_size);
132 kunmap_local(s);
133
134 afs_replace_symlink(vnode, symlink);
135 }
136
137 if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) {
138 netfs_free_folioq_buffer(vnode->directory);
139 vnode->directory = NULL;
140 vnode->directory_size = 0;
141 }
142
143 return ret;
144 }
145
afs_read_symlink(struct afs_vnode * vnode)146 static ssize_t afs_read_symlink(struct afs_vnode *vnode)
147 {
148 ssize_t ret;
149
150 fscache_use_cookie(afs_vnode_cache(vnode), false);
151 ret = afs_do_read_symlink(vnode);
152 fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL);
153 return ret;
154 }
155
afs_put_link(void * arg)156 static void afs_put_link(void *arg)
157 {
158 afs_put_symlink(arg);
159 }
160
afs_get_link(struct dentry * dentry,struct inode * inode,struct delayed_call * callback)161 const char *afs_get_link(struct dentry *dentry, struct inode *inode,
162 struct delayed_call *callback)
163 {
164 struct afs_symlink *symlink;
165 struct afs_vnode *vnode = AFS_FS_I(inode);
166 ssize_t ret;
167
168 if (!dentry) {
169 /* RCU pathwalk. */
170 symlink = rcu_dereference(vnode->symlink);
171 if (!symlink || !afs_check_validity(vnode))
172 return ERR_PTR(-ECHILD);
173 set_delayed_call(callback, NULL, NULL);
174 return symlink->content;
175 }
176
177 if (vnode->symlink) {
178 ret = afs_validate(vnode, NULL);
179 if (ret < 0)
180 return ERR_PTR(ret);
181
182 down_read(&vnode->validate_lock);
183 if (vnode->symlink)
184 goto good;
185 up_read(&vnode->validate_lock);
186 }
187
188 if (down_write_killable(&vnode->validate_lock) < 0)
189 return ERR_PTR(-ERESTARTSYS);
190 if (!vnode->symlink) {
191 ret = afs_read_symlink(vnode);
192 if (ret < 0) {
193 up_write(&vnode->validate_lock);
194 return ERR_PTR(ret);
195 }
196 }
197
198 downgrade_write(&vnode->validate_lock);
199
200 good:
201 symlink = rcu_dereference_protected(vnode->symlink,
202 lockdep_is_held(&vnode->validate_lock));
203 refcount_inc(&symlink->ref);
204 up_read(&vnode->validate_lock);
205
206 set_delayed_call(callback, afs_put_link, symlink);
207 return symlink->content;
208 }
209
afs_readlink(struct dentry * dentry,char __user * buffer,int buflen)210 int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
211 {
212 DEFINE_DELAYED_CALL(done);
213 const char *content;
214 int len;
215
216 content = afs_get_link(dentry, d_inode(dentry), &done);
217 if (IS_ERR(content)) {
218 do_delayed_call(&done);
219 return PTR_ERR(content);
220 }
221
222 len = umin(strlen(content), buflen);
223 if (copy_to_user(buffer, content, len))
224 len = -EFAULT;
225 do_delayed_call(&done);
226 return len;
227 }
228
229 /*
230 * Write the symlink contents to the cache as a single blob. We then throw
231 * away the page we used to receive it.
232 */
afs_symlink_writepages(struct address_space * mapping,struct writeback_control * wbc)233 int afs_symlink_writepages(struct address_space *mapping,
234 struct writeback_control *wbc)
235 {
236 struct afs_vnode *vnode = AFS_FS_I(mapping->host);
237 struct iov_iter iter;
238 int ret = 0;
239
240 if (!down_read_trylock(&vnode->validate_lock)) {
241 if (wbc->sync_mode == WB_SYNC_NONE) {
242 /* The VFS will have undirtied the inode. */
243 netfs_single_mark_inode_dirty(&vnode->netfs.inode);
244 return 0;
245 }
246 down_read(&vnode->validate_lock);
247 }
248
249 if (vnode->directory &&
250 atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
251 iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0,
252 i_size_read(&vnode->netfs.inode));
253 ret = netfs_writeback_single(mapping, wbc, &iter);
254 }
255
256 if (ret == 0) {
257 netfs_wb_begin(&vnode->netfs, false);
258 netfs_free_folioq_buffer(vnode->directory);
259 vnode->directory = NULL;
260 vnode->directory_size = 0;
261 netfs_wb_end(&vnode->netfs);
262 } else if (ret == 1) {
263 ret = 0; /* Skipped write due to lock conflict. */
264 }
265
266 up_read(&vnode->validate_lock);
267 return ret;
268 }
269
270 const struct inode_operations afs_symlink_inode_operations = {
271 .get_link = afs_get_link,
272 .readlink = afs_readlink,
273 };
274
275 const struct address_space_operations afs_symlink_aops = {
276 .writepages = afs_symlink_writepages,
277 };
278