/*
* file.c - part of debugfs, a tiny little debug file system
*
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* debugfs is for people to use instead of /proc or /sys.
* See Documentation/DocBook/filesystems for more details.
*
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/pagemap.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/srcu.h>
#include <asm/poll.h>
#include "internal.h"
struct poll_table_struct;
static ssize_t default_read_file(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t default_write_file(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
const struct file_operations debugfs_noop_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = simple_open,
.llseek = noop_llseek,
};
/**
* debugfs_use_file_start - mark the beginning of file data access
* @dentry: the dentry object whose data is being accessed.
* @srcu_idx: a pointer to some memory to store a SRCU index in.
*
* Up to a matching call to debugfs_use_file_finish(), any
* successive call into the file removing functions debugfs_remove()
* and debugfs_remove_recursive() will block. Since associated private
* file data may only get freed after a successful return of any of
* the removal functions, you may safely access it after a successful
* call to debugfs_use_file_start() without worrying about
* lifetime issues.
*
* If -%EIO is returned, the file has already been removed and thus,
* it is not safe to access any of its data. If, on the other hand,
* it is allowed to access the file data, zero is returned.
*
* Regardless of the return code, any call to
* debugfs_use_file_start() must be followed by a matching call
* to debugfs_use_file_finish().
*/
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
__acquires(&debugfs_srcu)
{
*srcu_idx = srcu_read_lock(&debugfs_srcu);
barrier();
if (d_unlinked(dentry))
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(debugfs_use_file_start);
/**
* debugfs_use_file_finish - mark the end of file data access
* @srcu_idx: the SRCU index "created" by a former call to
* debugfs_use_file_start().
*
* Allow any ongoing concurrent call into debugfs_remove() or
* debugfs_remove_recursive() blocked by a former call to
* debugfs_use_file_start() to proceed and return to its caller.
*/
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
{
srcu_read_unlock(&debugfs_srcu, srcu_idx);
}
EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
#define F_DENTRY(filp) ((filp)->f_path.dentry)
#define REAL_FOPS_DEREF(dentry) \
((const struct file_operations *)(dentry)->d_fsdata)
static int open_proxy_open(struct inode *inode, struct file *filp)
{
const struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = NULL;
int srcu_idx, r;
r = debugfs_use_file_start(dentry, &srcu_idx);
if (r) {
r = -ENOENT;
goto out;
}
real_fops = REAL_FOPS_DEREF(dentry);
real_fops = fops_get(real_fops);
if (!real_fops) {
/* Huh? Module did not clean up after itself at exit? */
WARN(1, "debugfs file owner did not clean up at exit: %pd",
dentry);
r = -ENXIO;
goto out;
}
replace_fops(filp, real_fops);
if (real_fops->open)
r = real_fops->open(inode, filp);
out:
fops_put(real_fops);
debugfs_use_file_finish(srcu_idx);
return r;
}
const struct file_operations debugfs_open_proxy_file_operations = {
.open = open_proxy_open,
};
#define PROTO(args...) args
#define ARGS(args...) args
#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
static ret_type full_proxy_ ## name(proto) \
{ \
const struct dentry *dentry = F_DENTRY(filp); \
const struct file_operations *real_fops = \
REAL_FOPS_DEREF(dentry); \
int srcu_idx; \
ret_type r; \
\
r = debugfs_use_file_start(dentry, &srcu_idx); \
if (likely(!r)) \
r = real_fops->name(args); \
debugfs_use_file_finish(srcu_idx); \
return r; \
}
FULL_PROXY_FUNC(llseek, loff_t, filp,
PROTO(struct file *filp, loff_t offset,<