Commit 66f54963 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Greg Kroah-Hartman

debugfs: implement symbolic links

debugfs: implement symbolic links

Implement a new function debugfs_create_symlink() which can be used
to create symbolic links in debugfs. This function can be useful
for people moving functionality from /proc to debugfs (e.g. the
gcov-kernel patch).
Signed-off-by: default avatarPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b92be9f1
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
static ssize_t default_read_file(struct file *file, char __user *buf, static ssize_t default_read_file(struct file *file, char __user *buf,
...@@ -44,6 +45,17 @@ const struct file_operations debugfs_file_operations = { ...@@ -44,6 +45,17 @@ const struct file_operations debugfs_file_operations = {
.open = default_open, .open = default_open,
}; };
static void *debugfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
nd_set_link(nd, dentry->d_inode->i_private);
return NULL;
}
const struct inode_operations debugfs_link_operations = {
.readlink = generic_readlink,
.follow_link = debugfs_follow_link,
};
static void debugfs_u8_set(void *data, u64 val) static void debugfs_u8_set(void *data, u64 val)
{ {
*(u8 *)data = val; *(u8 *)data = val;
......
...@@ -25,11 +25,13 @@ ...@@ -25,11 +25,13 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/string.h>
#define DEBUGFS_MAGIC 0x64626720 #define DEBUGFS_MAGIC 0x64626720
/* declared over in file.c */ /* declared over in file.c */
extern struct file_operations debugfs_file_operations; extern struct file_operations debugfs_file_operations;
extern struct inode_operations debugfs_link_operations;
static struct vfsmount *debugfs_mount; static struct vfsmount *debugfs_mount;
static int debugfs_mount_count; static int debugfs_mount_count;
...@@ -51,6 +53,9 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d ...@@ -51,6 +53,9 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d
case S_IFREG: case S_IFREG:
inode->i_fop = &debugfs_file_operations; inode->i_fop = &debugfs_file_operations;
break; break;
case S_IFLNK:
inode->i_op = &debugfs_link_operations;
break;
case S_IFDIR: case S_IFDIR:
inode->i_op = &simple_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
...@@ -96,6 +101,12 @@ static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -96,6 +101,12 @@ static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return res; return res;
} }
static int debugfs_link(struct inode *dir, struct dentry *dentry, int mode)
{
mode = (mode & S_IALLUGO) | S_IFLNK;
return debugfs_mknod(dir, dentry, mode, 0);
}
static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode) static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode)
{ {
int res; int res;
...@@ -158,10 +169,17 @@ static int debugfs_create_by_name(const char *name, mode_t mode, ...@@ -158,10 +169,17 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
mutex_lock(&parent->d_inode->i_mutex); mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name)); *dentry = lookup_one_len(name, parent, strlen(name));
if (!IS_ERR(*dentry)) { if (!IS_ERR(*dentry)) {
if ((mode & S_IFMT) == S_IFDIR) switch (mode & S_IFMT) {
case S_IFDIR:
error = debugfs_mkdir(parent->d_inode, *dentry, mode); error = debugfs_mkdir(parent->d_inode, *dentry, mode);
else break;
case S_IFLNK:
error = debugfs_link(parent->d_inode, *dentry, mode);
break;
default:
error = debugfs_create(parent->d_inode, *dentry, mode); error = debugfs_create(parent->d_inode, *dentry, mode);
break;
}
dput(*dentry); dput(*dentry);
} else } else
error = PTR_ERR(*dentry); error = PTR_ERR(*dentry);
...@@ -258,6 +276,49 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) ...@@ -258,6 +276,49 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
} }
EXPORT_SYMBOL_GPL(debugfs_create_dir); EXPORT_SYMBOL_GPL(debugfs_create_dir);
/**
* debugfs_create_symlink- create a symbolic link in the debugfs filesystem
* @name: a pointer to a string containing the name of the symbolic link to
* create.
* @parent: a pointer to the parent dentry for this symbolic link. This
* should be a directory dentry if set. If this paramater is NULL,
* then the symbolic link will be created in the root of the debugfs
* filesystem.
* @target: a pointer to a string containing the path to the target of the
* symbolic link.
*
* This function creates a symbolic link with the given name in debugfs that
* links to the given target path.
*
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the symbolic
* link is to be removed (no automatic cleanup happens if your module is
* unloaded, you are responsible here.) If an error occurs, %NULL will be
* returned.
*
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
const char *target)
{
struct dentry *result;
char *link;
link = kstrdup(target, GFP_KERNEL);
if (!link)
return NULL;
result = debugfs_create_file(name, S_IFLNK | S_IRWXUGO, parent, link,
NULL);
if (!result)
kfree(link);
return result;
}
EXPORT_SYMBOL_GPL(debugfs_create_symlink);
/** /**
* debugfs_remove - removes a file or directory from the debugfs filesystem * debugfs_remove - removes a file or directory from the debugfs filesystem
* @dentry: a pointer to a the dentry of the file or directory to be * @dentry: a pointer to a the dentry of the file or directory to be
...@@ -287,15 +348,22 @@ void debugfs_remove(struct dentry *dentry) ...@@ -287,15 +348,22 @@ void debugfs_remove(struct dentry *dentry)
if (debugfs_positive(dentry)) { if (debugfs_positive(dentry)) {
if (dentry->d_inode) { if (dentry->d_inode) {
dget(dentry); dget(dentry);
if (S_ISDIR(dentry->d_inode->i_mode)) { switch (dentry->d_inode->i_mode & S_IFMT) {
case S_IFDIR:
ret = simple_rmdir(parent->d_inode, dentry); ret = simple_rmdir(parent->d_inode, dentry);
if (ret) if (ret)
printk(KERN_ERR printk(KERN_ERR
"DebugFS rmdir on %s failed : " "DebugFS rmdir on %s failed : "
"directory not empty.\n", "directory not empty.\n",
dentry->d_name.name); dentry->d_name.name);
} else break;
case S_IFLNK:
kfree(dentry->d_inode->i_private);
/* fall through */
default:
simple_unlink(parent->d_inode, dentry); simple_unlink(parent->d_inode, dentry);
break;
}
if (!ret) if (!ret)
d_delete(dentry); d_delete(dentry);
dput(dentry); dput(dentry);
......
...@@ -33,6 +33,9 @@ struct dentry *debugfs_create_file(const char *name, mode_t mode, ...@@ -33,6 +33,9 @@ struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
const char *dest);
void debugfs_remove(struct dentry *dentry); void debugfs_remove(struct dentry *dentry);
struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *debugfs_create_u8(const char *name, mode_t mode,
...@@ -70,6 +73,13 @@ static inline struct dentry *debugfs_create_dir(const char *name, ...@@ -70,6 +73,13 @@ static inline struct dentry *debugfs_create_dir(const char *name,
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
static inline struct dentry *debugfs_create_symlink(const char *name,
struct dentry *parent,
const char *dest)
{
return ERR_PTR(-ENODEV);
}
static inline void debugfs_remove(struct dentry *dentry) static inline void debugfs_remove(struct dentry *dentry)
{ } { }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment