Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/afs/dynroot.c
49217 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_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
*/
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,
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
128
out:
129
afs_unuse_cell(cell, afs_cell_trace_unuse_lookup_dynroot);
130
out_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
*/
139
static 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
163
const struct inode_operations afs_dynroot_inode_operations = {
164
.lookup = afs_dynroot_lookup,
165
};
166
167
static 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
*/
178
static 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
189
const 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
195
static 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
*/
205
static 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
242
static 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
*/
249
static 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
*/
284
static 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
*/
330
static 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
359
static 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
*/
369
struct 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
}
407
408