/* -*- 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 = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL);
if (!sd)
return NULL;
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;
}
/*
*
* Return -EEXIST if there is already a configfs element with the same
* name for the same parent.
*
* called with parent inode's i_mutex held
*/
static int configfs_dirent_exists(struct configfs_dirent *parent_sd,
const unsigned char *new)
{
struct configfs_dirent * sd;
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (sd->s_element) {
const unsigned char *existing = configfs_get_name(sd);
if (strcmp(existing, new))
continue;
else
return -EEXIST;
}
}
return 0;
}
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_op