From 95fbadbb5566e383f0cfe40d895e698ab38bdbc7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:15 +0000 Subject: fs/adfs: dir: add common dir object initialisation Initialise the dir object before we pass it down to the directory format specific read handler. This allows us to get rid of the initialisation inside those handlers. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir_f.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index c1a950c7400a..e62f35eb7789 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -139,9 +139,6 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, size >>= blocksize_bits; - dir->nr_buffers = 0; - dir->sb = sb; - for (blk = 0; blk < size; blk++) { int phys; -- cgit From 1dd9f5babfd95fea5a77b27bab48c04c29db1f5f Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:20 +0000 Subject: fs/adfs: dir: add common directory buffer release method With the bhs pointer in place, we have no need for separate per-format free() methods, since a generic version will do. Provide a generic implementation, remove the format specific implementations and the method function pointer. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 2 +- fs/adfs/dir.c | 21 ++++++++++++++++++--- fs/adfs/dir_f.c | 28 ++++------------------------ fs/adfs/dir_fplus.c | 34 ++-------------------------------- 4 files changed, 25 insertions(+), 60 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 956ac0bd53e1..3bb6fd5b5eb0 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -126,7 +126,6 @@ struct adfs_dir_ops { int (*create)(struct adfs_dir *dir, struct object_info *obj); int (*remove)(struct adfs_dir *dir, struct object_info *obj); int (*sync)(struct adfs_dir *dir); - void (*free)(struct adfs_dir *dir); }; struct adfs_discmap { @@ -167,6 +166,7 @@ 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; +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, int wait); diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index c1b8b5bccbec..f50302775504 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -6,6 +6,7 @@ * * Common directory handling for ADFS */ +#include #include "adfs.h" /* @@ -13,6 +14,20 @@ */ static DEFINE_RWLOCK(adfs_dir_lock); +void adfs_dir_relse(struct adfs_dir *dir) +{ + unsigned int i; + + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bhs[i]); + dir->nr_buffers = 0; + + if (dir->bhs != dir->bh) + kfree(dir->bhs); + dir->bhs = NULL; + dir->sb = NULL; +} + static int adfs_dir_read(struct super_block *sb, u32 indaddr, unsigned int size, struct adfs_dir *dir) { @@ -105,7 +120,7 @@ unlock_out: read_unlock(&adfs_dir_lock); free_out: - ops->free(&dir); + adfs_dir_relse(&dir); return ret; } @@ -139,7 +154,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) ret = err; } - ops->free(&dir); + adfs_dir_relse(&dir); out: #endif return ret; @@ -211,7 +226,7 @@ unlock_out: read_unlock(&adfs_dir_lock); free_out: - ops->free(&dir); + adfs_dir_relse(&dir); out: return ret; } diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index e62f35eb7789..e249fdb915fa 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -9,8 +9,6 @@ #include "adfs.h" #include "dir_f.h" -static void adfs_f_free(struct adfs_dir *dir); - /* * Read an (unaligned) value of length 1..4 bytes */ @@ -128,7 +126,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, unsigned int size, struct adfs_dir *dir) { const unsigned int blocksize_bits = sb->s_blocksize_bits; - int blk = 0; + int blk; /* * Directories which are not a multiple of 2048 bytes @@ -152,6 +150,8 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, dir->bh[blk] = sb_bread(sb, phys); if (!dir->bh[blk]) goto release_buffers; + + dir->nr_buffers += 1; } memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); @@ -168,17 +168,12 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) goto bad_dir; - dir->nr_buffers = blk; - return 0; bad_dir: adfs_error(sb, "dir %06x is corrupted", indaddr); release_buffers: - for (blk -= 1; blk >= 0; blk -= 1) - brelse(dir->bh[blk]); - - dir->sb = NULL; + adfs_dir_relse(dir); return -EIO; } @@ -435,25 +430,10 @@ adfs_f_sync(struct adfs_dir *dir) return err; } -static void -adfs_f_free(struct adfs_dir *dir) -{ - int i; - - for (i = dir->nr_buffers - 1; i >= 0; i--) { - brelse(dir->bh[i]); - dir->bh[i] = NULL; - } - - dir->nr_buffers = 0; - dir->sb = NULL; -} - const struct adfs_dir_ops adfs_f_dir_ops = { .read = adfs_f_read, .setpos = adfs_f_setpos, .getnext = adfs_f_getnext, .update = adfs_f_update, .sync = adfs_f_sync, - .free = adfs_f_free }; diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 52c42a9986d9..25308b334dd3 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -15,7 +15,7 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct struct adfs_bigdirtail *t; unsigned long block; unsigned int blk, size; - int i, ret = -EIO; + int ret = -EIO; block = __adfs_block_map(sb, id, 0); if (!block) { @@ -92,18 +92,8 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct return 0; out: - if (dir->bhs) { - for (i = 0; i < dir->nr_buffers; i++) - brelse(dir->bhs[i]); + adfs_dir_relse(dir); - if (&dir->bh[0] != dir->bhs) - kfree(dir->bhs); - - dir->bhs = NULL; - } - - dir->nr_buffers = 0; - dir->sb = NULL; return ret; } @@ -205,29 +195,9 @@ adfs_fplus_sync(struct adfs_dir *dir) return err; } -static void -adfs_fplus_free(struct adfs_dir *dir) -{ - int i; - - if (dir->bhs) { - for (i = 0; i < dir->nr_buffers; i++) - brelse(dir->bhs[i]); - - if (&dir->bh[0] != dir->bhs) - kfree(dir->bhs); - - dir->bhs = NULL; - } - - dir->nr_buffers = 0; - dir->sb = NULL; -} - const struct adfs_dir_ops adfs_fplus_dir_ops = { .read = adfs_fplus_read, .setpos = adfs_fplus_setpos, .getnext = adfs_fplus_getnext, .sync = adfs_fplus_sync, - .free = adfs_fplus_free }; -- cgit From acf5f0be8a520c02bfed74cfc6735bf5fdd4a9e5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:25 +0000 Subject: fs/adfs: dir: add common directory sync method adfs_fplus_sync() can be used for both directory formats since we now have a common way to access the buffer heads, so move it into dir.c and appropriately rename it. Remove the directory-format specific implementations. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 1 - fs/adfs/dir.c | 23 ++++++++++++++++++----- fs/adfs/dir_f.c | 17 ----------------- fs/adfs/dir_fplus.c | 17 ----------------- 4 files changed, 18 insertions(+), 40 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 3bb6fd5b5eb0..5f1acee768f5 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -125,7 +125,6 @@ struct adfs_dir_ops { int (*update)(struct adfs_dir *dir, struct object_info *obj); int (*create)(struct adfs_dir *dir, struct object_info *obj); int (*remove)(struct adfs_dir *dir, struct object_info *obj); - int (*sync)(struct adfs_dir *dir); }; struct adfs_discmap { diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index f50302775504..16a2639d3ca5 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -38,6 +38,21 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir); } +static int adfs_dir_sync(struct adfs_dir *dir) +{ + int err = 0; + int i; + + for (i = dir->nr_buffers - 1; i >= 0; i--) { + struct buffer_head *bh = dir->bhs[i]; + sync_dirty_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + err = -EIO; + } + + return err; +} + void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj) { unsigned int dots, i; @@ -135,10 +150,8 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n", obj->indaddr, obj->parent_id); - if (!ops->update) { - ret = -EINVAL; - goto out; - } + if (!ops->update) + return -EINVAL; ret = adfs_dir_read(sb, obj->parent_id, 0, &dir); if (ret) @@ -149,7 +162,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) write_unlock(&adfs_dir_lock); if (wait) { - int err = ops->sync(&dir); + int err = adfs_dir_sync(&dir); if (!ret) ret = err; } diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index e249fdb915fa..80ac261b9ec4 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -414,26 +414,9 @@ bad_dir: #endif } -static int -adfs_f_sync(struct adfs_dir *dir) -{ - int err = 0; - int i; - - for (i = dir->nr_buffers - 1; i >= 0; i--) { - struct buffer_head *bh = dir->bh[i]; - sync_dirty_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) - err = -EIO; - } - - return err; -} - const struct adfs_dir_ops adfs_f_dir_ops = { .read = adfs_f_read, .setpos = adfs_f_setpos, .getnext = adfs_f_getnext, .update = adfs_f_update, - .sync = adfs_f_sync, }; diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 25308b334dd3..1196c8962feb 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -179,25 +179,8 @@ out: return ret; } -static int -adfs_fplus_sync(struct adfs_dir *dir) -{ - int err = 0; - int i; - - for (i = dir->nr_buffers - 1; i >= 0; i--) { - struct buffer_head *bh = dir->bhs[i]; - sync_dirty_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) - err = -EIO; - } - - return err; -} - const struct adfs_dir_ops adfs_fplus_dir_ops = { .read = adfs_fplus_read, .setpos = adfs_fplus_setpos, .getnext = adfs_fplus_getnext, - .sync = adfs_fplus_sync, }; -- cgit From a317120bf7f8306b594ee650ee14f08a0e599602 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:30 +0000 Subject: 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 Signed-off-by: Al Viro --- fs/adfs/adfs.h | 4 ++++ fs/adfs/dir.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ fs/adfs/dir_f.c | 56 +++++++++-------------------------------------------- fs/adfs/dir_fplus.c | 47 ++++++++++++-------------------------------- 4 files changed, 75 insertions(+), 82 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 5f1acee768f5..92cbc4b1d902 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -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, diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 16a2639d3ca5..3c303074aa5e 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -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; diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 80ac261b9ec4..3c3b423577d2 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -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; - - 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; + int ret; - 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; - - 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; + int ret; - /* - * 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); + ret = adfs_dir_copyfrom(&de, dir, pos, 26); + if (ret) + return ret; - /* - * 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); } /* diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 1196c8962feb..6a07c0dfcc93 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -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 = { -- cgit From 419a6e5e82ca0bdba0cc3624d969b65ae49d959b Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:35 +0000 Subject: fs/adfs: dir: add generic directory reading Both directory formats code the mechanics of fetching the directory buffers using their own implementations. Consolidate these into one implementation. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 2 ++ fs/adfs/dir.c | 49 ++++++++++++++++++++++++++++++++++ fs/adfs/dir_f.c | 24 +++-------------- fs/adfs/dir_fplus.c | 76 ++++++++++++++--------------------------------------- 4 files changed, 74 insertions(+), 77 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 92cbc4b1d902..01d065937c01 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -170,6 +170,8 @@ int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset, 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); +int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr, + unsigned int size, 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, int wait); diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 3c303074aa5e..b8e2a909fa3f 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -78,6 +78,55 @@ void adfs_dir_relse(struct adfs_dir *dir) dir->sb = NULL; } +int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr, + unsigned int size, struct adfs_dir *dir) +{ + struct buffer_head **bhs; + unsigned int i, num; + int block; + + num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits; + if (num > ARRAY_SIZE(dir->bh)) { + /* We only allow one extension */ + if (dir->bhs != dir->bh) + return -EINVAL; + + bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL); + if (!bhs) + return -ENOMEM; + + if (dir->nr_buffers) + memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs)); + + dir->bhs = bhs; + } + + for (i = dir->nr_buffers; i < num; i++) { + block = __adfs_block_map(sb, indaddr, i); + if (!block) { + adfs_error(sb, "dir %06x has a hole at offset %u", + indaddr, i); + goto error; + } + + dir->bhs[i] = sb_bread(sb, block); + if (!dir->bhs[i]) { + adfs_error(sb, + "dir %06x failed read at offset %u, mapped block 0x%08x", + indaddr, i, block); + goto error; + } + + dir->nr_buffers++; + } + return 0; + +error: + adfs_dir_relse(dir); + + return -EIO; +} + static int adfs_dir_read(struct super_block *sb, u32 indaddr, unsigned int size, struct adfs_dir *dir) { diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 3c3b423577d2..027ee714f42b 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -126,7 +126,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, unsigned int size, struct adfs_dir *dir) { const unsigned int blocksize_bits = sb->s_blocksize_bits; - int blk; + int ret; /* * Directories which are not a multiple of 2048 bytes @@ -135,24 +135,9 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, if (size & 2047) goto bad_dir; - size >>= blocksize_bits; - - for (blk = 0; blk < size; blk++) { - int phys; - - phys = __adfs_block_map(sb, indaddr, blk); - if (!phys) { - adfs_error(sb, "dir %06x has a hole at offset %d", - indaddr, blk); - goto release_buffers; - } - - dir->bh[blk] = sb_bread(sb, phys); - if (!dir->bh[blk]) - goto release_buffers; - - dir->nr_buffers += 1; - } + ret = adfs_dir_read_buffers(sb, indaddr, size, dir); + if (ret) + return ret; memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); @@ -172,7 +157,6 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, bad_dir: adfs_error(sb, "dir %06x is corrupted", indaddr); -release_buffers: adfs_dir_relse(dir); return -EIO; diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 6a07c0dfcc93..ae11236515d0 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -4,87 +4,49 @@ * * Copyright (C) 1997-1999 Russell King */ -#include #include "adfs.h" #include "dir_fplus.h" -static int -adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) +static int adfs_fplus_read(struct super_block *sb, u32 indaddr, + unsigned int size, struct adfs_dir *dir) { struct adfs_bigdirheader *h; struct adfs_bigdirtail *t; - unsigned long block; - unsigned int blk, size; - int ret = -EIO; - - block = __adfs_block_map(sb, id, 0); - if (!block) { - adfs_error(sb, "dir object %X has a hole at offset 0", id); - goto out; - } + unsigned int dirsize; + int ret; - dir->bhs[0] = sb_bread(sb, block); - if (!dir->bhs[0]) - goto out; - dir->nr_buffers += 1; + /* Read first buffer */ + ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir); + if (ret) + return ret; h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data; - size = le32_to_cpu(h->bigdirsize); - if (size != sz) { + dirsize = le32_to_cpu(h->bigdirsize); + if (dirsize != size) { adfs_msg(sb, KERN_WARNING, - "directory header size %X does not match directory size %X", - size, sz); + "dir %06x header size %X does not match directory size %X", + indaddr, dirsize, size); } if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || h->bigdirversion[2] != 0 || size & 2047 || h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) { - adfs_error(sb, "dir %06x has malformed header", id); + adfs_error(sb, "dir %06x has malformed header", indaddr); goto out; } - size >>= sb->s_blocksize_bits; - if (size > ARRAY_SIZE(dir->bh)) { - /* this directory is too big for fixed bh set, must allocate */ - struct buffer_head **bhs = - kcalloc(size, sizeof(struct buffer_head *), - GFP_KERNEL); - if (!bhs) { - adfs_msg(sb, KERN_ERR, - "not enough memory for dir object %X (%d blocks)", - id, size); - ret = -ENOMEM; - goto out; - } - dir->bhs = bhs; - /* copy over the pointer to the block that we've already read */ - dir->bhs[0] = dir->bh[0]; - } - - for (blk = 1; blk < size; blk++) { - block = __adfs_block_map(sb, id, blk); - if (!block) { - adfs_error(sb, "dir object %X has a hole at offset %d", id, blk); - goto out; - } - - dir->bhs[blk] = sb_bread(sb, block); - if (!dir->bhs[blk]) { - adfs_error(sb, "dir object %x failed read for offset %d, mapped block %lX", - id, blk, block); - goto out; - } - - dir->nr_buffers += 1; - } + /* Read remaining buffers */ + ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir); + if (ret) + return ret; t = (struct adfs_bigdirtail *) - (dir->bhs[size - 1]->b_data + (sb->s_blocksize - 8)); + (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8)); if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || t->bigdirendmasseq != h->startmasseq || t->reserved[0] != 0 || t->reserved[1] != 0) { - adfs_error(sb, "dir %06x has malformed tail", id); + adfs_error(sb, "dir %06x has malformed tail", indaddr); goto out; } -- cgit From c3c8149b3552b6656ded9ac86d53072f74771ba7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:09:45 +0000 Subject: fs/adfs: dir: add helper to mark directory buffers dirty Provide a helper for marking directory buffers dirty so they get written back to disk. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir.c | 12 ++++++++++++ fs/adfs/dir_f.c | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 882377e86041..e8aafc65d545 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -157,6 +157,15 @@ static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode, return ret; } +static void adfs_dir_mark_dirty(struct adfs_dir *dir) +{ + unsigned int i; + + /* Mark the buffers dirty */ + for (i = 0; i < dir->nr_buffers; i++) + mark_buffer_dirty(dir->bhs[i]); +} + static int adfs_dir_sync(struct adfs_dir *dir) { int err = 0; @@ -280,6 +289,9 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) ret = ops->update(&dir, obj); write_unlock(&adfs_dir_lock); + if (ret == 0) + adfs_dir_mark_dirty(&dir); + if (wait) { int err = adfs_dir_sync(&dir); if (!ret) diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 027ee714f42b..682df46d8d33 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -306,7 +306,7 @@ static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) { struct super_block *sb = dir->sb; - int ret, i; + int ret; ret = adfs_dir_find_entry(dir, obj->indaddr); if (ret < 0) { @@ -347,9 +347,6 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj) goto bad_dir; } #endif - for (i = dir->nr_buffers - 1; i >= 0; i--) - mark_buffer_dirty(dir->bh[i]); - ret = 0; out: return ret; -- cgit From 4287e4deb1280633ffbda608f946d6d7c2d76d4a Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:16 +0000 Subject: fs/adfs: dir: add more efficient iterate() per-format method Rather than using setpos + getnext to iterate through the directory entries, pass iterate() down to the dir format code to populate the dirents. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 1 + fs/adfs/dir.c | 16 ++-------------- fs/adfs/dir_f.c | 18 ++++++++++++++++++ fs/adfs/dir_fplus.c | 21 +++++++++++++++++++++ 4 files changed, 42 insertions(+), 14 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 01d065937c01..cbf33f375e0b 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -120,6 +120,7 @@ struct object_info { struct adfs_dir_ops { int (*read)(struct super_block *sb, unsigned int indaddr, unsigned int size, struct adfs_dir *dir); + int (*iterate)(struct adfs_dir *dir, struct dir_context *ctx); int (*setpos)(struct adfs_dir *dir, unsigned int fpos); int (*getnext)(struct adfs_dir *dir, struct object_info *obj); int (*update)(struct adfs_dir *dir, struct object_info *obj); diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 2a8f5f1fd3d0..7fda44464121 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -240,12 +240,8 @@ static int adfs_iterate(struct file *file, struct dir_context *ctx) struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; - struct object_info obj; struct adfs_dir dir; - int ret = 0; - - if (ctx->pos >> 32) - return 0; + int ret; down_read(&adfs_dir_rwsem); ret = adfs_dir_read_inode(sb, inode, &dir); @@ -263,15 +259,7 @@ static int adfs_iterate(struct file *file, struct dir_context *ctx) ctx->pos = 2; } - ret = ops->setpos(&dir, ctx->pos - 2); - if (ret) - goto unlock_relse; - while (ops->getnext(&dir, &obj) == 0) { - if (!dir_emit(ctx, obj.name, obj.name_len, - obj.indaddr, DT_UNKNOWN)) - break; - ctx->pos++; - } + ret = ops->iterate(&dir, ctx); unlock_relse: up_read(&adfs_dir_rwsem); diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 682df46d8d33..2e342871d6df 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -302,6 +302,23 @@ adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj) return ret; } +static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx) +{ + struct object_info obj; + int pos = 5 + (ctx->pos - 2) * 26; + + while (ctx->pos < 2 + ADFS_NUM_DIR_ENTRIES) { + if (__adfs_dir_get(dir, pos, &obj)) + break; + if (!dir_emit(ctx, obj.name, obj.name_len, + obj.indaddr, DT_UNKNOWN)) + break; + pos += 26; + ctx->pos++; + } + return 0; +} + static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) { @@ -359,6 +376,7 @@ bad_dir: const struct adfs_dir_ops adfs_f_dir_ops = { .read = adfs_f_read, + .iterate = adfs_f_iterate, .setpos = adfs_f_setpos, .getnext = adfs_f_getnext, .update = adfs_f_update, diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index ae11236515d0..edcbaa94ecb9 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -118,8 +118,29 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) return 0; } +static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx) +{ + struct object_info obj; + + if ((ctx->pos - 2) >> 32) + return 0; + + if (adfs_fplus_setpos(dir, ctx->pos - 2)) + return 0; + + while (!adfs_fplus_getnext(dir, &obj)) { + if (!dir_emit(ctx, obj.name, obj.name_len, + obj.indaddr, DT_UNKNOWN)) + break; + ctx->pos++; + } + + return 0; +} + const struct adfs_dir_ops adfs_fplus_dir_ops = { .read = adfs_fplus_read, + .iterate = adfs_fplus_iterate, .setpos = adfs_fplus_setpos, .getnext = adfs_fplus_getnext, }; -- cgit From 016936b32131d0b33328d8c109f83fabb56618a3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:21 +0000 Subject: fs/adfs: dir: use pointers to access directory head/tails Add and use pointers in the adfs_dir structure to access the directory head and tail structures, which will always be contiguous in a buffer. This allows us to avoid memcpy()ing the data in the new directory code, making it slightly more efficient. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 12 ++++++++---- fs/adfs/dir_f.c | 42 +++++++++++++++++------------------------- fs/adfs/dir_fplus.c | 11 ++++------- 3 files changed, 29 insertions(+), 36 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index cbf33f375e0b..1f431a42e14c 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -26,8 +26,6 @@ static inline u16 adfs_filetype(u32 loadaddr) #define ADFS_NDA_PUBLIC_READ (1 << 5) #define ADFS_NDA_PUBLIC_WRITE (1 << 6) -#include "dir_f.h" - /* * adfs file system inode data in memory */ @@ -98,8 +96,14 @@ struct adfs_dir { unsigned int pos; __u32 parent_id; - struct adfs_dirheader dirhead; - union adfs_dirtail dirtail; + union { + struct adfs_dirheader *dirhead; + struct adfs_bigdirheader *bighead; + }; + union { + struct adfs_newdirtail *newtail; + struct adfs_bigdirtail *bigtail; + }; }; /* diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 2e342871d6df..7e56fcc21303 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -58,7 +58,7 @@ static inline void adfs_writeval(unsigned char *p, int len, unsigned int val) #define bufoff(_bh,_idx) \ ({ int _buf = _idx >> blocksize_bits; \ int _off = _idx - (_buf << blocksize_bits);\ - (u8 *)(_bh[_buf]->b_data + _off); \ + (void *)(_bh[_buf]->b_data + _off); \ }) /* @@ -139,18 +139,18 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, if (ret) return ret; - memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); - memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); + dir->dirhead = bufoff(dir->bh, 0); + dir->newtail = bufoff(dir->bh, 2007); - if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || - memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + if (dir->dirhead->startmasseq != dir->newtail->endmasseq || + memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4)) goto bad_dir; - if (memcmp(&dir->dirhead.startname, "Nick", 4) && - memcmp(&dir->dirhead.startname, "Hugo", 4)) + if (memcmp(&dir->dirhead->startname, "Nick", 4) && + memcmp(&dir->dirhead->startname, "Hugo", 4)) goto bad_dir; - if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte) goto bad_dir; return 0; @@ -275,7 +275,7 @@ static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size, if (ret) adfs_error(sb, "unable to read directory"); else - dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3); + dir->parent_id = adfs_readval(dir->newtail->dirparent, 3); return ret; } @@ -322,7 +322,6 @@ static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx) static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) { - struct super_block *sb = dir->sb; int ret; ret = adfs_dir_find_entry(dir, obj->indaddr); @@ -336,33 +335,26 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj) /* * Increment directory sequence number */ - dir->bh[0]->b_data[0] += 1; - dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1; + dir->dirhead->startmasseq += 1; + dir->newtail->endmasseq += 1; ret = adfs_dir_checkbyte(dir); /* * Update directory check byte */ - dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret; + dir->newtail->dircheckbyte = ret; #if 1 - { - const unsigned int blocksize_bits = sb->s_blocksize_bits; - - memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); - memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); - - if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || - memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + if (dir->dirhead->startmasseq != dir->newtail->endmasseq || + memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4)) goto bad_dir; - if (memcmp(&dir->dirhead.startname, "Nick", 4) && - memcmp(&dir->dirhead.startname, "Hugo", 4)) + if (memcmp(&dir->dirhead->startname, "Nick", 4) && + memcmp(&dir->dirhead->startname, "Hugo", 4)) goto bad_dir; - if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte) goto bad_dir; - } #endif ret = 0; out: diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index edcbaa94ecb9..6f2dbcf6819b 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -20,7 +20,7 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr, if (ret) return ret; - h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data; + dir->bighead = h = (void *)dir->bhs[0]->b_data; dirsize = le32_to_cpu(h->bigdirsize); if (dirsize != size) { adfs_msg(sb, KERN_WARNING, @@ -40,7 +40,7 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr, if (ret) return ret; - t = (struct adfs_bigdirtail *) + dir->bigtail = t = (struct adfs_bigdirtail *) (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8)); if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || @@ -62,11 +62,9 @@ out: static int adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) { - struct adfs_bigdirheader *h = - (struct adfs_bigdirheader *) dir->bhs[0]->b_data; int ret = -ENOENT; - if (fpos <= le32_to_cpu(h->bigdirentries)) { + if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) { dir->pos = fpos; ret = 0; } @@ -77,8 +75,7 @@ adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) static int adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) { - struct adfs_bigdirheader *h = - (struct adfs_bigdirheader *) dir->bhs[0]->b_data; + struct adfs_bigdirheader *h = dir->bighead; struct adfs_bigdirentry bde; unsigned int offset; int ret; -- cgit From ffc8df347e4934c8bad776f7bdacb4842620b0c7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:27 +0000 Subject: fs/adfs: newdir: factor out directory format validation We have two locations where we validate the new directory format, so factor this out to a helper. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir_f.c | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 7e56fcc21303..196706d581bf 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -121,6 +121,21 @@ adfs_dir_checkbyte(const struct adfs_dir *dir) return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff; } +static int adfs_f_validate(struct adfs_dir *dir) +{ + struct adfs_dirheader *head = dir->dirhead; + struct adfs_newdirtail *tail = dir->newtail; + + if (head->startmasseq != tail->endmasseq || + (memcmp(&head->startname, "Nick", 4) && + memcmp(&head->startname, "Hugo", 4)) || + memcmp(&head->startname, &tail->endname, 4) || + adfs_dir_checkbyte(dir) != tail->dircheckbyte) + return -EIO; + + return 0; +} + /* Read and check that a directory is valid */ static int adfs_dir_read(struct super_block *sb, u32 indaddr, unsigned int size, struct adfs_dir *dir) @@ -142,15 +157,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, dir->dirhead = bufoff(dir->bh, 0); dir->newtail = bufoff(dir->bh, 2007); - if (dir->dirhead->startmasseq != dir->newtail->endmasseq || - memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4)) - goto bad_dir; - - if (memcmp(&dir->dirhead->startname, "Nick", 4) && - memcmp(&dir->dirhead->startname, "Hugo", 4)) - goto bad_dir; - - if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte) + if (adfs_f_validate(dir)) goto bad_dir; return 0; @@ -327,7 +334,7 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj) ret = adfs_dir_find_entry(dir, obj->indaddr); if (ret < 0) { adfs_error(dir->sb, "unable to locate entry to update"); - goto out; + return ret; } __adfs_dir_put(dir, ret, obj); @@ -344,26 +351,11 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj) */ dir->newtail->dircheckbyte = ret; -#if 1 - if (dir->dirhead->startmasseq != dir->newtail->endmasseq || - memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4)) - goto bad_dir; - - if (memcmp(&dir->dirhead->startname, "Nick", 4) && - memcmp(&dir->dirhead->startname, "Hugo", 4)) - goto bad_dir; + ret = adfs_f_validate(dir); + if (ret) + adfs_error(dir->sb, "whoops! I broke a directory!"); - if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte) - goto bad_dir; -#endif - ret = 0; -out: return ret; -#if 1 -bad_dir: - adfs_error(dir->sb, "whoops! I broke a directory!"); - return -EIO; -#endif } const struct adfs_dir_ops adfs_f_dir_ops = { -- cgit From 7a0e4048bfd16848ac115b17f49a3df7993a2fac Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:32 +0000 Subject: fs/adfs: newdir: improve directory validation Check that the lastmask and reserved fields are all zero, as per the documentation. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir_f.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 196706d581bf..ebe8616ee533 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -127,6 +127,7 @@ static int adfs_f_validate(struct adfs_dir *dir) struct adfs_newdirtail *tail = dir->newtail; if (head->startmasseq != tail->endmasseq || + tail->dirlastmask || tail->reserved[0] || tail->reserved[1] || (memcmp(&head->startname, "Nick", 4) && memcmp(&head->startname, "Hugo", 4)) || memcmp(&head->startname, &tail->endname, 4) || -- cgit From 9318731bec8d38bdbe701d395cf103157046831d Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:37 +0000 Subject: fs/adfs: newdir: merge adfs_dir_read() into adfs_f_read() adfs_dir_read() is only called from adfs_f_read(), so merge it into that function. As new directories are always 2048 bytes in size, (which we rely on elsewhere) we can consolidate some of the code. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir_f.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index ebe8616ee533..dbb4f1ef7bb7 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -138,20 +138,16 @@ static int adfs_f_validate(struct adfs_dir *dir) } /* Read and check that a directory is valid */ -static int adfs_dir_read(struct super_block *sb, u32 indaddr, - unsigned int size, struct adfs_dir *dir) +static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size, + struct adfs_dir *dir) { const unsigned int blocksize_bits = sb->s_blocksize_bits; int ret; - /* - * Directories which are not a multiple of 2048 bytes - * are considered bad v2 [3.6] - */ - if (size & 2047) - goto bad_dir; + if (size && size != ADFS_NEWDIR_SIZE) + return -EIO; - ret = adfs_dir_read_buffers(sb, indaddr, size, dir); + ret = adfs_dir_read_buffers(sb, indaddr, ADFS_NEWDIR_SIZE, dir); if (ret) return ret; @@ -161,6 +157,8 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr, if (adfs_f_validate(dir)) goto bad_dir; + dir->parent_id = adfs_readval(dir->newtail->dirparent, 3); + return 0; bad_dir: @@ -271,23 +269,6 @@ static int adfs_dir_find_entry(struct adfs_dir *dir, u32 indaddr) return ret; } -static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size, - struct adfs_dir *dir) -{ - int ret; - - if (size != ADFS_NEWDIR_SIZE) - return -EIO; - - ret = adfs_dir_read(sb, indaddr, size, dir); - if (ret) - adfs_error(sb, "unable to read directory"); - else - dir->parent_id = adfs_readval(dir->newtail->dirparent, 3); - - return ret; -} - static int adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) { -- cgit From cc625ccd0e6c2804cd0935743e3b51121a712562 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:42 +0000 Subject: fs/adfs: newdir: clean up adfs_f_update() __adfs_dir_put() and adfs_dir_find_entry() are only called from adfs_f_update(), so move them into this function, removing some unnecessary entry copying by doing so. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/dir_f.c | 73 +++++++++++++++++++-------------------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index dbb4f1ef7bb7..36cfadb2b893 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -229,46 +229,6 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) return 0; } -static int -__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj) -{ - struct adfs_direntry de; - int ret; - - ret = adfs_dir_copyfrom(&de, dir, pos, 26); - if (ret) - return ret; - - adfs_obj2dir(&de, obj); - - return adfs_dir_copyto(dir, pos, &de, 26); -} - -/* - * the caller is responsible for holding the necessary - * locks. - */ -static int adfs_dir_find_entry(struct adfs_dir *dir, u32 indaddr) -{ - int pos, ret; - - ret = -ENOENT; - - for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) { - struct object_info obj; - - if (!__adfs_dir_get(dir, pos, &obj)) - break; - - if (obj.indaddr == indaddr) { - ret = pos; - break; - } - } - - return ret; -} - static int adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) { @@ -308,18 +268,33 @@ static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx) return 0; } -static int -adfs_f_update(struct adfs_dir *dir, struct object_info *obj) +static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) { - int ret; + struct adfs_direntry de; + int offset, ret; - ret = adfs_dir_find_entry(dir, obj->indaddr); - if (ret < 0) { - adfs_error(dir->sb, "unable to locate entry to update"); - return ret; - } + offset = 5 - (int)sizeof(de); + + do { + offset += sizeof(de); + ret = adfs_dir_copyfrom(&de, dir, offset, sizeof(de)); + if (ret) { + adfs_error(dir->sb, "error reading directory entry"); + return -ENOENT; + } + if (!de.dirobname[0]) { + adfs_error(dir->sb, "unable to locate entry to update"); + return -ENOENT; + } + } while (adfs_readval(de.dirinddiscadd, 3) != obj->indaddr); + + /* Update the directory entry with the new object state */ + adfs_obj2dir(&de, obj); - __adfs_dir_put(dir, ret, obj); + /* Write the directory entry back to the directory */ + ret = adfs_dir_copyto(dir, pos, &de, 26); + if (ret) + return ret; /* * Increment directory sequence number -- cgit From aacc954c1be8910a994e09a8f8757a2e3e231c37 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:10:47 +0000 Subject: fs/adfs: newdir: split out directory commit from update After changing a directory, we need to update the sequence numbers and calculate the new check byte before the directory is scheduled to be written back to the media. Since this needs to happen for any change to the directory, move this into a separate method. Signed-off-by: Russell King Signed-off-by: Al Viro --- fs/adfs/adfs.h | 1 + fs/adfs/dir.c | 4 ++++ fs/adfs/dir_f.c | 26 +++++++++++++------------- 3 files changed, 18 insertions(+), 13 deletions(-) (limited to 'fs/adfs/dir_f.c') diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 1f431a42e14c..c05555252fec 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -130,6 +130,7 @@ struct adfs_dir_ops { int (*update)(struct adfs_dir *dir, struct object_info *obj); int (*create)(struct adfs_dir *dir, struct object_info *obj); int (*remove)(struct adfs_dir *dir, struct object_info *obj); + int (*commit)(struct adfs_dir *dir); }; struct adfs_discmap { diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 7fda44464121..3d4bbe836fb5 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -293,6 +293,10 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) goto unlock; ret = ops->update(&dir, obj); + if (ret) + goto forget; + + ret = ops->commit(&dir); if (ret) goto forget; up_write(&adfs_dir_rwsem); diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index 36cfadb2b893..30d526fecc3f 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -292,25 +292,24 @@ static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) adfs_obj2dir(&de, obj); /* Write the directory entry back to the directory */ - ret = adfs_dir_copyto(dir, pos, &de, 26); - if (ret) - return ret; - - /* - * Increment directory sequence number - */ + return adfs_dir_copyto(dir, offset, &de, 26); +} + +static int adfs_f_commit(struct adfs_dir *dir) +{ + int ret; + + /* Increment directory sequence number */ dir->dirhead->startmasseq += 1; dir->newtail->endmasseq += 1; - ret = adfs_dir_checkbyte(dir); - /* - * Update directory check byte - */ - dir->newtail->dircheckbyte = ret; + /* Update directory check byte */ + dir->newtail->dircheckbyte = adfs_dir_checkbyte(dir); + /* Make sure the directory still validates correctly */ ret = adfs_f_validate(dir); if (ret) - adfs_error(dir->sb, "whoops! I broke a directory!"); + adfs_msg(dir->sb, KERN_ERR, "error: update broke directory"); return ret; } @@ -321,4 +320,5 @@ const struct adfs_dir_ops adfs_f_dir_ops = { .setpos = adfs_f_setpos, .getnext = adfs_f_getnext, .update = adfs_f_update, + .commit = adfs_f_commit, }; -- cgit