mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
01c4ac1315
o Created dmfs.h as a private header for the filesystem code o Using seq_file.[ch] written by Al Viro as a generic mechanism for /proc style files which have one record per line. We use a slight modification here, so if you are using a recent -ac kernel you'll need to replace the existing seq_file.[ch] with the ones here and do a bit of editing to make it work. I'll submit the changes to Al Viro shortly as they are very small and I think make sense generally. o Using fail_writepage() o Init code for filesystem now all in dmfs-super.c o Some common code reduction amoung the dmfs-*.c files o Auto allocation of major device number (default). You can specify a particular major by using a module argument. If built in then you don't get this option at the moment but it could be added if required. o Hotplug support o General tidying o Updated projects.txt file o Patches updated to 2.4.14
354 lines
7.7 KiB
C
354 lines
7.7 KiB
C
/*
|
|
* dmfs-table.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.
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include "dm.h"
|
|
#include "dmfs.h"
|
|
|
|
static offset_t start_of_next_range(struct dm_table *t)
|
|
{
|
|
offset_t n = 0;
|
|
if (t->num_targets) {
|
|
n = t->highs[t->num_targets - 1] + 1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static char *dmfs_parse_line(struct dm_table *t, char *str)
|
|
{
|
|
offset_t start, size, high;
|
|
void *context;
|
|
struct target_type *ttype;
|
|
int rv = 0;
|
|
char *msg;
|
|
int pos = 0;
|
|
char target[33];
|
|
|
|
static char *err_table[] = {
|
|
"Missing/Invalid start argument",
|
|
"Missing/Invalid size argument",
|
|
"Missing target type"
|
|
};
|
|
printk("dmfs_parse_line: (%s)\n", str);
|
|
|
|
rv = sscanf(str, "%d %d %32s%n", &start, &size, target, &pos);
|
|
if (rv < 3) {
|
|
msg = err_table[rv];
|
|
goto out;
|
|
}
|
|
str += pos;
|
|
while(*str && isspace(*str))
|
|
str++;
|
|
|
|
msg = "Gap in table";
|
|
if (start != start_of_next_range(t))
|
|
goto out;
|
|
|
|
msg = "Target type unknown";
|
|
ttype = dm_get_target_type(target);
|
|
if (ttype) {
|
|
msg = "This message should never appear (constructor error)";
|
|
rv = ttype->ctr(t, start, size, str, &context);
|
|
msg = context;
|
|
if (rv == 0) {
|
|
printk("dmfs_parse: %u %u %s %s\n", start, size,
|
|
ttype->name,
|
|
ttype->print ? ttype->print(context) : "-");
|
|
msg = "Error adding target to table";
|
|
high = start + (size - 1);
|
|
if (dm_table_add_target(t, high, ttype, context) == 0)
|
|
return NULL;
|
|
ttype->dtr(t, context);
|
|
}
|
|
dm_put_target_type(ttype);
|
|
}
|
|
out:
|
|
return msg;
|
|
}
|
|
|
|
|
|
static int dmfs_copy(char *dst, int dstlen, char *src, int srclen, int *flag)
|
|
{
|
|
int len = min(dstlen, srclen);
|
|
char *start = dst;
|
|
|
|
while(len) {
|
|
*dst = *src++;
|
|
if (*dst == '\n')
|
|
goto end_of_line;
|
|
dst++;
|
|
len--;
|
|
}
|
|
out:
|
|
return (dst - start);
|
|
end_of_line:
|
|
dst++;
|
|
*flag = 1;
|
|
goto out;
|
|
}
|
|
|
|
static int dmfs_line_is_not_comment(char *str)
|
|
{
|
|
while(*str) {
|
|
if (*str == '#')
|
|
break;
|
|
if (!isspace(*str))
|
|
return 1;
|
|
str++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct dmfs_desc {
|
|
struct dm_table *table;
|
|
struct inode *inode;
|
|
char *tmp;
|
|
loff_t tmpl;
|
|
unsigned long lnum;
|
|
};
|
|
|
|
static int dmfs_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset, unsigned long size)
|
|
{
|
|
char *buf, *msg;
|
|
unsigned long count = desc->count, len, copied;
|
|
struct dmfs_desc *d = (struct dmfs_desc *)desc->buf;
|
|
|
|
if (size > count)
|
|
size = count;
|
|
|
|
len = size;
|
|
buf = kmap(page);
|
|
do {
|
|
int flag = 0;
|
|
copied = dmfs_copy(d->tmp + d->tmpl, PAGE_SIZE - d->tmpl - 1,
|
|
buf + offset, len, &flag);
|
|
offset += copied;
|
|
len -= copied;
|
|
if (d->tmpl + copied == PAGE_SIZE - 1)
|
|
goto line_too_long;
|
|
d->tmpl += copied;
|
|
if (flag || (len == 0 && count == size)) {
|
|
*(d->tmp + d->tmpl) = 0;
|
|
if (dmfs_line_is_not_comment(d->tmp)) {
|
|
msg = dmfs_parse_line(d->table, d->tmp);
|
|
if (msg) {
|
|
dmfs_add_error(d->inode, d->lnum, msg);
|
|
}
|
|
}
|
|
d->lnum++;
|
|
d->tmpl = 0;
|
|
}
|
|
} while(len > 0);
|
|
kunmap(page);
|
|
|
|
desc->count = count - size;
|
|
desc->written += size;
|
|
|
|
return size;
|
|
|
|
line_too_long:
|
|
printk(KERN_INFO "dmfs_read_actor: Line %lu too long\n", d->lnum);
|
|
kunmap(page);
|
|
return 0;
|
|
}
|
|
|
|
static struct dm_table *dmfs_parse(struct inode *inode, struct file *filp)
|
|
{
|
|
struct dm_table *t = NULL;
|
|
unsigned long page;
|
|
struct dmfs_desc d;
|
|
loff_t pos = 0;
|
|
|
|
if (inode->i_size == 0)
|
|
return NULL;
|
|
|
|
page = __get_free_page(GFP_NOFS);
|
|
if (page) {
|
|
t = dm_table_create();
|
|
if (t) {
|
|
read_descriptor_t desc;
|
|
|
|
desc.written = 0;
|
|
desc.count = inode->i_size;
|
|
desc.buf = (char *)&d;
|
|
d.table = t;
|
|
d.inode = inode;
|
|
d.tmp = (char *)page;
|
|
d.tmpl = 0;
|
|
d.lnum = 1;
|
|
|
|
do_generic_file_read(filp, &pos, &desc, dmfs_read_actor);
|
|
if (desc.written != inode->i_size) {
|
|
dm_table_destroy(t);
|
|
t = NULL;
|
|
}
|
|
}
|
|
free_page(page);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static int dmfs_table_release(struct inode *inode, struct file *f)
|
|
{
|
|
struct dentry *dentry = f->f_dentry;
|
|
struct inode *parent = dentry->d_parent->d_inode;
|
|
struct dmfs_i *dmi = DMFS_I(parent);
|
|
struct dm_table *table;
|
|
|
|
if (f->f_mode & FMODE_WRITE) {
|
|
|
|
down(&dmi->sem);
|
|
dmfs_zap_errors(dentry->d_parent->d_inode);
|
|
table = dmfs_parse(dentry->d_parent->d_inode, f);
|
|
|
|
if (table) {
|
|
if (dmi->table)
|
|
dm_table_destroy(dmi->table);
|
|
dmi->table = table;
|
|
}
|
|
up(&dmi->sem);
|
|
|
|
put_write_access(parent);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_readpage(struct file *file, struct page *page)
|
|
{
|
|
if (!Page_Uptodate(page)) {
|
|
memset(kmap(page), 0, PAGE_CACHE_SIZE);
|
|
kunmap(page);
|
|
flush_dcache_page(page);
|
|
SetPageUptodate(page);
|
|
}
|
|
UnlockPage(page);
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_prepare_write(struct file *file, struct page *page,
|
|
unsigned offset, unsigned to)
|
|
{
|
|
void *addr = kmap(page);
|
|
if (!Page_Uptodate(page)) {
|
|
memset(addr, 0, PAGE_CACHE_SIZE);
|
|
flush_dcache_page(page);
|
|
SetPageUptodate(page);
|
|
}
|
|
SetPageDirty(page);
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_commit_write(struct file *file, struct page *page,
|
|
unsigned offset, unsigned to)
|
|
{
|
|
struct inode *inode = page->mapping->host;
|
|
loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
|
|
|
|
kunmap(page);
|
|
if (pos > inode->i_size)
|
|
inode->i_size = pos;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* There is a small race here in that two processes might call this at
|
|
* the same time and both fail. So its a fail safe race :-) This should
|
|
* move into namei.c (and thus use the spinlock and do this properly)
|
|
* at some stage if we continue to use this set of functions for ensuring
|
|
* exclusive write access to the file
|
|
*/
|
|
static int get_exclusive_write_access(struct inode *inode)
|
|
{
|
|
if (get_write_access(inode))
|
|
return -1;
|
|
if (atomic_read(&inode->i_writecount) != 1) {
|
|
put_write_access(inode);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_table_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct dentry *dentry = file->f_dentry;
|
|
struct inode *parent = dentry->d_parent->d_inode;
|
|
|
|
if (file->f_mode & FMODE_WRITE) {
|
|
if (get_exclusive_write_access(parent))
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_table_sync(struct file *file, struct dentry *dentry, int datasync)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int dmfs_table_revalidate(struct dentry *dentry)
|
|
{
|
|
struct inode *inode = dentry->d_inode;
|
|
struct inode *parent = dentry->d_parent->d_inode;
|
|
|
|
inode->i_size = parent->i_size;
|
|
return 0;
|
|
}
|
|
|
|
struct address_space_operations dmfs_address_space_operations = {
|
|
readpage: dmfs_readpage,
|
|
writepage: fail_writepage,
|
|
prepare_write: dmfs_prepare_write,
|
|
commit_write: dmfs_commit_write,
|
|
};
|
|
|
|
static struct file_operations dmfs_table_file_operations = {
|
|
llseek: generic_file_llseek,
|
|
read: generic_file_read,
|
|
write: generic_file_write,
|
|
open: dmfs_table_open,
|
|
release: dmfs_table_release,
|
|
fsync: dmfs_table_sync,
|
|
};
|
|
|
|
static struct inode_operations dmfs_table_inode_operations = {
|
|
revalidate: dmfs_table_revalidate,
|
|
};
|
|
|
|
struct inode *dmfs_create_table(struct inode *dir, int mode)
|
|
{
|
|
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
|
|
|
|
if (inode) {
|
|
inode->i_mapping = dir->i_mapping;
|
|
inode->i_mapping->a_ops = &dmfs_address_space_operations;
|
|
inode->i_fop = &dmfs_table_file_operations;
|
|
inode->i_op = &dmfs_table_inode_operations;
|
|
}
|
|
|
|
return inode;
|
|
}
|
|
|