// -*- c-basic-offset: 4 -*-
/*
* clickfs.cc -- the Click filesystem
* Eddie Kohler
*
* Copyright (c) 2002-2003 International Computer Science Institute
*
* This source code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <click/config.h>
#include "modulepriv.hh"
#include "proclikefs.h"
#include <click/router.hh>
#include <click/master.hh>
#include <click/llrpc.h>
#include <click/ino.hh>
#include <click/cxxprotect.h>
CLICK_CXX_PROTECT
#include <linux/spinlock.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
# include <linux/locks.h>
#endif
#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;
spinlock_t clickfs_write_lock;
atomic_t clickfs_read_count;
extern uint32_t click_config_generation;
static int clickfs_ready;
//#define SPIN_LOCK_MSG(l, file, line, what) printk("<1>%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"); spin_lock((l)); } while (0)
#define SPIN_UNLOCK(l, file, line) do { SPIN_LOCK_MSG((l), (file), (line), "unlock"); spin_unlock((l)); } while (0)
#define LOCK_CONFIG_READ() lock_config_read(__FILE__, __LINE__)
#define UNLOCK_CONFIG_READ() unlock_config_read()
#define LOCK_CONFIG_WRITE() lock_config_write(__FILE__, __LINE__)
#define UNLOCK_CONFIG_WRITE() unlock_config_write(__FILE__, __LINE__)
/*************************** Config locking *********************************/
static inline void
lock_config_read(const char *file, int line)
{
SPIN_LOCK_MSG(&clickfs_write_lock, file, line, "soft lock");
while (!spin_trylock(&clickfs_write_lock))
schedule();
atomic_inc(&clickfs_read_count);
SPIN_UNLOCK(&clickfs_write_lock, file, line);
}
static inline void
unlock_config_read()
{
assert(atomic_read(&clickfs_read_count) > 0);
atomic_dec(&clickfs_read_count);
}
static inline void
lock_config_write(const char *file, int line)
{
while (1) {
SPIN_LOCK_MSG(&clickfs_write_lock, file, line, "soft lock");
while (!spin_trylock(&clickfs_write_lock))
schedule();
if (atomic_read(&clickfs_read_count) == 0)
return;
SPIN_UNLOCK(&clickfs_write_lock, file, line);
schedule();
}
}
static inline void
unlock_config_write(const char *file, int line)
{
SPIN_UNLOCK(&clickfs_write_lock, file, line);
}
/*************************** Inode constants ********************************/
#define INODE_INFO(inode) (*((ClickInodeInfo *)(&(inode)->u)))
struct ClickInodeInfo {
uint32_t config_generation;
};
inline bool
inode_out_of_date(struct inode *inode)
{
return INO_ELEMENTNO(inode->i_ino) >= 0
&& INODE_INFO(inode).config_generation != click_config_generation;
}
static ClickIno click_ino;
/*************************** Inode operations ********************************/
#ifdef LINUX_2_2
// borrowed from Linux 2.4
static inline struct inode *
new_inode(struct super_block *sb)
{
struct inode *inode = get_empty_inode();
if (inode) {
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
}
return inode;
}
#endif
static struct inode *
click_inode(struct super_block *sb, ino_t ino)
{
// Must be called with click_config_lock held.
if (click_ino.prepare(click_router, click_config_generation) < 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 (INO_ISHANDLER(ino)) {
int hi = INO_HANDLERNO(ino);
if (const Handler *h = Router::handler(click_router, hi)) {
inode->i_mode = S_IFREG | (h->read_visible() ? click_mode_r : 0) | (h->write_visible() ? click_mode_w : 0);
inode->i_uid = inode->i_gid = 0;
inode->i_op = click_handler_inode_ops;
#ifdef LINUX_2_4
inode->i_fop = click_handler_file_ops;
#endif
inode->i_nlink = click_ino.nlink(ino);
} else {
// can't happen
iput(inode);
inode = 0;
panic("click_inode");
}
} else {
inode->i_mode = click_mode_dir;
inode->i_uid = inode->i_gid = 0;
inode->i_op = click_dir_inode_ops;
#ifdef LINUX_2_4
inode->i_fop = click_dir_file_ops;
#endif
inode->i_nlink = 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(2, 6, 0)
click_dir_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *)
#else
click_dir_lookup(struct inode *dir, struct dentry *dentry)
#endif
{
LOCK_CONFIG_READ();
MDEBUG("click_dir_lookup %lx", dir->i_ino);
struct inode *inode = 0;
int error;
if (inode_out_of_date(dir))
error = -EIO;
else if ((error = click_ino.prepare(click_router, click_config_generation)) < 0)
/* save error */;
else {
String dentry_name = String::stable_string(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_READ();
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 {
dentry->d_op = &click_dentry_ops;
d_add(dentry, inode);
return 0;
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
static int
click_dir_revalidate(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
MDEBUG("click_dir_revalidate %lx", (inode ? inode->i_ino : 0));
if (!inode)
return -EINVAL;
int error = 0;
LOCK_CONFIG_READ();
if (INODE_INFO(inode).config_generation != click_config_generation) {
if (INO_ELEMENTNO(inode->i_ino) >= 0) // not a global directory
error = -EIO;
else if ((error = click_ino.prepare(click_router, click_config_generation)) < 0)
/* preserve error */;
else {
INODE_INFO(inode).config_generation = click_config_generation;
inode->i_nlink = click_ino.nlink(inode->i_ino);
}
}
UNLOCK_CONFIG_READ();
return error;
}
#endif
struct my_filldir_container {
filldir_t filldir;
void *dirent;
};
static bool
my_filldir(const char *name, int namelen, ino_t ino, int dirtype, uint32_t f_pos, void *thunk)
{
my_filldir_container *mfd = (my_filldir_container *)thunk;
#ifdef LINUX_2_2
(void)dirtype;
int error = mfd->filldir(mfd->dirent, name, namelen, f_pos, ino);
#else
int error = mfd->filldir(mfd->dirent, name, namelen, f_pos, ino, dirtype);
#endif
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;
uint32_t f_pos = filp->f_pos;
MDEBUG("click_dir_readdir %lx", ino);
LOCK_CONFIG_READ();
int error;
if (inode_out_of_date(inode))
error = -ENOENT;
else
error = click_ino.prepare(click_router, click_config_generation);
// '.' and '..'
if (error >= 0 && f_pos == 0) {
if (my_filldir(".", 1, ino, f_pos, DT_DIR, &mfd))
f_pos++;
else
error = -1;
}
if (error >= 0 && f_pos == 1) {
if (my_filldir("..", 2, filp->f_dentry->d_parent->d_inode->i_ino, f_pos, DT_DIR, &mfd))
f_pos++;
else
error = -1;
}
// real entries
if (error >= 0)
error = click_ino.readdir(ino, f_pos, my_filldir, &mfd);
UNLOCK_CONFIG_READ();
filp->f_pos = f_pos;
return (error == -1 ? 0 : error);
}
} // extern "C"
/*************************** Superblock operations ***************************/
static struct super_operations click_superblock_ops;
extern "C" {
#ifdef LINUX_2_2
static void
click_write_inode(struct inode *)
{
}
static void
click_put_inode(struct inode *inode)
{
// Delete inodes when they're unused, since we can recreate them easily.
if (inode->i_count == 1)
inode->i_nlink = 0;
}
#endif
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;
MDEBUG("click_config_lock");
LOCK_CONFIG_READ();
root_inode = click_inode(sb, INO_GLOBALDIR);
UNLOCK_CONFIG_READ();
if (!root_inode)
goto out_no_root;
#ifdef LINUX_2_4
sb->s_root = d_alloc_root(root_inode);
#else
sb->s_root = d_alloc_root(root_inode, 0);
#endif
MDEBUG("got root inode %p:%p", root_inode, root_inode->i_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("<1>click_read_super: get root inode failed\n");
iput(root_inode);
sb->s_dev = 0;
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
static int
click_fill_super(struct super_block *sb, void *data, int flags)
{
return click_read_super(sb, data, flags) ? 0 : -ENOMEM;
}
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)
{
lock_super(sb);
if (sb->s_root) {
struct inode *old_inode = sb->s_root->d_inode;
LOCK_CONFIG_READ();
sb->s_root->d_inode = click_inode(sb, INO_GLOBALDIR);
UNLOCK_CONFIG_READ();
iput(old_inode);
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_op = &click_superblock_ops;
} else
printk("<1>silly click_reread_super\n");
unlock_super(sb);
}
#ifdef LINUX_2_4
static int
click_delete_dentry(struct dentry *)
{
return 1;
}
#else
static void
click_delete_dentry(struct dentry *dentry)
{
d_drop(dentry);
}
#endif
} // extern "C"
/*************************** Handler operations ******************************/
struct HandlerStringInfo {
int next;
int flags;
};
static String *handler_strings = 0;
static HandlerStringInfo *handler_strings_info = 0;
static int handler_strings_cap = 0;
static int handler_strings_free = -1;
static spinlock_t handler_strings_lock;
#define FILP_STRINGNO(filp) (reinterpret_cast<intptr_t>((filp)->private_data))
#define FILP_READ_STRINGNO(filp) FILP_STRINGNO(filp)
#define FILP_WRITE_STRINGNO(filp) FILP_STRINGNO(filp)
static int
increase_handler_strings()
{
// must be called with handler_strings_lock held
if (handler_strings_cap < 0) // in process of cleaning up module
return -1;
int new_cap = (handler_strings_cap ? 2*handler_strings_cap : 16);
String *new_strs = new String[new_cap];
if (!new_strs)
return -1;
HandlerStringInfo *new_infos = new HandlerStringInfo[new_cap];
if (!new_infos) {
delete[] new_strs;
return -1;
}
for (int i = 0; i < handler_strings_cap; i++)
new_strs[i] = handler_strings[i];
for (int i = handler_strings_cap; i < new_cap; i++)
new_infos[i].next = i + 1;
new_infos[new_cap - 1].next = handler_strings_free;
memcpy(new_infos, handler_strings_info, sizeof(HandlerStringInfo) * handler_strings_cap);
delete[] handler_strings;
delete[] handler_strings_info;
handler_strings_free = handler_strings_cap;
handler_strings_cap = new_cap;
handler_strings = new_strs;
handler_strings_info = new_infos;
return 0;
}
static int
next_handler_string(const Handler *h)
{
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
if (handler_strings_free < 0)
increase_handler_strings();
int hs = handler_strings_free;
if (hs >= 0) {
handler_strings_free = handler_strings_info[hs].next;
handler_strings_info[hs].flags = h->flags() | HANDLER_NEED_READ;
}
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
return hs;
}
static void
free_handler_string(int hs)
{
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
if (hs >= 0 && hs < handler_strings_cap) {
handler_strings[hs] = String();
handler_strings_info[hs].next = handler_strings_free;
handler_strings_free = hs;
}
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
}
static void
lock_threads()
{
for (int i = 0; i < click_master->nthreads(); i++)
while (!click_master->thread(i)->attempt_lock_tasks())
schedule();
click_master->acquire_lock();
}
static void
unlock_threads()
{
click_master->release_lock();
for (int i = click_master->nthreads() - 1; i >= 0; i--)
click_master->thread(i)->unlock_tasks();
}
extern "C" {
static int
handler_open(struct inode *inode, struct file *filp)
{
LOCK_CONFIG_READ();
bool reading = (filp->f_flags & O_ACCMODE) != O_WRONLY;
bool writing = (filp->f_flags & O_ACCMODE) != O_RDONLY;
int retval = 0;
int stringno = -1;
const Handler *h;
if ((reading && writing)
|| (filp->f_flags & O_APPEND)
|| (writing && !(filp->f_flags & O_TRUNC)))
retval = -EACCES;
else if (inode_out_of_date(inode))
retval = -EIO;
else if (!(h = Router::handler(click_router, INO_HANDLERNO(inode->i_ino))))
retval = -EIO;
else if ((reading && !h->read_visible())
|| (writing && !h->write_visible()))
retval = -EPERM;
else if ((stringno = next_handler_string(h)) < 0)
retval = -ENOMEM;
else {
handler_strings[stringno] = String();
retval = 0;
}
UNLOCK_CONFIG_READ();
if (retval < 0 && stringno >= 0) {
free_handler_string(stringno);
stringno = -1;
}
filp->private_data = reinterpret_cast<void *>(stringno);
return retval;
}
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;
int stringno = FILP_READ_STRINGNO(filp);
if (stringno < 0 || stringno >= handler_strings_cap)
return -EIO;
// (re)read handler if necessary
if (handler_strings_info[stringno].flags & (HANDLER_REREAD | HANDLER_NEED_READ)) {
LOCK_CONFIG_READ();
int retval;
const Handler *h;
struct inode *inode = filp->f_dentry->d_inode;
if (inode_out_of_date(inode)
|| !(h = Router::handler(click_router, INO_HANDLERNO(inode->i_ino))))
retval = -EIO;
else if (!h->read_visible())
retval = -EPERM;
else {
int eindex = INO_ELEMENTNO(inode->i_ino);
Element *e = Router::element(click_router, eindex);
if (h->exclusive()) {
lock_threads();
handler_strings[stringno] = h->call_read(e);
unlock_threads();
} else
handler_strings[stringno] = h->call_read(e);
if (!h->raw() && handler_strings[stringno] && handler_strings[stringno].back() != '\n')
handler_strings[stringno] += '\n';
retval = (handler_strings[stringno].out_of_memory() ? -ENOMEM : 0);
}
UNLOCK_CONFIG_READ();
if (retval < 0)
return retval;
handler_strings_info[stringno].flags &= ~HANDLER_NEED_READ;
}
const String &s = handler_strings[stringno];
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 += count;
return count;
}
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;
int stringno = FILP_WRITE_STRINGNO(filp);
if (stringno < 0 || stringno >= handler_strings_cap)
return -EIO;
String &s = handler_strings[stringno];
int old_length = s.length();
#ifdef LARGEST_HANDLER_WRITE
if (f_pos + count > LARGEST_HANDLER_WRITE
&& !(handler_strings_info[stringno].flags & HANDLER_WRITE_UNLIMITED))
return -EFBIG;
#endif
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 += count;
return count;
}
static int
handler_flush(struct file *filp)
{
bool writing = (filp->f_flags & O_ACCMODE) != O_RDONLY;
int stringno = FILP_WRITE_STRINGNO(filp);
int retval = 0;
#ifdef LINUX_2_2
int f_count = filp->f_count;
#else
int f_count = atomic_read(&filp->f_count);
#endif
if (writing && f_count == 1
&& stringno >= 0 && stringno < handler_strings_cap) {
LOCK_CONFIG_WRITE();
struct inode *inode = filp->f_dentry->d_inode;
const Handler *h;
if (inode_out_of_date(inode)
|| !(h = Router::handler(click_router, INO_HANDLERNO(inode->i_ino)))
|| !h->write_visible())
retval = -EIO;
else if (handler_strings[stringno].out_of_memory())
retval = -ENOMEM;
else {
int eindex = INO_ELEMENTNO(inode->i_ino);
Element *e = Router::element(click_router, eindex);
String context_string = "In write handler '" + h->name() + "'";
if (e)
context_string += String(" for '") + e->declaration() + "'";
ContextErrorHandler cerrh(click_logged_errh, context_string + ":");
if (h->exclusive()) {
lock_threads();
retval = h->call_write(handler_strings[stringno], e, true, &cerrh);
unlock_threads();
} else
retval = h->call_write(handler_strings[stringno], e, true, &cerrh);
}
UNLOCK_CONFIG_WRITE();
}
return retval;
}
static int
handler_release(struct inode *, struct file *filp)
{
// free handler string
int stringno = FILP_READ_STRINGNO(filp);
if (stringno >= 0)
free_handler_string(stringno);
return 0;
}
static int
handler_ioctl(struct inode *inode, struct file *filp,
unsigned command, unsigned long address)
{
if (command & _CLICK_IOC_SAFE)
LOCK_CONFIG_READ();
else
LOCK_CONFIG_WRITE();
int retval;
Element *e;
if (inode_out_of_date(inode))
retval = -EIO;
else if (!click_router)
retval = -EINVAL;
else if (INO_ELEMENTNO(inode->i_ino) < 0
|| !(e = click_router->element(INO_ELEMENTNO(inode->i_ino))))
retval = -EIO;
else {
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
arg_ptr = (size && (command & (_CLICK_IOC_IN | _CLICK_IOC_OUT)) ? data : address_ptr);
if (click_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:
if (command & _CLICK_IOC_SAFE)
UNLOCK_CONFIG_READ();
else
UNLOCK_CONFIG_WRITE();
return retval;
}
#ifdef LINUX_2_2
static int
proc_click_readlink_proc(proc_dir_entry *, char *page)
{
strcpy(page, "/click");
return 6;
}
#endif
#if INO_DEBUG
static String
read_ino_info(Element *, void *)
{
return click_ino.info();
}
#endif
} // extern "C"
/*********************** Initialization and termination **********************/
int
init_clickfs()
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
static_assert(sizeof(((struct inode *)0)->u) >= sizeof(ClickInodeInfo));
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
clickfs = proclikefs_register_filesystem("click", 0, click_get_sb);
#else
// NB: remove FS_SINGLE if it will ever make sense to have different
// Click superblocks -- if we introduce mount options, for example
clickfs = proclikefs_register_filesystem("click", FS_SINGLE, click_read_super);
#endif
if (!clickfs
|| !(click_dir_file_ops = proclikefs_new_file_operations(clickfs))
|| !(click_dir_inode_ops = proclikefs_new_inode_operations(clickfs))
|| !(click_handler_file_ops = proclikefs_new_file_operations(clickfs))
|| !(click_handler_inode_ops = proclikefs_new_inode_operations(clickfs))) {
printk("<1>click: could not initialize clickfs!\n");
return -EINVAL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) \
&& LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
click_superblock_ops.put_inode = force_delete;
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
click_superblock_ops.write_inode = click_write_inode;
click_superblock_ops.put_inode = click_put_inode;
#endif
click_superblock_ops.put_super = proclikefs_put_super;
// XXX statfs
click_dentry_ops.d_delete = click_delete_dentry;
#ifdef LINUX_2_4
click_dir_file_ops->read = generic_read_dir;
#endif
click_dir_file_ops->readdir = click_dir_readdir;
click_dir_inode_ops->lookup = click_dir_lookup;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
click_dir_inode_ops->revalidate = click_dir_revalidate;
#endif
#ifdef LINUX_2_2
click_dir_inode_ops->default_file_ops = click_dir_file_ops;
#endif
click_handler_file_ops->read = handler_read;
click_handler_file_ops->write = handler_write;
click_handler_file_ops->ioctl = handler_ioctl;
click_handler_file_ops->open = handler_open;
click_handler_file_ops->flush = handler_flush;
click_handler_file_ops->release = handler_release;
#ifdef LINUX_2_2
click_handler_inode_ops->default_file_ops = click_handler_file_ops;
#endif
spin_lock_init(&handler_strings_lock);
spin_lock_init(&clickfs_write_lock);
atomic_set(&clickfs_read_count, 0);
click_ino.initialize();
proclikefs_reinitialize_supers(clickfs, click_reread_super);
clickfs_ready = 1;
// initialize a symlink from /proc/click -> /click, to ease transition
#ifdef LINUX_2_4
(void) proc_symlink("click", 0, "/click");
#elif defined(LINUX_2_2)
if (proc_dir_entry *link = create_proc_entry("click", S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 0))
link->readlink_proc = proc_click_readlink_proc;
#endif
#if INO_DEBUG
Router::add_read_handler(0, "ino_info", read_ino_info, 0);
#endif
return 0;
}
void
cleanup_clickfs()
{
MDEBUG("cleanup_clickfs");
#if defined(LINUX_2_4) || defined(LINUX_2_2)
// remove the '/proc/click' directory
remove_proc_entry("click", 0);
#endif
// kill filesystem
MDEBUG("proclikefs_unregister_filesystem");
clickfs_ready = 0;
proclikefs_unregister_filesystem(clickfs);
// clean up handler_strings
MDEBUG("cleaning up handler strings");
SPIN_LOCK(&handler_strings_lock, __FILE__, __LINE__);
delete[] handler_strings;
delete[] handler_strings_info;
handler_strings = 0;
handler_strings_info = 0;
handler_strings_cap = -1;
handler_strings_free = -1;
SPIN_UNLOCK(&handler_strings_lock, __FILE__, __LINE__);
MDEBUG("click_ino cleanup");
click_ino.cleanup();
}
syntax highlighted by Code2HTML, v. 0.9.1