mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
fs/adfs: dir: add generic copy functions
Directories can span multiple buffers, and we currently open-code memcpy access to these buffers, including dealing with entries that are split across multiple buffers. Such code exists in both directory format implementations. Provide common functions to allow data to be copied from/to the directory buffers as if they were a contiguous set of buffers, and use them when accessing directories. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
acf5f0be8a
commit
a317120bf7
@ -165,6 +165,10 @@ extern const struct dentry_operations adfs_dentry_operations;
|
||||
extern const struct adfs_dir_ops adfs_f_dir_ops;
|
||||
extern const struct adfs_dir_ops adfs_fplus_dir_ops;
|
||||
|
||||
int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
|
||||
size_t len);
|
||||
int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
|
||||
size_t len);
|
||||
void adfs_dir_relse(struct adfs_dir *dir);
|
||||
void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
|
||||
extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
|
||||
|
@ -14,6 +14,56 @@
|
||||
*/
|
||||
static DEFINE_RWLOCK(adfs_dir_lock);
|
||||
|
||||
int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
|
||||
size_t len)
|
||||
{
|
||||
struct super_block *sb = dir->sb;
|
||||
unsigned int index, remain;
|
||||
|
||||
index = offset >> sb->s_blocksize_bits;
|
||||
offset &= sb->s_blocksize - 1;
|
||||
remain = sb->s_blocksize - offset;
|
||||
if (index + (remain < len) >= dir->nr_buffers)
|
||||
return -EINVAL;
|
||||
|
||||
if (remain < len) {
|
||||
memcpy(dst, dir->bhs[index]->b_data + offset, remain);
|
||||
dst += remain;
|
||||
len -= remain;
|
||||
index += 1;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
memcpy(dst, dir->bhs[index]->b_data + offset, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
|
||||
size_t len)
|
||||
{
|
||||
struct super_block *sb = dir->sb;
|
||||
unsigned int index, remain;
|
||||
|
||||
index = offset >> sb->s_blocksize_bits;
|
||||
offset &= sb->s_blocksize - 1;
|
||||
remain = sb->s_blocksize - offset;
|
||||
if (index + (remain < len) >= dir->nr_buffers)
|
||||
return -EINVAL;
|
||||
|
||||
if (remain < len) {
|
||||
memcpy(dir->bhs[index]->b_data + offset, src, remain);
|
||||
src += remain;
|
||||
len -= remain;
|
||||
index += 1;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
memcpy(dir->bhs[index]->b_data + offset, src, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adfs_dir_relse(struct adfs_dir *dir)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -224,24 +224,12 @@ adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
|
||||
static int
|
||||
__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
|
||||
{
|
||||
struct super_block *sb = dir->sb;
|
||||
struct adfs_direntry de;
|
||||
int thissize, buffer, offset;
|
||||
int ret;
|
||||
|
||||
buffer = pos >> sb->s_blocksize_bits;
|
||||
|
||||
if (buffer > dir->nr_buffers)
|
||||
return -EINVAL;
|
||||
|
||||
offset = pos & (sb->s_blocksize - 1);
|
||||
thissize = sb->s_blocksize - offset;
|
||||
if (thissize > 26)
|
||||
thissize = 26;
|
||||
|
||||
memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
|
||||
if (thissize != 26)
|
||||
memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
|
||||
26 - thissize);
|
||||
ret = adfs_dir_copyfrom(&de, dir, pos, 26);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!de.dirobname[0])
|
||||
return -ENOENT;
|
||||
@ -254,42 +242,16 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
|
||||
static int
|
||||
__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
|
||||
{
|
||||
struct super_block *sb = dir->sb;
|
||||
struct adfs_direntry de;
|
||||
int thissize, buffer, offset;
|
||||
int ret;
|
||||
|
||||
buffer = pos >> sb->s_blocksize_bits;
|
||||
ret = adfs_dir_copyfrom(&de, dir, pos, 26);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (buffer > dir->nr_buffers)
|
||||
return -EINVAL;
|
||||
|
||||
offset = pos & (sb->s_blocksize - 1);
|
||||
thissize = sb->s_blocksize - offset;
|
||||
if (thissize > 26)
|
||||
thissize = 26;
|
||||
|
||||
/*
|
||||
* Get the entry in total
|
||||
*/
|
||||
memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
|
||||
if (thissize != 26)
|
||||
memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
|
||||
26 - thissize);
|
||||
|
||||
/*
|
||||
* update it
|
||||
*/
|
||||
adfs_obj2dir(&de, obj);
|
||||
|
||||
/*
|
||||
* Put the new entry back
|
||||
*/
|
||||
memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
|
||||
if (thissize != 26)
|
||||
memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
|
||||
26 - thissize);
|
||||
|
||||
return 0;
|
||||
return adfs_dir_copyto(dir, pos, &de, 26);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -112,34 +112,6 @@ adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
|
||||
{
|
||||
struct super_block *sb = dir->sb;
|
||||
unsigned int buffer, partial, remainder;
|
||||
|
||||
buffer = offset >> sb->s_blocksize_bits;
|
||||
offset &= sb->s_blocksize - 1;
|
||||
|
||||
partial = sb->s_blocksize - offset;
|
||||
|
||||
if (partial >= len)
|
||||
memcpy(to, dir->bhs[buffer]->b_data + offset, len);
|
||||
else {
|
||||
char *c = (char *)to;
|
||||
|
||||
remainder = len - partial;
|
||||
|
||||
memcpy(c,
|
||||
dir->bhs[buffer]->b_data + offset,
|
||||
partial);
|
||||
|
||||
memcpy(c + partial,
|
||||
dir->bhs[buffer + 1]->b_data,
|
||||
remainder);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
|
||||
{
|
||||
@ -147,16 +119,19 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
|
||||
(struct adfs_bigdirheader *) dir->bhs[0]->b_data;
|
||||
struct adfs_bigdirentry bde;
|
||||
unsigned int offset;
|
||||
int ret = -ENOENT;
|
||||
int ret;
|
||||
|
||||
if (dir->pos >= le32_to_cpu(h->bigdirentries))
|
||||
goto out;
|
||||
return -ENOENT;
|
||||
|
||||
offset = offsetof(struct adfs_bigdirheader, bigdirname);
|
||||
offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
|
||||
offset += dir->pos * sizeof(struct adfs_bigdirentry);
|
||||
|
||||
dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
|
||||
ret = adfs_dir_copyfrom(&bde, dir, offset,
|
||||
sizeof(struct adfs_bigdirentry));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
obj->loadaddr = le32_to_cpu(bde.bigdirload);
|
||||
obj->execaddr = le32_to_cpu(bde.bigdirexec);
|
||||
@ -170,13 +145,15 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
|
||||
offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
|
||||
offset += le32_to_cpu(bde.bigdirobnameptr);
|
||||
|
||||
dir_memcpy(dir, offset, obj->name, obj->name_len);
|
||||
ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adfs_object_fixup(dir, obj);
|
||||
|
||||
dir->pos += 1;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct adfs_dir_ops adfs_fplus_dir_ops = {
|
||||
|
Loading…
Reference in New Issue
Block a user