Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/afs/dynroot.c
26278 views
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 ([email protected])
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
16
static 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
*/
21
static 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
*/
31
static 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
*/
47
static 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->i_state & 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
*/
93
static 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, false,
112
afs_cell_trace_use_lookup_dynroot);
113
if (IS_ERR(cell)) {
114
ret = PTR_ERR(cell);
115
goto out_no_cell;
116
}
117
118
inode = afs_iget_pseudo_dir(dir->i_sb, cell->dynroot_ino * 2 + dotted);
119
if (IS_ERR(inode)) {
120
ret = PTR_ERR(inode);
121
goto out;
122
}
123
124
dentry->d_fsdata = cell;
125
return d_splice_alias(inode, dentry);
126
127
out:
128
afs_unuse_cell(cell, afs_cell_trace_unuse_lookup_dynroot);
129
out_no_cell:
130
if (!inode)
131
return d_splice_alias(inode, dentry);
132
return ret == -ENOENT ? NULL : ERR_PTR(ret);
133
}
134
135
/*
136
* Look up an entry in a dynroot directory.
137
*/
138
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
139
unsigned int flags)
140
{
141
_enter("%pd", dentry);
142
143
if (flags & LOOKUP_CREATE)
144
return ERR_PTR(-EOPNOTSUPP);
145
146
if (dentry->d_name.len >= AFSNAMEMAX) {
147
_leave(" = -ENAMETOOLONG");
148
return ERR_PTR(-ENAMETOOLONG);
149
}
150
151
if (dentry->d_name.len == 5 &&
152
memcmp(dentry->d_name.name, "@cell", 5) == 0)
153
return afs_lookup_atcell(dir, dentry, 2);
154
155
if (dentry->d_name.len == 6 &&
156
memcmp(dentry->d_name.name, ".@cell", 6) == 0)
157
return afs_lookup_atcell(dir, dentry, 3);
158
159
return afs_dynroot_lookup_cell(dir, dentry, flags);
160
}
161
162
const struct inode_operations afs_dynroot_inode_operations = {
163
.lookup = afs_dynroot_lookup,
164
};
165
166
static void afs_dynroot_d_release(struct dentry *dentry)
167
{
168
struct afs_cell *cell = dentry->d_fsdata;
169
170
afs_unuse_cell(cell, afs_cell_trace_unuse_dynroot_mntpt);
171
}
172
173
/*
174
* Keep @cell symlink dentries around, but only keep cell autodirs when they're
175
* being used.
176
*/
177
static int afs_dynroot_delete_dentry(const struct dentry *dentry)
178
{
179
const struct qstr *name = &dentry->d_name;
180
181
if (name->len == 5 && memcmp(name->name, "@cell", 5) == 0)
182
return 0;
183
if (name->len == 6 && memcmp(name->name, ".@cell", 6) == 0)
184
return 0;
185
return 1;
186
}
187
188
const struct dentry_operations afs_dynroot_dentry_operations = {
189
.d_delete = afs_dynroot_delete_dentry,
190
.d_release = afs_dynroot_d_release,
191
.d_automount = afs_d_automount,
192
};
193
194
static void afs_atcell_delayed_put_cell(void *arg)
195
{
196
struct afs_cell *cell = arg;
197
198
afs_put_cell(cell, afs_cell_trace_put_atcell);
199
}
200
201
/*
202
* Read @cell or .@cell symlinks.
203
*/
204
static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
205
struct delayed_call *done)
206
{
207
struct afs_vnode *vnode = AFS_FS_I(inode);
208
struct afs_cell *cell;
209
struct afs_net *net = afs_i2net(inode);
210
const char *name;
211
bool dotted = vnode->fid.vnode == 3;
212
213
if (!rcu_access_pointer(net->ws_cell))
214
return ERR_PTR(-ENOENT);
215
216
if (!dentry) {
217
/* We're in RCU-pathwalk. */
218
cell = rcu_dereference(net->ws_cell);
219
if (dotted)
220
name = cell->name - 1;
221
else
222
name = cell->name;
223
/* Shouldn't need to set a delayed call. */
224
return name;
225
}
226
227
down_read(&net->cells_lock);
228
229
cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
230
if (dotted)
231
name = cell->name - 1;
232
else
233
name = cell->name;
234
afs_get_cell(cell, afs_cell_trace_get_atcell);
235
set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
236
237
up_read(&net->cells_lock);
238
return name;
239
}
240
241
static const struct inode_operations afs_atcell_inode_operations = {
242
.get_link = afs_atcell_get_link,
243
};
244
245
/*
246
* Create an inode for the @cell or .@cell symlinks.
247
*/
248
static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry, ino_t ino)
249
{
250
struct afs_vnode *vnode;
251
struct inode *inode;
252
struct afs_fid fid = { .vnode = ino, .unique = 1, };
253
254
inode = iget5_locked(dir->i_sb, fid.vnode,
255
afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
256
if (!inode)
257
return ERR_PTR(-ENOMEM);
258
259
vnode = AFS_FS_I(inode);
260
261
if (inode->i_state & I_NEW) {
262
netfs_inode_init(&vnode->netfs, NULL, false);
263
simple_inode_init_ts(inode);
264
set_nlink(inode, 1);
265
inode->i_size = 0;
266
inode->i_mode = S_IFLNK | 0555;
267
inode->i_op = &afs_atcell_inode_operations;
268
inode->i_uid = GLOBAL_ROOT_UID;
269
inode->i_gid = GLOBAL_ROOT_GID;
270
inode->i_blocks = 0;
271
inode->i_generation = 0;
272
inode->i_flags |= S_NOATIME;
273
274
unlock_new_inode(inode);
275
}
276
return d_splice_alias(inode, dentry);
277
}
278
279
/*
280
* Transcribe the cell database into readdir content under the RCU read lock.
281
* Each cell produces two entries, one prefixed with a dot and one not.
282
*/
283
static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ctx)
284
{
285
const struct afs_cell *cell;
286
loff_t newpos;
287
288
_enter("%llu", ctx->pos);
289
290
for (;;) {
291
unsigned int ix = ctx->pos >> 1;
292
293
cell = idr_get_next(&net->cells_dyn_ino, &ix);
294
if (!cell)
295
return 0;
296
if (READ_ONCE(cell->state) == AFS_CELL_REMOVING ||
297
READ_ONCE(cell->state) == AFS_CELL_DEAD) {
298
ctx->pos += 2;
299
ctx->pos &= ~1;
300
continue;
301
}
302
303
newpos = ix << 1;
304
if (newpos > ctx->pos)
305
ctx->pos = newpos;
306
307
_debug("pos %llu -> cell %u", ctx->pos, cell->dynroot_ino);
308
309
if ((ctx->pos & 1) == 0) {
310
if (!dir_emit(ctx, cell->name, cell->name_len,
311
cell->dynroot_ino, DT_DIR))
312
return 0;
313
ctx->pos++;
314
}
315
if ((ctx->pos & 1) == 1) {
316
if (!dir_emit(ctx, cell->name - 1, cell->name_len + 1,
317
cell->dynroot_ino + 1, DT_DIR))
318
return 0;
319
ctx->pos++;
320
}
321
}
322
return 0;
323
}
324
325
/*
326
* Read the AFS dynamic root directory. This produces a list of cellnames,
327
* dotted and undotted, along with @cell and .@cell links if configured.
328
*/
329
static int afs_dynroot_readdir(struct file *file, struct dir_context *ctx)
330
{
331
struct afs_net *net = afs_d2net(file->f_path.dentry);
332
int ret = 0;
333
334
if (!dir_emit_dots(file, ctx))
335
return 0;
336
337
if (ctx->pos == 2) {
338
if (rcu_access_pointer(net->ws_cell) &&
339
!dir_emit(ctx, "@cell", 5, 2, DT_LNK))
340
return 0;
341
ctx->pos = 3;
342
}
343
if (ctx->pos == 3) {
344
if (rcu_access_pointer(net->ws_cell) &&
345
!dir_emit(ctx, ".@cell", 6, 3, DT_LNK))
346
return 0;
347
ctx->pos = 4;
348
}
349
350
if ((unsigned long long)ctx->pos <= AFS_MAX_DYNROOT_CELL_INO) {
351
down_read(&net->cells_lock);
352
ret = afs_dynroot_readdir_cells(net, ctx);
353
up_read(&net->cells_lock);
354
}
355
return ret;
356
}
357
358
static const struct file_operations afs_dynroot_file_operations = {
359
.llseek = generic_file_llseek,
360
.read = generic_read_dir,
361
.iterate_shared = afs_dynroot_readdir,
362
.fsync = noop_fsync,
363
};
364
365
/*
366
* Create an inode for a dynamic root directory.
367
*/
368
struct inode *afs_dynroot_iget_root(struct super_block *sb)
369
{
370
struct afs_super_info *as = AFS_FS_S(sb);
371
struct afs_vnode *vnode;
372
struct inode *inode;
373
struct afs_fid fid = { .vid = 0, .vnode = 1, .unique = 1,};
374
375
if (as->volume)
376
fid.vid = as->volume->vid;
377
378
inode = iget5_locked(sb, fid.vnode,
379
afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
380
if (!inode)
381
return ERR_PTR(-ENOMEM);
382
383
vnode = AFS_FS_I(inode);
384
385
/* there shouldn't be an existing inode */
386
if (inode->i_state & I_NEW) {
387
netfs_inode_init(&vnode->netfs, NULL, false);
388
simple_inode_init_ts(inode);
389
set_nlink(inode, 2);
390
inode->i_size = 0;
391
inode->i_mode = S_IFDIR | 0555;
392
inode->i_op = &afs_dynroot_inode_operations;
393
inode->i_fop = &afs_dynroot_file_operations;
394
inode->i_uid = GLOBAL_ROOT_UID;
395
inode->i_gid = GLOBAL_ROOT_GID;
396
inode->i_blocks = 0;
397
inode->i_generation = 0;
398
inode->i_flags |= S_NOATIME;
399
400
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
401
unlock_new_inode(inode);
402
}
403
_leave(" = %p", inode);
404
return inode;
405
}
406
407