summaryrefslogtreecommitdiffstats
path: root/fs/adfs/super.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-19 20:33:22 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-19 20:33:22 +0200
commitd2fbf4b6d585e40f2369675148777abce3abd0e7 (patch)
tree0749aa1da31af21d752585dc7a2f31715d434ee3 /fs/adfs/super.c
parentMerge branch 'work.mount0' of git://git.kernel.org/pub/scm/linux/kernel/git/v... (diff)
parentfs/adfs: add time stamp and file type helpers (diff)
downloadlinux-d2fbf4b6d585e40f2369675148777abce3abd0e7.tar.xz
linux-d2fbf4b6d585e40f2369675148777abce3abd0e7.zip
Merge branch 'work.adfs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull adfs updates from Al Viro: "More ADFS patches from Russell King" * 'work.adfs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fs/adfs: add time stamp and file type helpers fs/adfs: super: limit idlen according to directory type fs/adfs: super: fix use-after-free bug fs/adfs: super: safely update options on remount fs/adfs: super: correct superblock flags fs/adfs: clean up indirect disc addresses and fragment IDs fs/adfs: clean up error message printing fs/adfs: use %pV for error messages fs/adfs: use format_version from disc_record fs/adfs: add helper to get filesystem size fs/adfs: add helper to get discrecord from map fs/adfs: correct disc record structure
Diffstat (limited to 'fs/adfs/super.c')
-rw-r--r--fs/adfs/super.c121
1 files changed, 71 insertions, 50 deletions
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index ffb669f9bba7..65b04ebb51c3 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -6,7 +6,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/buffer_head.h>
#include <linux/parser.h>
#include <linux/mount.h>
#include <linux/seq_file.h>
@@ -17,25 +16,42 @@
#include "dir_f.h"
#include "dir_fplus.h"
+#define ADFS_SB_FLAGS SB_NOATIME
+
#define ADFS_DEFAULT_OWNER_MASK S_IRWXU
#define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO)
void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
{
- char error_buf[128];
+ struct va_format vaf;
va_list args;
va_start(args, fmt);
- vsnprintf(error_buf, sizeof(error_buf), fmt, args);
- va_end(args);
+ vaf.fmt = fmt;
+ vaf.va = &args;
- printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
+ printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %pV\n",
sb->s_id, function ? ": " : "",
- function ? function : "", error_buf);
+ function ? function : "", &vaf);
+
+ va_end(args);
+}
+
+void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ printk("%sADFS-fs (%s): %pV\n", pfx, sb->s_id, &vaf);
+ va_end(args);
}
static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
{
+ unsigned int max_idlen;
int i;
/* sector size must be 256, 512 or 1024 bytes */
@@ -55,8 +71,13 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize)
return 1;
- /* idlen must be no greater than 19 v2 [1.0] */
- if (dr->idlen > 19)
+ /*
+ * Maximum idlen is limited to 16 bits for new directories by
+ * the three-byte storage of an indirect disc address. For
+ * big directories, idlen must be no greater than 19 v2 [1.0]
+ */
+ max_idlen = dr->format_version ? 19 : 16;
+ if (dr->idlen > max_idlen)
return 1;
/* reserved bytes should be zero */
@@ -152,10 +173,10 @@ static const match_table_t tokens = {
{Opt_err, NULL}
};
-static int parse_options(struct super_block *sb, char *options)
+static int parse_options(struct super_block *sb, struct adfs_sb_info *asb,
+ char *options)
{
char *p;
- struct adfs_sb_info *asb = ADFS_SB(sb);
int option;
if (!options)
@@ -199,8 +220,9 @@ static int parse_options(struct super_block *sb, char *options)
asb->s_ftsuffix = option;
break;
default:
- printk("ADFS-fs: unrecognised mount option \"%s\" "
- "or missing value\n", p);
+ adfs_msg(sb, KERN_ERR,
+ "unrecognised mount option \"%s\" or missing value",
+ p);
return -EINVAL;
}
}
@@ -209,21 +231,31 @@ static int parse_options(struct super_block *sb, char *options)
static int adfs_remount(struct super_block *sb, int *flags, char *data)
{
+ struct adfs_sb_info temp_asb;
+ int ret;
+
sync_filesystem(sb);
- *flags |= SB_NODIRATIME;
- return parse_options(sb, data);
+ *flags |= ADFS_SB_FLAGS;
+
+ temp_asb = *ADFS_SB(sb);
+ ret = parse_options(sb, &temp_asb, data);
+ if (ret == 0)
+ *ADFS_SB(sb) = temp_asb;
+
+ return ret;
}
static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *sb = dentry->d_sb;
struct adfs_sb_info *sbi = ADFS_SB(sb);
+ struct adfs_discrecord *dr = adfs_map_discrecord(sbi->s_map);
u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
buf->f_type = ADFS_SUPER_MAGIC;
buf->f_namelen = sbi->s_namelen;
buf->f_bsize = sb->s_blocksize;
- buf->f_blocks = sbi->s_size;
+ buf->f_blocks = adfs_disc_size(dr) >> sb->s_blocksize_bits;
buf->f_files = sbi->s_ids_per_zone * sbi->s_map_size;
buf->f_bavail =
buf->f_bfree = adfs_map_free(sb);
@@ -327,8 +359,7 @@ static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_di
i = zone - 1;
dm[0].dm_startblk = 0;
dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
- dm[i].dm_endbit = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) +
- (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) +
+ dm[i].dm_endbit = (adfs_disc_size(dr) >> dr->log2bpmb) +
(ADFS_DR_SIZE_BITS - i * zone_size);
if (adfs_checkmap(sb, dm))
@@ -344,27 +375,18 @@ error_free:
return ERR_PTR(-EIO);
}
-static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
-{
- unsigned long discsize;
-
- discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
- discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
-
- return discsize;
-}
-
static int adfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct adfs_discrecord *dr;
struct buffer_head *bh;
struct object_info root_obj;
unsigned char *b_data;
+ unsigned int blocksize;
struct adfs_sb_info *asb;
struct inode *root;
int ret = -EINVAL;
- sb->s_flags |= SB_NODIRATIME;
+ sb->s_flags |= ADFS_SB_FLAGS;
asb = kzalloc(sizeof(*asb), GFP_KERNEL);
if (!asb)
@@ -378,12 +400,12 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK;
asb->s_ftsuffix = 0;
- if (parse_options(sb, data))
+ if (parse_options(sb, asb, data))
goto error;
sb_set_blocksize(sb, BLOCK_SIZE);
if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
- adfs_error(sb, "unable to read superblock");
+ adfs_msg(sb, KERN_ERR, "error: unable to read superblock");
ret = -EIO;
goto error;
}
@@ -391,11 +413,8 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
if (adfs_checkbblk(b_data)) {
- if (!silent)
- printk("VFS: Can't find an adfs filesystem on dev "
- "%s.\n", sb->s_id);
ret = -EINVAL;
- goto error_free_bh;
+ goto error_badfs;
}
dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
@@ -404,33 +423,33 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
* Do some sanity checks on the ADFS disc record
*/
if (adfs_checkdiscrecord(dr)) {
- if (!silent)
- printk("VPS: Can't find an adfs filesystem on dev "
- "%s.\n", sb->s_id);
ret = -EINVAL;
- goto error_free_bh;
+ goto error_badfs;
}
+ blocksize = 1 << dr->log2secsize;
brelse(bh);
- if (sb_set_blocksize(sb, 1 << dr->log2secsize)) {
+
+ if (sb_set_blocksize(sb, blocksize)) {
bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
if (!bh) {
- adfs_error(sb, "couldn't read superblock on "
- "2nd try.");
+ adfs_msg(sb, KERN_ERR,
+ "error: couldn't read superblock on 2nd try.");
ret = -EIO;
goto error;
}
b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
if (adfs_checkbblk(b_data)) {
- adfs_error(sb, "disc record mismatch, very weird!");
+ adfs_msg(sb, KERN_ERR,
+ "error: disc record mismatch, very weird!");
ret = -EINVAL;
goto error_free_bh;
}
dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
} else {
if (!silent)
- printk(KERN_ERR "VFS: Unsupported blocksize on dev "
- "%s.\n", sb->s_id);
+ adfs_msg(sb, KERN_ERR,
+ "error: unsupported blocksize");
ret = -EINVAL;
goto error;
}
@@ -443,8 +462,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
asb->s_idlen = dr->idlen;
asb->s_map_size = dr->nzones | (dr->nzones_high << 8);
asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
- asb->s_size = adfs_discsize(dr, sb->s_blocksize_bits);
- asb->s_version = dr->format_version;
asb->s_log2sharesize = dr->log2sharesize;
asb->s_map = adfs_read_map(sb, dr);
@@ -460,9 +477,9 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
*/
sb->s_op = &adfs_sops;
- dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4);
+ dr = adfs_map_discrecord(asb->s_map);
- root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
+ root_obj.parent_id = root_obj.indaddr = le32_to_cpu(dr->root);
root_obj.name_len = 0;
/* Set root object date as 01 Jan 1987 00:00:00 */
root_obj.loadaddr = 0xfff0003f;
@@ -470,13 +487,12 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
root_obj.size = ADFS_NEWDIR_SIZE;
root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ |
ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
- root_obj.filetype = -1;
/*
* If this is a F+ disk with variable length directories,
* get the root_size from the disc record.
*/
- if (asb->s_version) {
+ if (dr->format_version) {
root_obj.size = le32_to_cpu(dr->root_size);
asb->s_dir = &adfs_fplus_dir_ops;
asb->s_namelen = ADFS_FPLUS_NAME_LEN;
@@ -505,6 +521,11 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
}
return 0;
+error_badfs:
+ if (!silent)
+ adfs_msg(sb, KERN_ERR,
+ "error: can't find an ADFS filesystem on dev %s.",
+ sb->s_id);
error_free_bh:
brelse(bh);
error: