Page MenuHomec4science

clickfs.cc
No OneTemporary

File Metadata

Created
Fri, Jul 4, 14:37

clickfs.cc

// -*- c-basic-offset: 4 -*-
/*
* clickfs.cc -- the Click filesystem
* Eddie Kohler
*
* Copyright (c) 2002-2003 International Computer Science Institute
* Copyright (c) 2004-2010 Regents of the University of California
* Copyright (c) 2008 Meraki, Inc.
* Copyright (c) 2002-2013 Eddie Kohler
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include "modulepriv.hh"
#include "proclikefs.h"
#include <click/router.hh>
#include <click/master.hh>
#include <click/straccum.hh>
#include <click/llrpc.h>
#include <click/ino.hh>
#include <click/list.hh>
#include <click/cxxprotect.h>
CLICK_CXX_PROTECT
#include <linux/mutex.h>
#include <linux/namei.h>
#include <linux/proc_fs.h>
CLICK_CXX_UNPROTECT
#include <click/cxxunprotect.h>
#define CLICKFS_SUPER_MAGIC 0x436C696B /* "Clik" */
static struct file_operations *click_dir_file_ops;
static struct inode_operations *click_dir_inode_ops;
static struct file_operations *click_handler_file_ops;
static struct inode_operations *click_handler_inode_ops;
static struct dentry_operations click_dentry_ops;
static struct proclikefs_file_system *clickfs;
static struct mutex clickfs_lock;
extern uint32_t click_config_generation;
static int clickfs_ready;
//#define SPIN_LOCK_MSG(l, file, line, what) printk(KERN_ALERT "%s:%d: pid %d: %sing %p in clickfs\n", (file), (line), current->pid, (what), (l))
#define SPIN_LOCK_MSG(l, file, line, what) ((void)(file), (void)(line))
#define SPIN_LOCK(l, file, line) do { SPIN_LOCK_MSG((l), (file), (line), "lock"); mutex_lock((l)); } while (0)
#define SPIN_UNLOCK(l, file, line) do { SPIN_LOCK_MSG((l), (file), (line), "unlock"); mutex_unlock((l)); } while (0)
#define LOCK_CONFIG() lock_config(__FILE__, __LINE__)
#define UNLOCK_CONFIG(...) unlock_config(__FILE__, __LINE__, ## __VA_ARGS__)
#define DOWNGRADE_CONFIG_LOCK(r) downgrade_config_lock(__FILE__, __LINE__, r)
/*************************** Config locking *********************************/
extern struct task_struct *clickfs_task;
static inline Router*
lock_config(const char* file, int line)
{
SPIN_LOCK(&clickfs_lock, file, line);
return 0;
}
static inline void
unlock_config(const char* file, int line, Router* read_locked_router = 0)
{
if (read_locked_router)
read_locked_router->unuse();
else
SPIN_UNLOCK(&clickfs_lock, file, line);
}
static inline Router*
downgrade_config_lock(const char* file, int line, Router* router)
{
router->use();
SPIN_UNLOCK(&clickfs_lock, file, line);
return router;
}
/*************************** Inode constants ********************************/
#define INODE_INFO(inode) (*((ClickInodeInfo *)(&(inode)->i_private)))
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)
#define set_nlink(inode, nlink) ((inode)->i_nlink = (nlink))
#endif
struct ClickInodeInfo {
uint32_t config_generation;
};
static ClickIno click_ino;
// Must be called with LOCK_CONFIG held (& not downgraded).
static int click_ino_check() {
int r = 0;
if (click_ino.generation() != click_config_generation)
r = click_ino.prepare(click_router, click_config_generation);
return r;
}
// Must be called with LOCK_CONFIG held (& not downgraded).
// Return "subdir_error" if this subdirectory was configuration-specific and
// the configuration was changed. Otherwise, updates this directory's link
// count (if the configuration was changed) and returns 0.
// If click_ino_check returns < 0, then it is not safe to access
// click_ino. If click_ino_check returns 0, then it is safe to access
// both click_ino and `inode`.
static int click_ino_check(struct inode *inode, int subdir_error) {
int error;
if (INODE_INFO(inode).config_generation != click_config_generation) {
if (click_ino.has_element(inode->i_ino))
return subdir_error;
if ((error = click_ino_check()) < 0)
return error;
INODE_INFO(inode).config_generation = click_config_generation;
set_nlink(inode, click_ino.nlink(inode->i_ino));
}
return 0;
}
/*************************** Inode operations ********************************/
static struct inode *
click_inode(struct super_block *sb, ino_t ino)
{
// Must be called with clickfs_lock held.
if (click_ino_check() < 0)
return 0;
struct inode *inode = new_inode(sb);
if (!inode)
return 0;
inode->i_ino = ino;
INODE_INFO(inode).config_generation = click_config_generation;
if (click_ino.is_handler(ino)) {
int hi = click_ino.ino_handler(ino);
if (const Handler *h = Router::handler(click_router, hi)) {
inode->i_mode = S_IFREG;
if (h->read_visible())
inode->i_mode |= click_fsmode.read;
if (h->write_visible() || (h->read_visible() && h->read_param()))
inode->i_mode |= click_fsmode.write;
//Julien:
//inode->i_uid = click_fsmode.uid;
//inode->i_gid = click_fsmode.gid;
inode->i_uid.val = click_fsmode.uid;
inode->i_gid.val = click_fsmode.gid;
inode->i_op = click_handler_inode_ops;
inode->i_fop = click_handler_file_ops;
set_nlink(inode, click_ino.nlink(ino));
} else {
// can't happen
iput(inode);
inode = 0;
panic("click_inode");
}
} else {
inode->i_mode = click_fsmode.dir;
//Julien:
//inode->i_uid = click_fsmode.uid;
//inode->i_gid = click_fsmode.gid;
inode->i_uid.val = click_fsmode.uid;
inode->i_gid.val = click_fsmode.gid;
inode->i_op = click_dir_inode_ops;
inode->i_fop = click_dir_file_ops;
set_nlink(inode, click_ino.nlink(ino));
}
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
MDEBUG("%lx:%p:%p: leaving click_inode", ino, inode, inode->i_op);
return inode;
}
/*************************** Directory operations ****************************/
extern "C" {
static struct dentry *
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
click_dir_lookup(struct inode *dir, struct dentry *dentry, unsigned)
#else
click_dir_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *)
#endif
{
LOCK_CONFIG();
MDEBUG("click_dir_lookup %lx", dir->i_ino);
struct inode *inode = 0;
int error;
if ((error = click_ino_check(dir, -EIO)) >= 0) {
// BEWARE! Using stable_string() here is quite dangerous, since the
// data is actually mutable. The code path has been audited to make
// sure this is OK.
String dentry_name = String::make_stable(reinterpret_cast<const char *>(dentry->d_name.name), dentry->d_name.len);
if (ino_t new_ino = click_ino.lookup(dir->i_ino, dentry_name))
inode = click_inode(dir->i_sb, new_ino);
else
error = -ENOENT;
}
UNLOCK_CONFIG();
if (error < 0)
return reinterpret_cast<struct dentry *>(ERR_PTR(error));
else if (!inode)
// couldn't get an inode
return reinterpret_cast<struct dentry *>(ERR_PTR(-EINVAL));
else {
#if !HAVE_LINUX_SUPER_BLOCK_S_D_OP
dentry->d_op = &click_dentry_ops;
#endif
d_add(dentry, inode);
return 0;
}
}
static int
click_dentry_revalidate(struct dentry *dentry, unsigned flags)
{
struct inode *inode = dentry->d_inode;
int r;
MDEBUG("click_dentry_revalidate %lx", (inode ? inode->i_ino : 0));
if (!inode)
return -EINVAL;
if (INODE_INFO(inode).config_generation == click_config_generation)
return 1;
LOCK_CONFIG();
if (click_ino.has_element(inode->i_ino)) { // not a global directory
shrink_dcache_parent(dentry);
d_drop(dentry);
r = 0;
}
# ifdef LOOKUP_RCU
else if (flags & LOOKUP_RCU)
r = -ECHILD;
# endif
else if ((r = click_ino_check(inode, -EIO)) == 0)
r = 1;
UNLOCK_CONFIG();
return r;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
static int
click_dentry_revalidate_nd(struct dentry *dentry, struct nameidata *nd)
{
return click_dentry_revalidate(dentry, nd->flags);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
static bool
my_filldir(const char *name, int namelen, ino_t ino, int dirtype, loff_t f_pos, void *thunk)
{
struct dir_context* ctx = (struct dir_context*) thunk;
return dir_emit(ctx, name, namelen, ino, dirtype);
}
static int
click_dir_iterate(struct file *filp, struct dir_context* ctx)
{
struct inode *inode = filp->f_dentry->d_inode;
ino_t ino = inode->i_ino;
MDEBUG("click_dir_readdir %lx", ino);
LOCK_CONFIG();
int error = click_ino_check(inode, -ENOENT);
int stored = 0;
if (error < 0)
goto done;
// global '..'
if (ino == ClickIno::ino_globaldir && ctx->pos == 0) {
if (!my_filldir("..", 2, parent_ino(filp->f_dentry), ctx->pos, DT_DIR, ctx))
goto done;
ctx->pos = 1;
stored++;
}
// real entries
stored += click_ino.readdir(ino, ctx->pos, my_filldir, ctx);
done:
UNLOCK_CONFIG();
return (error ? error : stored);
}
#else
struct my_filldir_container {
filldir_t filldir;
void *dirent;
};
static bool
my_filldir(const char *name, int namelen, ino_t ino, int dirtype, loff_t f_pos, void *thunk)
{
my_filldir_container *mfd = (my_filldir_container *)thunk;
int error = mfd->filldir(mfd->dirent, name, namelen, f_pos, ino, dirtype);
return error >= 0;
}
static int
click_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct my_filldir_container mfd;
mfd.filldir = filldir;
mfd.dirent = dirent;
struct inode *inode = filp->f_dentry->d_inode;
ino_t ino = inode->i_ino;
loff_t f_pos = filp->f_pos;
MDEBUG("click_dir_readdir %lx", ino);
LOCK_CONFIG();
int error = click_ino_check(inode, -ENOENT);
int stored = 0;
if (error < 0)
goto done;
// global '..'
if (ino == ClickIno::ino_globaldir && f_pos == 0) {
if (!my_filldir("..", 2, filp->f_dentry->d_parent->d_inode->i_ino, f_pos, DT_DIR, &mfd))
goto done;
f_pos++;
stored++;
}
// real entries
stored += click_ino.readdir(ino, f_pos, my_filldir, &mfd);
done:
UNLOCK_CONFIG();
filp->f_pos = f_pos;
return (error ? error : stored);
}
#endif
} // extern "C"
/*************************** Superblock operations ***************************/
static struct super_operations click_superblock_ops;
extern "C" {
static struct super_block *
click_read_super(struct super_block *sb, void * /* data */, int)
{
struct inode *root_inode = 0;
if (!clickfs_ready)
goto out_no_root;
MDEBUG("click_read_super");
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_magic = CLICKFS_SUPER_MAGIC;
sb->s_op = &click_superblock_ops;
#if HAVE_LINUX_SUPER_BLOCK_S_D_OP
sb->s_d_op = &click_dentry_ops;
#endif
MDEBUG("click_config_lock");
LOCK_CONFIG();
root_inode = click_inode(sb, ClickIno::ino_globaldir);
UNLOCK_CONFIG();
if (!root_inode)
goto out_no_root;
#if HAVE_LINUX_D_MAKE_ROOT
sb->s_root = d_make_root(root_inode);
#else
sb->s_root = d_alloc_root(root_inode);
#endif
MDEBUG("got root inode %p:%p", root_inode, root_inode->i_op);
MDEBUG("d_op %p:%p", &click_dentry_ops, sb->s_root->d_op);
if (!sb->s_root)
goto out_no_root;
// XXX options
MDEBUG("got root directory");
proclikefs_read_super(sb);
MDEBUG("done click_read_super");
return sb;
out_no_root:
printk(KERN_ALERT "click_read_super: get root inode failed\n");
iput(root_inode);
sb->s_dev = 0;
return 0;
}
static int
click_fill_super(struct super_block *sb, void *data, int flags)
{
return click_read_super(sb, data, flags) ? 0 : -ENOMEM;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
static struct dentry *
click_get_sb(struct file_system_type *fs_type, int flags, const char *, void *data)
{
return mount_single(fs_type, flags, data, click_fill_super);
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
static int
click_get_sb(struct file_system_type *fs_type, int flags, const char *, void *data, struct vfsmount *vfsmount)
{
return get_sb_single(fs_type, flags, data, click_fill_super, vfsmount);
}
#else
static struct super_block *
click_get_sb(struct file_system_type *fs_type, int flags, const char *, void *data)
{
return get_sb_single(fs_type, flags, data, click_fill_super);
}
#endif
static void
click_reread_super(struct super_block *sb)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
lock_super(sb);
#endif
if (sb->s_root) {
struct inode *old_inode = sb->s_root->d_inode;
LOCK_CONFIG();
sb->s_root->d_inode = click_inode(sb, ClickIno::ino_globaldir);
UNLOCK_CONFIG();
iput(old_inode);
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_op = &click_superblock_ops;
#if HAVE_LINUX_SUPER_BLOCK_S_D_OP
sb->s_d_op = &click_dentry_ops;
#endif
} else
printk(KERN_ALERT "silly click_reread_super\n");
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
unlock_super(sb);
#endif
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
static int
click_delete_dentry(const struct dentry *)
{
return 1;
}
#else
static int
click_delete_dentry(struct dentry *)
{
return 1;
}
#endif
} // extern "C"
/*************************** Handler operations ******************************/
// see static_assert below
#define HS_ALIVE 1
#define HS_READING 2
#define HS_DONE 4
#define HS_RAW 8
#define HS_DIRECT HANDLER_DIRECT
#define HS_WRITE_UNLIMITED HANDLER_WRITE_UNLIMITED
struct HandlerString {
String data;
int flags; // 0 means free
List_member<HandlerString> link;
};
// handler_strings holds all currently active HandlerString objects, as well
// as a selection of freed ones for fast reuse. Freed objects are at the
// front() of the list.
static List<HandlerString, &HandlerString::link>* handler_strings = 0;
static int free_handler_strings = 0;
static struct mutex handler_strings_lock;
#define FILP_HS(filp) (reinterpret_cast<HandlerString*>((filp)->private_data))
#define FILP_READ_HS(filp) FILP_HS(filp)
#define FILP_WRITE_HS(filp) FILP_HS(filp)
static HandlerString* alloc_handler_string(const Handler* h) {
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
HandlerString* hs = 0;
if (!handler_strings)
handler_strings = new List<HandlerString, &HandlerString::link>;
if (handler_strings) {
if (!handler_strings->empty() && !handler_strings->front()->flags) {
hs = handler_strings->front();
handler_strings->pop_front();
--free_handler_strings;
} else
hs = new HandlerString;
if (hs) {
handler_strings->push_back(hs);
hs->flags = HS_ALIVE
| (h->flags() & (HS_WRITE_UNLIMITED | HS_DIRECT));
}
}
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
return hs;
}
static void free_handler_string(HandlerString* hs) {
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
handler_strings->erase(hs);
hs->data = String();
hs->flags = 0;
if (free_handler_strings < 8) {
handler_strings->push_front(hs);
++free_handler_strings;
} else
delete hs;
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
}
static inline void handler_string_add_newline(HandlerString* hs,
const Handler* h) {
if (!h->raw()
&& !(hs->flags & HS_RAW)
&& !(hs->flags & HS_DIRECT)
&& hs->data
&& hs->data.back() != '\n')
hs->data += '\n';
}
static inline String handler_string_strip_newline(const HandlerString* hs,
const Handler* h) {
if (!h->raw()
&& !(hs->flags & HS_RAW)
&& hs->data
&& hs->data.back() == '\n')
return hs->data.substring(hs->data.begin(), hs->data.end() - 1);
else
return hs->data;
}
static void
lock_threads()
{
click_master->block_all();
}
static void
unlock_threads()
{
click_master->unblock_all();
}
namespace {
class ClickfsHandlerErrorHandler : public ErrorHandler { public:
ClickfsHandlerErrorHandler() {
}
void *emit(const String &str, void *, bool) {
_sa << str << '\n';
return 0;
}
StringAccum _sa;
};
}
extern "C" {
static int
handler_open(struct inode *inode, struct file *filp)
{
LOCK_CONFIG();
bool reading = (filp->f_mode & FMODE_READ) != 0;
bool writing = (filp->f_mode & FMODE_WRITE) != 0;
int retval = 0;
HandlerString* hs = 0;
const Handler *h;
if ((filp->f_flags & O_APPEND)
|| (!reading && writing && !(filp->f_flags & O_TRUNC)))
retval = -EACCES;
else if ((retval = click_ino_check(inode, -EIO)) < 0)
/* save retval */;
else if (!(h = Router::handler(click_router, click_ino.ino_handler(inode->i_ino))))
retval = -EIO;
else if (reading && writing && (h->flags() & HANDLER_DIRECT))
retval = -EACCES;
else if (reading ? !h->read_visible() : !h->write_visible())
retval = -EPERM;
else if (!(hs = alloc_handler_string(h)))
retval = -ENOMEM;
else
retval = 0;
UNLOCK_CONFIG();
if (retval < 0 && hs) {
free_handler_string(hs);
hs = 0;
}
filp->private_data = hs;
return retval;
}
static ssize_t
handler_prepare_read(HandlerString* hs, struct file* filp,
char* buffer, size_t count, loff_t* store_f_pos)
{
Router* locktype = LOCK_CONFIG();
ssize_t retval;
const Handler *h;
struct inode *inode = filp->f_dentry->d_inode;
if ((retval = click_ino_check(inode, -EIO)) < 0)
/* save retval */;
else if (!(h = Router::handler(click_router, click_ino.ino_handler(inode->i_ino))))
retval = -EIO;
else if (!h->read_visible())
retval = -EPERM;
else {
int eindex = click_ino.ino_element(inode->i_ino);
Element *e = Router::element(click_router, eindex);
if (h->allow_concurrent_handlers())
locktype = DOWNGRADE_CONFIG_LOCK(e->router());
if ((hs->flags & HS_DIRECT) && buffer) {
click_handler_direct_info hdi;
hdi.buffer = buffer;
hdi.count = count;
hdi.store_f_pos = store_f_pos;
hdi.string = &hs->data;
hdi.retval = 0;
(void) h->__call_read(e, &hdi);
count = hdi.count;
retval = hdi.retval;
} else if (hs->flags & HS_DIRECT)
retval = -EINVAL;
else {
String param;
if ((filp->f_mode & FMODE_READ) && (filp->f_mode & FMODE_WRITE))
param = handler_string_strip_newline(hs, h);
if (!h->allow_concurrent_threads()) {
lock_threads();
hs->data = h->call_read(e, param, 0);
unlock_threads();
} else
hs->data = h->call_read(e, param, 0);
handler_string_add_newline(hs, h);
retval = (hs->data.out_of_memory() ? -ENOMEM : 0);
}
}
UNLOCK_CONFIG(locktype);
if (retval >= 0)
hs->flags |= HS_DONE;
return retval;
}
static loff_t
handler_llseek(struct file* filp, loff_t offset, int origin)
{
HandlerString* hs = FILP_HS(filp);
if (!hs)
return -EIO;
if ((filp->f_mode & FMODE_READ) && (filp->f_mode & FMODE_WRITE))
return -ESPIPE;
if (origin == SEEK_END) {
// ensure the string's existence before seeking
if ((filp->f_mode & FMODE_READ)
&& (hs->flags & (HS_DIRECT | HS_DONE)) != HS_DONE) {
ssize_t r = handler_prepare_read(hs, filp, 0, 0, 0);
if (r < 0)
return r;
}
offset += hs->data.length();
} else if (origin == SEEK_CUR)
offset += filp->f_pos;
if (offset >= 0 && offset <= 0x7FFFFFFF) {
if (offset != filp->f_pos) {
filp->f_pos = offset;
filp->f_version = 0;
}
return offset;
} else
return -EINVAL;
}
static ssize_t
handler_read(struct file *filp, char *buffer, size_t count, loff_t *store_f_pos)
{
loff_t f_pos = *store_f_pos;
ssize_t r = -EIO;
HandlerString* hs = FILP_READ_HS(filp);
if (!hs)
return r;
// read-write handler: reset file position if switching to reading
if ((filp->f_mode & FMODE_READ) && (filp->f_mode & FMODE_WRITE)
&& !(hs->flags & HS_READING)) {
f_pos = 0;
hs->flags |= HS_READING;
}
// (re)read handler if necessary
if ((hs->flags & (HS_DIRECT | HS_DONE)) != HS_DONE) {
r = handler_prepare_read(hs, filp, buffer, count, store_f_pos);
if (r < 0)
return r;
}
if (!(hs->flags & HS_DIRECT)) {
const String &s = hs->data;
if (f_pos > s.length())
f_pos = s.length();
if (f_pos + count > s.length())
count = s.length() - f_pos;
if (copy_to_user(buffer, s.data() + f_pos, count) > 0)
return -EFAULT;
*store_f_pos = f_pos + count;
r = count;
}
return r;
}
static ssize_t
handler_write(struct file *filp, const char *buffer, size_t count, loff_t *store_f_pos)
{
loff_t f_pos = *store_f_pos;
HandlerString* hs = FILP_WRITE_HS(filp);
if (!hs)
return -EIO;
String &s = hs->data;
int old_length = s.length();
hs->flags &= ~HS_DONE;
#ifdef LARGEST_HANDLER_WRITE
if (f_pos + count > LARGEST_HANDLER_WRITE
&& !(hs->flags & HS_WRITE_UNLIMITED))
return -EFBIG;
#endif
// read-write handler: reset file position if switching to writing
if ((filp->f_mode & FMODE_READ) && (filp->f_mode & FMODE_WRITE)
&& (hs->flags & HS_READING)) {
f_pos = 0;
hs->flags &= ~HS_READING;
}
if (f_pos + count > old_length) {
s.append_fill(0, f_pos + count - old_length);
if (s.out_of_memory())
return -ENOMEM;
}
int length = s.length();
if (f_pos > length)
return -EFBIG;
else if (f_pos + count > length)
count = length - f_pos;
char *data = s.mutable_data();
if (f_pos > old_length)
memset(data + old_length, 0, f_pos - old_length);
if (copy_from_user(data + f_pos, buffer, count) > 0)
return -EFAULT;
*store_f_pos = f_pos + count;
return count;
}
static int
handler_do_write(struct file *filp, void *address_ptr)
{
Router* locktype = LOCK_CONFIG();
HandlerString* hs = FILP_WRITE_HS(filp);
struct inode *inode = filp->f_dentry->d_inode;
const Handler *h;
int retval;
if ((retval = click_ino_check(inode, -EIO)) < 0)
/* save retval */;
else if (!(h = Router::handler(click_router, click_ino.ino_handler(inode->i_ino)))
|| !h->write_visible())
retval = -EIO;
else if (hs->data.out_of_memory())
retval = -ENOMEM;
else {
int eindex = click_ino.ino_element(inode->i_ino);
Element *e = Router::element(click_router, eindex);
if (h->allow_concurrent_handlers())
locktype = DOWNGRADE_CONFIG_LOCK(e->router());
click_llrpc_call_handler_st chs;
chs.flags = 0;
int r;
if (address_ptr
&& (r = CLICK_LLRPC_GET_DATA(&chs, address_ptr, sizeof(chs))) < 0) {
retval = r;
goto exit;
}
String data;
if (!address_ptr || !(chs.flags & CLICK_LLRPC_CALL_HANDLER_FLAG_RAW))
data = handler_string_strip_newline(hs, h);
else
data = hs->data;
ClickfsHandlerErrorHandler cerrh;
if (!h->allow_concurrent_threads()) {
lock_threads();
retval = h->call_write(data, e, &cerrh);
unlock_threads();
} else
retval = h->call_write(data, e, &cerrh);
hs->flags |= HS_DONE;
if (cerrh._sa && !address_ptr) {
ErrorHandler *errh = click_logged_errh;
if (e)
errh->message("In write handler '%s' for '%s':", h->name().c_str(), e->declaration().c_str());
else
errh->message("In write handler '%s':", h->name().c_str());
String str = cerrh._sa.take_string();
const char *s = str.begin(), *end = str.end();
while (s != end) {
const char *nl = find(s, end, '\n');
errh->xmessage(ErrorHandler::combine_anno(str.substring(s, nl), " "));
s = nl + (nl != end);
}
}
if (address_ptr && chs.errorlen > 0) {
String str = cerrh._sa.take_string();
const char *s = str.begin(), *end = str.end();
while (s != end) {
String landmark;
s = ErrorHandler::parse_anno(str, s, end, "l", &landmark,
(const char *) 0);
const char *nl = find(s, end, '\n');
cerrh._sa << ErrorHandler::clean_landmark(landmark, true)
<< str.substring(s, nl) << '\n';
s = nl + (nl != end);
}
size_t len = cerrh._sa.length();
if (len > chs.errorlen)
len = chs.errorlen;
chs.errorlen = cerrh._sa.length();
if (chs.errorlen > 0
&& (r = CLICK_LLRPC_PUT_DATA(chs.errorbuf, cerrh._sa.data(), len)) < 0) {
retval = r;
goto exit;
}
if ((r = CLICK_LLRPC_PUT_DATA(address_ptr, &chs, sizeof(chs))) < 0) {
retval = r;
goto exit;
}
}
}
exit:
UNLOCK_CONFIG(locktype);
return retval;
}
static int
handler_flush(struct file *filp
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
, struct files_struct *files
#endif
)
{
bool writing = (filp->f_mode & FMODE_WRITE) && !(filp->f_mode & FMODE_READ);
HandlerString* hs = FILP_WRITE_HS(filp);
int retval = 0;
#ifdef file_count
long f_count = file_count(filp);
#else
int f_count = atomic_read(&filp->f_count);
#endif
if (writing && f_count == 1 && hs && !(hs->flags & HS_DONE))
retval = handler_do_write(filp, 0);
return retval;
}
static int
handler_release(struct inode *, struct file *filp)
{
// free handler string
HandlerString* hs = FILP_READ_HS(filp);
if (hs)
free_handler_string(hs);
return 0;
}
static inline int
do_handler_ioctl(struct inode *inode, struct file *filp,
unsigned command, unsigned long address)
{
Router* locktype = LOCK_CONFIG();
int retval;
Element *e;
if ((retval = click_ino_check(inode, -EIO)) < 0)
/* save retval */;
else if (!click_router)
retval = -EINVAL;
else if (command == CLICK_LLRPC_CALL_HANDLER)
retval = handler_do_write(filp, reinterpret_cast<void *>(address));
else if (command == CLICK_LLRPC_ABANDON_HANDLER) {
HandlerString* hs = FILP_HS(filp);
hs->data = String();
hs->flags |= HS_DONE;
retval = 0;
} else if (command == CLICK_LLRPC_RAW_HANDLER) {
HandlerString* hs = FILP_HS(filp);
hs->flags |= HS_RAW;
retval = 0;
} else if (click_ino.ino_element(inode->i_ino) < 0
|| !(e = click_router->element(click_ino.ino_element(inode->i_ino))))
retval = -EIO;
else {
if (command & _CLICK_IOC_SAFE)
locktype = DOWNGRADE_CONFIG_LOCK(e->router());
union {
char buf[128];
long align;
} ubuf;
char *data;
void *address_ptr, *arg_ptr;
// allocate ioctl buffer
int size = _CLICK_IOC_SIZE(command);
if (size <= 128)
data = ubuf.buf;
else if (size > 16384 || !(data = new char[size])) {
retval = -ENOMEM;
goto exit;
}
// fetch incoming data if necessary
address_ptr = reinterpret_cast<void *>(address);
if (size && (command & _CLICK_IOC_IN)
&& (retval = CLICK_LLRPC_GET_DATA(data, address_ptr, size)) < 0)
goto free_exit;
// call llrpc
if (size && (command & (_CLICK_IOC_IN | _CLICK_IOC_OUT)))
arg_ptr = data;
else
arg_ptr = address_ptr;
if (e->router()->initialized())
retval = e->llrpc(command, arg_ptr);
else
retval = e->Element::llrpc(command, arg_ptr);
// store outgoing data if necessary
if (retval >= 0 && size && (command & _CLICK_IOC_OUT))
retval = CLICK_LLRPC_PUT_DATA(address_ptr, data, size);
free_exit:
if (data != ubuf.buf)
delete[] data;
}
exit:
UNLOCK_CONFIG(locktype);
return retval;
}
#if HAVE_UNLOCKED_IOCTL
static long
handler_unlocked_ioctl(struct file *filp,
unsigned command, unsigned long address)
{
return do_handler_ioctl(filp->f_dentry->d_inode, filp, command, address);
}
#else
static int
handler_ioctl(struct inode *inode, struct file *filp,
unsigned command, unsigned long address)
{
return do_handler_ioctl(inode, filp, command, address);
}
#endif
#if INO_DEBUG
static String
read_ino_info(Element *, void *)
{
return click_ino.info();
}
#endif
} // extern "C"
/*********************** Initialization and termination **********************/
struct file_operations *
click_new_file_operations()
{
if (!clickfs)
clickfs = proclikefs_register_filesystem("click", 0, click_get_sb);
if (clickfs)
return proclikefs_new_file_operations(clickfs);
else
return 0;
}
int
init_clickfs()
{
static_assert(HANDLER_DIRECT + HANDLER_WRITE_UNLIMITED < Handler::USER_FLAG_0, "Too few driver handler flags available.");
static_assert(((HS_DIRECT | HS_WRITE_UNLIMITED) & (HS_READING | HS_DONE | HS_RAW)) == 0, "Handler flag overlap.");
mutex_init(&handler_strings_lock);
mutex_init(&clickfs_lock);
// clickfs creation moved to click_new_file_operations()
if (!(click_dir_file_ops = click_new_file_operations())
|| !(click_dir_inode_ops = proclikefs_new_inode_operations(clickfs))
|| !(click_handler_file_ops = click_new_file_operations())
|| !(click_handler_inode_ops = proclikefs_new_inode_operations(clickfs))) {
printk(KERN_ALERT "click: could not initialize clickfs!\n");
return -EINVAL;
}
click_superblock_ops.put_super = proclikefs_put_super;
// XXX statfs
click_dentry_ops.d_delete = click_delete_dentry;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
click_dentry_ops.d_revalidate = click_dentry_revalidate;
#else
click_dentry_ops.d_revalidate = click_dentry_revalidate_nd;
#endif
click_dir_file_ops->read = generic_read_dir;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
click_dir_file_ops->iterate = click_dir_iterate;
#else
click_dir_file_ops->readdir = click_dir_readdir;
#endif
click_dir_inode_ops->lookup = click_dir_lookup;
click_handler_file_ops->llseek = handler_llseek;
click_handler_file_ops->read = handler_read;
click_handler_file_ops->write = handler_write;
#if HAVE_UNLOCKED_IOCTL
click_handler_file_ops->unlocked_ioctl = handler_unlocked_ioctl;
#else
click_handler_file_ops->ioctl = handler_ioctl;
#endif
click_handler_file_ops->open = handler_open;
click_handler_file_ops->flush = handler_flush;
click_handler_file_ops->release = handler_release;
click_ino.initialize();
proclikefs_reinitialize_supers(clickfs, click_reread_super);
clickfs_ready = 1;
// initialize a symlink from /proc/click -> /click, to ease transition
(void) proc_symlink("click", 0, "/click");
#if INO_DEBUG
Router::add_read_handler(0, "ino_info", read_ino_info, 0);
#endif
return 0;
}
void
cleanup_clickfs()
{
MDEBUG("cleanup_clickfs");
// remove the '/proc/click' directory
remove_proc_entry("click", 0);
// kill filesystem
MDEBUG("proclikefs_unregister_filesystem");
clickfs_ready = 0;
if (clickfs)
proclikefs_unregister_filesystem(clickfs);
// clean up handler_strings
MDEBUG("cleaning up handler strings");
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
while (handler_strings && !handler_strings->empty()) {
HandlerString* hs = handler_strings->front();
handler_strings->pop_front();
delete hs;
}
delete handler_strings;
handler_strings = 0;
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
MDEBUG("click_ino cleanup");
click_ino.cleanup();
}

Event Timeline