Skip to content

Commit 07a90e9

Browse files
committed
cachefiles: Implement culling daemon commands
Implement the ability for the userspace daemon to try and cull a file or directory in the cache. Two daemon commands are implemented: (1) The "inuse" command. This queries if a file is in use or whether it can be deleted. It checks the S_KERNEL_FILE flag on the inode referred to by the specified filename. (2) The "cull" command. This asks for a file or directory to be removed, where removal means either unlinking it or moving it to the graveyard directory for userspace to dismantle. Changes ======= ver #2: - Fix logging of wrong error[1]. - Need to unmark an inode we've moved to the graveyard before unlocking. Signed-off-by: David Howells <[email protected]> Reviewed-by: Jeff Layton <[email protected]> cc: [email protected] Link: https://lore.kernel.org/r/20211203094950.GA2480@kili/ [1] Link: https://lore.kernel.org/r/163819643179.215744.13641580295708315695.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906945705.143852.8177595531814485350.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967155792.1823006.1088936326902550910.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021555037.640689.9472627499842585255.stgit@warthog.procyon.org.uk/ # v4
1 parent 169379e commit 07a90e9

File tree

3 files changed

+320
-2
lines changed

3 files changed

+320
-2
lines changed

fs/cachefiles/daemon.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args)
574574
goto notdir;
575575

576576
cachefiles_begin_secure(cache, &saved_cred);
577-
ret = -ENOANO; // PLACEHOLDER: Do culling
577+
ret = cachefiles_cull(cache, path.dentry, args);
578578
cachefiles_end_secure(cache, saved_cred);
579579

580580
path_put(&path);
@@ -645,7 +645,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args)
645645
goto notdir;
646646

647647
cachefiles_begin_secure(cache, &saved_cred);
648-
ret = -ENOANO; // PLACEHOLDER: Check if in use
648+
ret = cachefiles_check_in_use(cache, path.dentry, args);
649649
cachefiles_end_secure(cache, saved_cred);
650650

651651
path_put(&path);

fs/cachefiles/internal.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,23 @@ extern struct kmem_cache *cachefiles_object_jar;
189189
*/
190190
extern void cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
191191
struct file *file);
192+
extern int cachefiles_bury_object(struct cachefiles_cache *cache,
193+
struct cachefiles_object *object,
194+
struct dentry *dir,
195+
struct dentry *rep,
196+
enum fscache_why_object_killed why);
192197
extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
193198
struct dentry *dir,
194199
const char *name,
195200
bool *_is_new);
196201
extern void cachefiles_put_directory(struct dentry *dir);
197202

203+
extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
204+
char *filename);
205+
206+
extern int cachefiles_check_in_use(struct cachefiles_cache *cache,
207+
struct dentry *dir, char *filename);
208+
198209
/*
199210
* security.c
200211
*/

fs/cachefiles/namei.c

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,310 @@ void cachefiles_put_directory(struct dentry *dir)
217217
dput(dir);
218218
}
219219
}
220+
221+
/*
222+
* Remove a regular file from the cache.
223+
*/
224+
static int cachefiles_unlink(struct cachefiles_cache *cache,
225+
struct cachefiles_object *object,
226+
struct dentry *dir, struct dentry *dentry,
227+
enum fscache_why_object_killed why)
228+
{
229+
struct path path = {
230+
.mnt = cache->mnt,
231+
.dentry = dir,
232+
};
233+
int ret;
234+
235+
trace_cachefiles_unlink(object, dentry, why);
236+
ret = security_path_unlink(&path, dentry);
237+
if (ret < 0) {
238+
cachefiles_io_error(cache, "Unlink security error");
239+
return ret;
240+
}
241+
242+
ret = cachefiles_inject_remove_error();
243+
if (ret == 0) {
244+
ret = vfs_unlink(&init_user_ns, d_backing_inode(dir), dentry, NULL);
245+
if (ret == -EIO)
246+
cachefiles_io_error(cache, "Unlink failed");
247+
}
248+
if (ret != 0)
249+
trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret,
250+
cachefiles_trace_unlink_error);
251+
return ret;
252+
}
253+
254+
/*
255+
* Delete an object representation from the cache
256+
* - File backed objects are unlinked
257+
* - Directory backed objects are stuffed into the graveyard for userspace to
258+
* delete
259+
*/
260+
int cachefiles_bury_object(struct cachefiles_cache *cache,
261+
struct cachefiles_object *object,
262+
struct dentry *dir,
263+
struct dentry *rep,
264+
enum fscache_why_object_killed why)
265+
{
266+
struct dentry *grave, *trap;
267+
struct path path, path_to_graveyard;
268+
char nbuffer[8 + 8 + 1];
269+
int ret;
270+
271+
_enter(",'%pd','%pd'", dir, rep);
272+
273+
if (rep->d_parent != dir) {
274+
inode_unlock(d_inode(dir));
275+
_leave(" = -ESTALE");
276+
return -ESTALE;
277+
}
278+
279+
/* non-directories can just be unlinked */
280+
if (!d_is_dir(rep)) {
281+
dget(rep); /* Stop the dentry being negated if it's only pinned
282+
* by a file struct.
283+
*/
284+
ret = cachefiles_unlink(cache, object, dir, rep, why);
285+
dput(rep);
286+
287+
inode_unlock(d_inode(dir));
288+
_leave(" = %d", ret);
289+
return ret;
290+
}
291+
292+
/* directories have to be moved to the graveyard */
293+
_debug("move stale object to graveyard");
294+
inode_unlock(d_inode(dir));
295+
296+
try_again:
297+
/* first step is to make up a grave dentry in the graveyard */
298+
sprintf(nbuffer, "%08x%08x",
299+
(uint32_t) ktime_get_real_seconds(),
300+
(uint32_t) atomic_inc_return(&cache->gravecounter));
301+
302+
/* do the multiway lock magic */
303+
trap = lock_rename(cache->graveyard, dir);
304+
305+
/* do some checks before getting the grave dentry */
306+
if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) {
307+
/* the entry was probably culled when we dropped the parent dir
308+
* lock */
309+
unlock_rename(cache->graveyard, dir);
310+
_leave(" = 0 [culled?]");
311+
return 0;
312+
}
313+
314+
if (!d_can_lookup(cache->graveyard)) {
315+
unlock_rename(cache->graveyard, dir);
316+
cachefiles_io_error(cache, "Graveyard no longer a directory");
317+
return -EIO;
318+
}
319+
320+
if (trap == rep) {
321+
unlock_rename(cache->graveyard, dir);
322+
cachefiles_io_error(cache, "May not make directory loop");
323+
return -EIO;
324+
}
325+
326+
if (d_mountpoint(rep)) {
327+
unlock_rename(cache->graveyard, dir);
328+
cachefiles_io_error(cache, "Mountpoint in cache");
329+
return -EIO;
330+
}
331+
332+
grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer));
333+
if (IS_ERR(grave)) {
334+
unlock_rename(cache->graveyard, dir);
335+
trace_cachefiles_vfs_error(object, d_inode(cache->graveyard),
336+
PTR_ERR(grave),
337+
cachefiles_trace_lookup_error);
338+
339+
if (PTR_ERR(grave) == -ENOMEM) {
340+
_leave(" = -ENOMEM");
341+
return -ENOMEM;
342+
}
343+
344+
cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave));
345+
return -EIO;
346+
}
347+
348+
if (d_is_positive(grave)) {
349+
unlock_rename(cache->graveyard, dir);
350+
dput(grave);
351+
grave = NULL;
352+
cond_resched();
353+
goto try_again;
354+
}
355+
356+
if (d_mountpoint(grave)) {
357+
unlock_rename(cache->graveyard, dir);
358+
dput(grave);
359+
cachefiles_io_error(cache, "Mountpoint in graveyard");
360+
return -EIO;
361+
}
362+
363+
/* target should not be an ancestor of source */
364+
if (trap == grave) {
365+
unlock_rename(cache->graveyard, dir);
366+
dput(grave);
367+
cachefiles_io_error(cache, "May not make directory loop");
368+
return -EIO;
369+
}
370+
371+
/* attempt the rename */
372+
path.mnt = cache->mnt;
373+
path.dentry = dir;
374+
path_to_graveyard.mnt = cache->mnt;
375+
path_to_graveyard.dentry = cache->graveyard;
376+
ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0);
377+
if (ret < 0) {
378+
cachefiles_io_error(cache, "Rename security error %d", ret);
379+
} else {
380+
struct renamedata rd = {
381+
.old_mnt_userns = &init_user_ns,
382+
.old_dir = d_inode(dir),
383+
.old_dentry = rep,
384+
.new_mnt_userns = &init_user_ns,
385+
.new_dir = d_inode(cache->graveyard),
386+
.new_dentry = grave,
387+
};
388+
trace_cachefiles_rename(object, rep, grave, why);
389+
ret = cachefiles_inject_read_error();
390+
if (ret == 0)
391+
ret = vfs_rename(&rd);
392+
if (ret != 0)
393+
trace_cachefiles_vfs_error(object, d_inode(dir), ret,
394+
cachefiles_trace_rename_error);
395+
if (ret != 0 && ret != -ENOMEM)
396+
cachefiles_io_error(cache,
397+
"Rename failed with error %d", ret);
398+
}
399+
400+
__cachefiles_unmark_inode_in_use(object, rep);
401+
unlock_rename(cache->graveyard, dir);
402+
dput(grave);
403+
_leave(" = 0");
404+
return 0;
405+
}
406+
407+
/*
408+
* Look up an inode to be checked or culled. Return -EBUSY if the inode is
409+
* marked in use.
410+
*/
411+
static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache,
412+
struct dentry *dir,
413+
char *filename)
414+
{
415+
struct dentry *victim;
416+
int ret = -ENOENT;
417+
418+
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
419+
420+
victim = lookup_one_len(filename, dir, strlen(filename));
421+
if (IS_ERR(victim))
422+
goto lookup_error;
423+
if (d_is_negative(victim))
424+
goto lookup_put;
425+
if (d_inode(victim)->i_flags & S_KERNEL_FILE)
426+
goto lookup_busy;
427+
return victim;
428+
429+
lookup_busy:
430+
ret = -EBUSY;
431+
lookup_put:
432+
inode_unlock(d_inode(dir));
433+
dput(victim);
434+
return ERR_PTR(ret);
435+
436+
lookup_error:
437+
inode_unlock(d_inode(dir));
438+
ret = PTR_ERR(victim);
439+
if (ret == -ENOENT)
440+
return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */
441+
442+
if (ret == -EIO) {
443+
cachefiles_io_error(cache, "Lookup failed");
444+
} else if (ret != -ENOMEM) {
445+
pr_err("Internal error: %d\n", ret);
446+
ret = -EIO;
447+
}
448+
449+
return ERR_PTR(ret);
450+
}
451+
452+
/*
453+
* Cull an object if it's not in use
454+
* - called only by cache manager daemon
455+
*/
456+
int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
457+
char *filename)
458+
{
459+
struct dentry *victim;
460+
struct inode *inode;
461+
int ret;
462+
463+
_enter(",%pd/,%s", dir, filename);
464+
465+
victim = cachefiles_lookup_for_cull(cache, dir, filename);
466+
if (IS_ERR(victim))
467+
return PTR_ERR(victim);
468+
469+
/* check to see if someone is using this object */
470+
inode = d_inode(victim);
471+
inode_lock(inode);
472+
if (inode->i_flags & S_KERNEL_FILE) {
473+
ret = -EBUSY;
474+
} else {
475+
/* Stop the cache from picking it back up */
476+
inode->i_flags |= S_KERNEL_FILE;
477+
ret = 0;
478+
}
479+
inode_unlock(inode);
480+
if (ret < 0)
481+
goto error_unlock;
482+
483+
ret = cachefiles_bury_object(cache, NULL, dir, victim,
484+
FSCACHE_OBJECT_WAS_CULLED);
485+
if (ret < 0)
486+
goto error;
487+
488+
dput(victim);
489+
_leave(" = 0");
490+
return 0;
491+
492+
error_unlock:
493+
inode_unlock(d_inode(dir));
494+
error:
495+
dput(victim);
496+
if (ret == -ENOENT)
497+
return -ESTALE; /* Probably got retired by the netfs */
498+
499+
if (ret != -ENOMEM) {
500+
pr_err("Internal error: %d\n", ret);
501+
ret = -EIO;
502+
}
503+
504+
_leave(" = %d", ret);
505+
return ret;
506+
}
507+
508+
/*
509+
* Find out if an object is in use or not
510+
* - called only by cache manager daemon
511+
* - returns -EBUSY or 0 to indicate whether an object is in use or not
512+
*/
513+
int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir,
514+
char *filename)
515+
{
516+
struct dentry *victim;
517+
int ret = 0;
518+
519+
victim = cachefiles_lookup_for_cull(cache, dir, filename);
520+
if (IS_ERR(victim))
521+
return PTR_ERR(victim);
522+
523+
inode_unlock(d_inode(dir));
524+
dput(victim);
525+
return ret;
526+
}

0 commit comments

Comments
 (0)