1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-23 21:35:29 +03:00
lvm2/driver/device-mapper/dm-fs.c

348 lines
6.7 KiB
C
Raw Normal View History

/*
* dm.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* procfs and devfs handling for device mapper
*
* Changelog
*
* 16/08/2001 - First version [Joe Thornber]
*/
#include "dm.h"
#include <linux/proc_fs.h>
#include <linux/ctype.h>
/*
* /dev/device-mapper/control is the control char device used to
* create/destroy mapping devices.
*
* When a mapping device called <name> is created it appears as
* /dev/device-mapper/<name>. In addition the interface to control the
* mapping will appear in /proc/device-mapper/<name>.
*/
const char *_fs_dir = "device-mapper";
2001-08-20 17:45:43 +04:00
const char *_control_name = "control";
static struct proc_dir_entry *_proc_dir;
2001-08-20 17:45:43 +04:00
static struct proc_dir_entry *_control;
static devfs_handle_t _dev_dir;
2001-08-20 17:45:43 +04:00
static int _line_splitter(struct file *file, const char *buffer,
unsigned long count, void *data);
int _process_control(const char *b, const char *e, int minor);
static int _process_table(const char *b, const char *e, int minor);
static const char *_eat_space(const char *b, const char *e);
static int _get_word(const char *b, const char *e,
const char **wb, const char **we);
static int _tok_cmp(const char *str, const char *b, const char *e);
static void _tok_cpy(char *dest, size_t max,
const char *b, const char *e);
2001-08-20 17:45:43 +04:00
typedef int (*process_fn)(const char *b, const char *e, int minor);
2001-08-20 17:45:43 +04:00
struct pf_data {
process_fn fn;
2001-08-20 17:45:43 +04:00
int minor;
};
int dm_init_fs()
{
2001-08-20 17:45:43 +04:00
struct pf_data *pfd = kmalloc(sizeof(*pfd), GFP_KERNEL);
2001-08-20 17:45:43 +04:00
if (!pfd)
return 0;
2001-08-20 17:45:43 +04:00
_dev_dir = devfs_mk_dir(0, _fs_dir, NULL);
2001-08-20 17:45:43 +04:00
if (!(_proc_dir = create_proc_entry(_fs_dir, S_IFDIR, &proc_root)))
goto fail;
if (!(_control = create_proc_entry(_control_name, S_IWUSR, _proc_dir)))
2001-08-20 17:45:43 +04:00
goto fail;
2001-08-20 17:45:43 +04:00
_control->write_proc = _line_splitter;
2001-08-20 17:45:43 +04:00
pfd->fn = _process_control;
pfd->minor = -1;
_control->data = pfd;
2001-08-22 19:02:55 +04:00
return 0;
2001-08-20 17:45:43 +04:00
fail:
dm_fin_fs();
2001-08-22 19:02:55 +04:00
return -ENOMEM;
}
2001-08-20 17:45:43 +04:00
void dm_fin_fs(void)
{
2001-08-20 17:45:43 +04:00
if (_control) {
remove_proc_entry(_control_name, _proc_dir);
_control = 0;
}
2001-08-20 17:45:43 +04:00
if (_proc_dir) {
remove_proc_entry(_fs_dir, &proc_root);
_proc_dir = 0;
}
2001-08-20 17:45:43 +04:00
if (_dev_dir)
devfs_unregister(_dev_dir);
}
int dm_fs_add(struct mapped_device *md)
{
struct pf_data *pfd = kmalloc(sizeof(*pfd), GFP_KERNEL);
if (!pfd)
return -ENOMEM;
pfd->fn = _process_table;
pfd->minor = MINOR(md->dev);
if (!(md->pde = create_proc_entry(md->name, S_IRUGO | S_IWUSR,
_proc_dir))) {
kfree(pfd);
return -ENOMEM;
}
md->pde->write_proc = _line_splitter;
md->pde->data = pfd;
md->devfs_entry =
devfs_register(_dev_dir, md->name, DEVFS_FL_CURRENT_OWNER,
MAJOR(md->dev), MINOR(md->dev),
S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
&dm_blk_dops, NULL);
if (!md->devfs_entry) {
kfree(pfd);
remove_proc_entry(md->name, _proc_dir);
md->pde = 0;
return -ENOMEM; /* FIXME: better error ? */
}
return 0;
}
int dm_fs_remove(struct mapped_device *md)
{
if (md->pde) {
kfree(md->pde->data);
remove_proc_entry(md->name, _proc_dir);
md->pde = 0;
}
devfs_unregister(md->devfs_entry);
md->devfs_entry = 0;
return 0;
}
2001-08-20 17:45:43 +04:00
int _process_control(const char *b, const char *e, int minor)
{
2001-08-20 17:45:43 +04:00
const char *wb, *we;
char name[64];
2001-08-20 17:45:43 +04:00
int create = 0;
2001-08-20 17:45:43 +04:00
/*
* create <name> [minor]
* remove <name>
*/
if (!_get_word(b, e, &wb, &we))
return -EINVAL;
b = we;
2001-08-20 17:45:43 +04:00
if (!_tok_cmp("create", wb, we))
create = 1;
2001-08-20 17:45:43 +04:00
else if (_tok_cmp("remove", wb, we))
return -EINVAL;
2001-08-20 17:45:43 +04:00
if (!_get_word(b, e, &wb, &we))
return -EINVAL;
b = we;
_tok_cpy(name, sizeof(name), wb, we);
if (!create)
return dm_remove(name);
else {
2001-08-20 17:45:43 +04:00
if (_get_word(b, e, &wb, &we)) {
minor = simple_strtol(wb, (char **) &we, 10);
2001-08-20 17:45:43 +04:00
if (we == wb)
return -EINVAL;
}
return dm_create(name, minor);
}
2001-08-20 17:45:43 +04:00
return -EINVAL;
}
2001-08-20 17:45:43 +04:00
static int _process_table(const char *b, const char *e, int minor)
{
2001-08-20 17:45:43 +04:00
const char *wb, *we;
struct mapped_device *md = dm_find_minor(minor);
void *context;
int ret;
if (!md)
return -ENXIO;
2001-08-20 17:45:43 +04:00
if (!_get_word(b, e, &wb, &we))
return -EINVAL;
if (!_tok_cmp("begin", b, e)) {
/* suspend the device if it's active */
dm_suspend(md);
2001-08-20 17:45:43 +04:00
/* start loading a table */
dm_start_table(md);
2001-08-20 17:45:43 +04:00
} else if (!_tok_cmp("end", b, e)) {
/* activate the device ... <evil chuckle> ... */
dm_complete_table(md);
dm_activate(md);
2001-08-20 17:45:43 +04:00
} else {
/* add the new entry */
int len = we - wb;
char high_s[64], *ptr;
char target[64];
struct target *t;
offset_t high;
if (len > sizeof(high_s))
return 0;
strncpy(high_s, wb, we - wb);
high_s[len] = '\0';
high = simple_strtol(high_s, &ptr, 10);
2001-08-20 17:45:43 +04:00
if (ptr == high_s)
return 0;
b = we;
if (!_get_word(b, e, &wb, &we))
return 0;
len = we - wb;
if (len > sizeof(target))
return 0;
strncpy(target, wb, len);
target[len] = '\0';
if (!(t = dm_get_target(target)))
return 0;
if ((ret = t->ctr(md->highs[md->num_targets - 1],
high, md, we, &context)))
return ret;
dm_add_entry(md, high, t->map, context);
}
2001-08-20 17:45:43 +04:00
return 1;
}
static const char *_eat_space(const char *b, const char *e)
{
while(b != e && isspace((int) *b))
b++;
return b;
}
2001-08-20 17:45:43 +04:00
static int _get_word(const char *b, const char *e,
const char **wb, const char **we)
2001-08-20 17:45:43 +04:00
{
b = _eat_space(b, e);
if (b == e)
return 0;
*wb = b;
while(b != e && !isspace((int) *b))
2001-08-20 17:45:43 +04:00
b++;
*we = b;
2001-08-20 17:45:43 +04:00
return 1;
}
static int _line_splitter(struct file *file, const char *buffer,
unsigned long count, void *data)
2001-08-20 17:45:43 +04:00
{
int r;
2001-08-20 17:45:43 +04:00
const char *b = buffer, *e = buffer + count, *lb;
struct pf_data *pfd = (struct pf_data *) data;
while(b < e) {
b = _eat_space(b, e);
if (b == e)
2001-08-22 18:30:30 +04:00
break;
2001-08-20 17:45:43 +04:00
lb = b;
while((b != e) && *b != '\n')
b++;
if ((r = pfd->fn(lb, b, pfd->minor)))
return r;
}
2001-08-20 17:45:43 +04:00
return count;
}
static int _tok_cmp(const char *str, const char *b, const char *e)
{
while (*str && b != e) {
if (*str < *b)
return -1;
if (*str > *b)
return 1;
str++, b++;
}
if (!*str && b == e)
return 0;
if (*str)
return 1;
2001-08-20 17:45:43 +04:00
return -1;
}
static void _tok_cpy(char *dest, size_t max,
const char *b, const char *e)
{
size_t len = e - b;
if (len > --max)
len = max;
strncpy(dest, b, len);
2001-08-22 18:30:30 +04:00
dest[len] = '\0';
}