/* -*- mode: c; c-basic-offset: 8; -*-
* vim: noexpandtab sw=8 ts=8 sts=0:
*
* dir.c - Operations for configfs directories.
*
* This program 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; either
* version 2 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*/
#undef DEBUG
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
DECLARE_RWSEM(configfs_rename_sem);
static void configfs_d_iput(struct dentry * dentry,
struct inode * inode)
{
struct configfs_dirent * sd = dentry->d_fsdata;
if (sd) {
BUG_ON(sd->s_dentry != dentry);
sd->s_dentry = NULL;
configfs_put(sd);
}
iput(inode);
}
/*
* We _must_ delete our dentries on last dput, as the chain-to-parent
* behavior is required to clear the parents of default_groups.
*/
static int configfs_d_delete(struct dentry *dentry)
{
return 1;
}
static struct dentry_operations configfs_dentry_ops = {
.d_iput = configfs_d_iput,
/* simple_delete_dentry() isn't exported */
.d_delete = configfs_d_delete,
};
/*
* Allocates a new configfs_dirent and links it to the parent configfs_dirent
*/
static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * parent_sd,
void * element)
{
struct configfs_dirent * sd;
sd = kmalloc(sizeof(*sd), GFP_KERNEL);
if (!sd)
return NULL;
memset(sd, 0, sizeof(*sd));
atomic_set(&sd->s_count, 1);
INIT_LIST_HEAD(&sd->s_links);
INIT_LIST_HEAD(&sd->s_children);
list_add(&sd->s_sibling, &parent_sd->s_children);
sd->s_element = element;
return sd;
}
int configfs_make_dirent(struct configfs_dirent * parent_sd,
struct dentry * dentry, void * element,
umode_t mode, int type)
{
struct configfs_dirent * sd;
sd = configfs_new_dirent(parent_sd, element);
if (!sd)
return -ENOMEM;
sd->s_mode = mode;
sd->s_type = type;
sd->s_dentry = dentry;
if (dentry) {
dentry->d_fsdata = configfs_get(sd);
dentry->d_op = &configfs_dentry_ops;
}
return 0;
}
static int init_dir(struct inode * inode)
{
inode->i_op = &configfs_dir_inode_operations;
inode->i_fop = &configfs_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode->i_nlink++;
return 0;
}
static int init_file(struct inode * inode)
{
inode->i_size = PAGE_SIZE;
inode->i_fop = &configfs_file_operations;
return 0;
}
static int init_symlink(struct inode * inode)
{
inode->i_op = &configfs_symlink_inode_operations;
return 0;
}
static int create_dir(struct config_item * k, struct dentry * p,
struct dentry * d)
{
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
error = configfs_create(d, mode, init_dir);
if (!error) {
error = configfs_make_dirent(p->d_fsdata, d, k, mode,
CONFIGFS_DIR);
if (!