File Index
Files and directories
Sign in
FILE

dynroot.c

File preview with syntax highlighting.

linux-6.19.14/fs/afs/dynroot.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* AFS dynamic root handling
  3 *
  4 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
  5 * Written by David Howells (dhowells@redhat.com)
  6 */
  7
  8#include <linux/fs.h>
  9#include <linux/namei.h>
 10#include <linux/dns_resolver.h>
 11#include "internal.h"
 12
 13#define AFS_MIN_DYNROOT_CELL_INO 4 /* Allow for ., .., @cell, .@cell */
 14#define AFS_MAX_DYNROOT_CELL_INO ((unsigned int)INT_MAX)
 15
 16static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry, ino_t ino);
 17
 18/*
 19 * iget5() comparator for inode created by autocell operations
 20 */
 21static int afs_iget5_pseudo_test(struct inode *inode, void *opaque)
 22{
 23	struct afs_fid *fid = opaque;
 24
 25	return inode->i_ino == fid->vnode;
 26}
 27
 28/*
 29 * iget5() inode initialiser
 30 */
 31static int afs_iget5_pseudo_set(struct inode *inode, void *opaque)
 32{
 33	struct afs_super_info *as = AFS_FS_S(inode->i_sb);
 34	struct afs_vnode *vnode = AFS_FS_I(inode);
 35	struct afs_fid *fid = opaque;
 36
 37	vnode->volume		= as->volume;
 38	vnode->fid		= *fid;
 39	inode->i_ino		= fid->vnode;
 40	inode->i_generation	= fid->unique;
 41	return 0;
 42}
 43
 44/*
 45 * Create an inode for an autocell dynamic automount dir.
 46 */
 47static struct inode *afs_iget_pseudo_dir(struct super_block *sb, ino_t ino)
 48{
 49	struct afs_vnode *vnode;
 50	struct inode *inode;
 51	struct afs_fid fid = { .vnode = ino, .unique = 1, };
 52
 53	_enter("");
 54
 55	inode = iget5_locked(sb, fid.vnode,
 56			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
 57	if (!inode) {
 58		_leave(" = -ENOMEM");
 59		return ERR_PTR(-ENOMEM);
 60	}
 61
 62	_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
 63	       inode, inode->i_ino, fid.vid, fid.vnode, fid.unique);
 64
 65	vnode = AFS_FS_I(inode);
 66
 67	if (inode_state_read_once(inode) & I_NEW) {
 68		netfs_inode_init(&vnode->netfs, NULL, false);
 69		simple_inode_init_ts(inode);
 70		set_nlink(inode, 2);
 71		inode->i_size		= 0;
 72		inode->i_mode		= S_IFDIR | 0555;
 73		inode->i_op		= &afs_autocell_inode_operations;
 74		inode->i_uid		= GLOBAL_ROOT_UID;
 75		inode->i_gid		= GLOBAL_ROOT_GID;
 76		inode->i_blocks		= 0;
 77		inode->i_generation	= 0;
 78		inode->i_flags		|= S_AUTOMOUNT | S_NOATIME;
 79
 80		set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
 81		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
 82
 83		unlock_new_inode(inode);
 84	}
 85	_leave(" = %p", inode);
 86	return inode;
 87}
 88
 89/*
 90 * Try to automount the mountpoint with pseudo directory, if the autocell
 91 * option is set.
 92 */
 93static struct dentry *afs_dynroot_lookup_cell(struct inode *dir, struct dentry *dentry,
 94					      unsigned int flags)
 95{
 96	struct afs_cell *cell = NULL;
 97	struct afs_net *net = afs_d2net(dentry);
 98	struct inode *inode = NULL;
 99	const char *name = dentry->d_name.name;
100	size_t len = dentry->d_name.len;
101	bool dotted = false;
102	int ret = -ENOENT;
103
104	/* Names prefixed with a dot are R/W mounts. */
105	if (name[0] == '.') {
106		name++;
107		len--;
108		dotted = true;
109	}
110
111	cell = afs_lookup_cell(net, name, len, NULL,
112			       AFS_LOOKUP_CELL_DYNROOT,
113			       afs_cell_trace_use_lookup_dynroot);
114	if (IS_ERR(cell)) {
115		ret = PTR_ERR(cell);
116		goto out_no_cell;
117	}
118
119	inode = afs_iget_pseudo_dir(dir->i_sb, cell->dynroot_ino * 2 + dotted);
120	if (IS_ERR(inode)) {
121		ret = PTR_ERR(inode);
122		goto out;
123	}
124
125	dentry->d_fsdata = cell;
126	return d_splice_alias(inode, dentry);
127
128out:
129	afs_unuse_cell(cell, afs_cell_trace_unuse_lookup_dynroot);
130out_no_cell:
131	if (!inode)
132		return d_splice_alias(inode, dentry);
133	return ret == -ENOENT ? NULL : ERR_PTR(ret);
134}
135
136/*
137 * Look up an entry in a dynroot directory.
138 */
139static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
140					 unsigned int flags)
141{
142	_enter("%pd", dentry);
143
144	if (flags & LOOKUP_CREATE)
145		return ERR_PTR(-EOPNOTSUPP);
146
147	if (dentry->d_name.len >= AFSNAMEMAX) {
148		_leave(" = -ENAMETOOLONG");
149		return ERR_PTR(-ENAMETOOLONG);
150	}
151
152	if (dentry->d_name.len == 5 &&
153	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
154		return afs_lookup_atcell(dir, dentry, 2);
155
156	if (dentry->d_name.len == 6 &&
157	    memcmp(dentry->d_name.name, ".@cell", 6) == 0)
158		return afs_lookup_atcell(dir, dentry, 3);
159
160	return afs_dynroot_lookup_cell(dir, dentry, flags);
161}
162
163const struct inode_operations afs_dynroot_inode_operations = {
164	.lookup		= afs_dynroot_lookup,
165};
166
167static void afs_dynroot_d_release(struct dentry *dentry)
168{
169	struct afs_cell *cell = dentry->d_fsdata;
170
171	afs_unuse_cell(cell, afs_cell_trace_unuse_dynroot_mntpt);
172}
173
174/*
175 * Keep @cell symlink dentries around, but only keep cell autodirs when they're
176 * being used.
177 */
178static int afs_dynroot_delete_dentry(const struct dentry *dentry)
179{
180	const struct qstr *name = &dentry->d_name;
181
182	if (name->len == 5 && memcmp(name->name, "@cell", 5) == 0)
183		return 0;
184	if (name->len == 6 && memcmp(name->name, ".@cell", 6) == 0)
185		return 0;
186	return 1;
187}
188
189const struct dentry_operations afs_dynroot_dentry_operations = {
190	.d_delete	= afs_dynroot_delete_dentry,
191	.d_release	= afs_dynroot_d_release,
192	.d_automount	= afs_d_automount,
193};
194
195static void afs_atcell_delayed_put_cell(void *arg)
196{
197	struct afs_cell *cell = arg;
198
199	afs_put_cell(cell, afs_cell_trace_put_atcell);
200}
201
202/*
203 * Read @cell or .@cell symlinks.
204 */
205static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
206				       struct delayed_call *done)
207{
208	struct afs_vnode *vnode = AFS_FS_I(inode);
209	struct afs_cell *cell;
210	struct afs_net *net = afs_i2net(inode);
211	const char *name;
212	bool dotted = vnode->fid.vnode == 3;
213
214	if (!rcu_access_pointer(net->ws_cell))
215		return ERR_PTR(-ENOENT);
216
217	if (!dentry) {
218		/* We're in RCU-pathwalk. */
219		cell = rcu_dereference(net->ws_cell);
220		if (dotted)
221			name = cell->name - 1;
222		else
223			name = cell->name;
224		/* Shouldn't need to set a delayed call. */
225		return name;
226	}
227
228	down_read(&net->cells_lock);
229
230	cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
231	if (dotted)
232		name = cell->name - 1;
233	else
234		name = cell->name;
235	afs_get_cell(cell, afs_cell_trace_get_atcell);
236	set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
237
238	up_read(&net->cells_lock);
239	return name;
240}
241
242static const struct inode_operations afs_atcell_inode_operations = {
243	.get_link	= afs_atcell_get_link,
244};
245
246/*
247 * Create an inode for the @cell or .@cell symlinks.
248 */
249static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry, ino_t ino)
250{
251	struct afs_vnode *vnode;
252	struct inode *inode;
253	struct afs_fid fid = { .vnode = ino, .unique = 1, };
254
255	inode = iget5_locked(dir->i_sb, fid.vnode,
256			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
257	if (!inode)
258		return ERR_PTR(-ENOMEM);
259
260	vnode = AFS_FS_I(inode);
261
262	if (inode_state_read_once(inode) & I_NEW) {
263		netfs_inode_init(&vnode->netfs, NULL, false);
264		simple_inode_init_ts(inode);
265		set_nlink(inode, 1);
266		inode->i_size		= 0;
267		inode->i_mode		= S_IFLNK | 0555;
268		inode->i_op		= &afs_atcell_inode_operations;
269		inode->i_uid		= GLOBAL_ROOT_UID;
270		inode->i_gid		= GLOBAL_ROOT_GID;
271		inode->i_blocks		= 0;
272		inode->i_generation	= 0;
273		inode->i_flags		|= S_NOATIME;
274
275		unlock_new_inode(inode);
276	}
277	return d_splice_alias(inode, dentry);
278}
279
280/*
281 * Transcribe the cell database into readdir content under the RCU read lock.
282 * Each cell produces two entries, one prefixed with a dot and one not.
283 */
284static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ctx)
285{
286	const struct afs_cell *cell;
287	loff_t newpos;
288
289	_enter("%llu", ctx->pos);
290
291	for (;;) {
292		unsigned int ix = ctx->pos >> 1;
293
294		cell = idr_get_next(&net->cells_dyn_ino, &ix);
295		if (!cell)
296			return 0;
297		if (READ_ONCE(cell->state) == AFS_CELL_REMOVING ||
298		    READ_ONCE(cell->state) == AFS_CELL_DEAD) {
299			ctx->pos += 2;
300			ctx->pos &= ~1;
301			continue;
302		}
303
304		newpos = ix << 1;
305		if (newpos > ctx->pos)
306			ctx->pos = newpos;
307
308		_debug("pos %llu -> cell %u", ctx->pos, cell->dynroot_ino);
309
310		if ((ctx->pos & 1) == 0) {
311			if (!dir_emit(ctx, cell->name, cell->name_len,
312				      cell->dynroot_ino, DT_DIR))
313				return 0;
314			ctx->pos++;
315		}
316		if ((ctx->pos & 1) == 1) {
317			if (!dir_emit(ctx, cell->name - 1, cell->name_len + 1,
318				      cell->dynroot_ino + 1, DT_DIR))
319				return 0;
320			ctx->pos++;
321		}
322	}
323	return 0;
324}
325
326/*
327 * Read the AFS dynamic root directory.  This produces a list of cellnames,
328 * dotted and undotted, along with @cell and .@cell links if configured.
329 */
330static int afs_dynroot_readdir(struct file *file, struct dir_context *ctx)
331{
332	struct afs_net *net = afs_d2net(file->f_path.dentry);
333	int ret = 0;
334
335	if (!dir_emit_dots(file, ctx))
336		return 0;
337
338	if (ctx->pos == 2) {
339		if (rcu_access_pointer(net->ws_cell) &&
340		    !dir_emit(ctx, "@cell", 5, 2, DT_LNK))
341			return 0;
342		ctx->pos = 3;
343	}
344	if (ctx->pos == 3) {
345		if (rcu_access_pointer(net->ws_cell) &&
346		    !dir_emit(ctx, ".@cell", 6, 3, DT_LNK))
347			return 0;
348		ctx->pos = 4;
349	}
350
351	if ((unsigned long long)ctx->pos <= AFS_MAX_DYNROOT_CELL_INO) {
352		down_read(&net->cells_lock);
353		ret = afs_dynroot_readdir_cells(net, ctx);
354		up_read(&net->cells_lock);
355	}
356	return ret;
357}
358
359static const struct file_operations afs_dynroot_file_operations = {
360	.llseek		= generic_file_llseek,
361	.read		= generic_read_dir,
362	.iterate_shared	= afs_dynroot_readdir,
363	.fsync		= noop_fsync,
364};
365
366/*
367 * Create an inode for a dynamic root directory.
368 */
369struct inode *afs_dynroot_iget_root(struct super_block *sb)
370{
371	struct afs_super_info *as = AFS_FS_S(sb);
372	struct afs_vnode *vnode;
373	struct inode *inode;
374	struct afs_fid fid = { .vid = 0, .vnode = 1, .unique = 1,};
375
376	if (as->volume)
377		fid.vid = as->volume->vid;
378
379	inode = iget5_locked(sb, fid.vnode,
380			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
381	if (!inode)
382		return ERR_PTR(-ENOMEM);
383
384	vnode = AFS_FS_I(inode);
385
386	/* there shouldn't be an existing inode */
387	if (inode_state_read_once(inode) & I_NEW) {
388		netfs_inode_init(&vnode->netfs, NULL, false);
389		simple_inode_init_ts(inode);
390		set_nlink(inode, 2);
391		inode->i_size		= 0;
392		inode->i_mode		= S_IFDIR | 0555;
393		inode->i_op		= &afs_dynroot_inode_operations;
394		inode->i_fop		= &afs_dynroot_file_operations;
395		inode->i_uid		= GLOBAL_ROOT_UID;
396		inode->i_gid		= GLOBAL_ROOT_GID;
397		inode->i_blocks		= 0;
398		inode->i_generation	= 0;
399		inode->i_flags		|= S_NOATIME;
400
401		set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
402		unlock_new_inode(inode);
403	}
404	_leave(" = %p", inode);
405	return inode;
406}
~/ Read-only file preview.