diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:11:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:11:40 +0000 |
commit | 8b0a8165cdad0f4133837d753649ef4682e42c3b (patch) | |
tree | 5c58f869f31ddb1f7bd6e8bdea269b680b36c5b6 /fs/udf | |
parent | Releasing progress-linux version 6.8.12-1~progress7.99u1. (diff) | |
download | linux-8b0a8165cdad0f4133837d753649ef4682e42c3b.tar.xz linux-8b0a8165cdad0f4133837d753649ef4682e42c3b.zip |
Merging upstream version 6.9.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fs/udf')
-rw-r--r-- | fs/udf/dir.c | 2 | ||||
-rw-r--r-- | fs/udf/inode.c | 27 | ||||
-rw-r--r-- | fs/udf/namei.c | 21 | ||||
-rw-r--r-- | fs/udf/super.c | 556 | ||||
-rw-r--r-- | fs/udf/udf_sb.h | 1 | ||||
-rw-r--r-- | fs/udf/udftime.c | 11 |
6 files changed, 328 insertions, 290 deletions
diff --git a/fs/udf/dir.c b/fs/udf/dir.c index f6533f9385..f94f45fe2c 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -67,7 +67,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) pos_valid = true; } - fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); + fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL); if (!fname) { ret = -ENOMEM; goto out; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index d8493449d4..bbf8918417 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -341,7 +341,7 @@ const struct address_space_operations udf_aops = { */ int udf_expand_file_adinicb(struct inode *inode) { - struct page *page; + struct folio *folio; struct udf_inode_info *iinfo = UDF_I(inode); int err; @@ -357,12 +357,13 @@ int udf_expand_file_adinicb(struct inode *inode) return 0; } - page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); - if (!page) - return -ENOMEM; + folio = __filemap_get_folio(inode->i_mapping, 0, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, GFP_KERNEL); + if (IS_ERR(folio)) + return PTR_ERR(folio); - if (!PageUptodate(page)) - udf_adinicb_readpage(page); + if (!folio_test_uptodate(folio)) + udf_adinicb_readpage(&folio->page); down_write(&iinfo->i_data_sem); memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00, iinfo->i_lenAlloc); @@ -371,22 +372,22 @@ int udf_expand_file_adinicb(struct inode *inode) iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; else iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; - set_page_dirty(page); - unlock_page(page); + folio_mark_dirty(folio); + folio_unlock(folio); up_write(&iinfo->i_data_sem); err = filemap_fdatawrite(inode->i_mapping); if (err) { /* Restore everything back so that we don't lose data... */ - lock_page(page); + folio_lock(folio); down_write(&iinfo->i_data_sem); - memcpy_to_page(page, 0, iinfo->i_data + iinfo->i_lenEAttr, - inode->i_size); - unlock_page(page); + memcpy_from_folio(iinfo->i_data + iinfo->i_lenEAttr, + folio, 0, inode->i_size); + folio_unlock(folio); iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; iinfo->i_lenAlloc = inode->i_size; up_write(&iinfo->i_data_sem); } - put_page(page); + folio_put(folio); mark_inode_dirty(inode); return err; diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 1bb6ed9489..1308109fd4 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -59,7 +59,7 @@ static int udf_fiiter_find_entry(struct inode *dir, const struct qstr *child, child->name[0] == '.' && child->name[1] == '.'; int ret; - fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); + fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL); if (!fname) return -ENOMEM; @@ -566,7 +566,7 @@ out: static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *symname) { - struct inode *inode = udf_new_inode(dir, S_IFLNK | 0777); + struct inode *inode; struct pathComponent *pc; const char *compstart; struct extent_position epos = {}; @@ -579,17 +579,20 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir, struct udf_inode_info *iinfo; struct super_block *sb = dir->i_sb; - if (IS_ERR(inode)) - return PTR_ERR(inode); - - iinfo = UDF_I(inode); - down_write(&iinfo->i_data_sem); - name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS); + name = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL); if (!name) { err = -ENOMEM; - goto out_no_entry; + goto out; + } + + inode = udf_new_inode(dir, S_IFLNK | 0777); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; } + iinfo = UDF_I(inode); + down_write(&iinfo->i_data_sem); inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &udf_symlink_inode_operations; inode_nohighmem(inode); diff --git a/fs/udf/super.c b/fs/udf/super.c index 928a04d9d9..2217f7ed7a 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -40,20 +40,20 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/parser.h> #include <linux/stat.h> #include <linux/cdrom.h> #include <linux/nls.h> #include <linux/vfs.h> #include <linux/vmalloc.h> #include <linux/errno.h> -#include <linux/mount.h> #include <linux/seq_file.h> #include <linux/bitmap.h> #include <linux/crc-itu-t.h> #include <linux/log2.h> #include <asm/byteorder.h> #include <linux/iversion.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include "udf_sb.h" #include "udf_i.h" @@ -91,16 +91,20 @@ enum { UDF_MAX_LINKS = 0xffff }; #define UDF_MAX_FILESIZE (1ULL << 42) /* These are the "meat" - everything else is stuffing */ -static int udf_fill_super(struct super_block *, void *, int); +static int udf_fill_super(struct super_block *sb, struct fs_context *fc); static void udf_put_super(struct super_block *); static int udf_sync_fs(struct super_block *, int); -static int udf_remount_fs(struct super_block *, int *, char *); static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); static int udf_statfs(struct dentry *, struct kstatfs *); static int udf_show_options(struct seq_file *, struct dentry *); +static int udf_init_fs_context(struct fs_context *fc); +static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param); +static int udf_reconfigure(struct fs_context *fc); +static void udf_free_fc(struct fs_context *fc); +static const struct fs_parameter_spec udf_param_spec[]; struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) { @@ -119,18 +123,25 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) } /* UDF filesystem type */ -static struct dentry *udf_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int udf_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super); + return get_tree_bdev(fc, udf_fill_super); } +static const struct fs_context_operations udf_context_ops = { + .parse_param = udf_parse_param, + .get_tree = udf_get_tree, + .reconfigure = udf_reconfigure, + .free = udf_free_fc, +}; + static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", - .mount = udf_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, + .init_fs_context = udf_init_fs_context, + .parameters = udf_param_spec, }; MODULE_ALIAS_FS("udf"); @@ -177,7 +188,6 @@ static int __init init_inodecache(void) udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), 0, (SLAB_RECLAIM_ACCOUNT | - SLAB_MEM_SPREAD | SLAB_ACCOUNT), init_once); if (!udf_inode_cachep) @@ -204,12 +214,10 @@ static const struct super_operations udf_sb_ops = { .put_super = udf_put_super, .sync_fs = udf_sync_fs, .statfs = udf_statfs, - .remount_fs = udf_remount_fs, .show_options = udf_show_options, }; struct udf_options { - unsigned char novrs; unsigned int blocksize; unsigned int session; unsigned int lastblock; @@ -223,6 +231,65 @@ struct udf_options { struct nls_table *nls_map; }; +/* + * UDF has historically preserved prior mount options across + * a remount, so copy those here if remounting, otherwise set + * initial mount defaults. + */ +static void udf_init_options(struct fs_context *fc, struct udf_options *uopt) +{ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + struct super_block *sb = fc->root->d_sb; + struct udf_sb_info *sbi = UDF_SB(sb); + + uopt->flags = sbi->s_flags; + uopt->uid = sbi->s_uid; + uopt->gid = sbi->s_gid; + uopt->umask = sbi->s_umask; + uopt->fmode = sbi->s_fmode; + uopt->dmode = sbi->s_dmode; + uopt->nls_map = NULL; + } else { + uopt->flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | + (1 << UDF_FLAG_STRICT); + /* + * By default we'll use overflow[ug]id when UDF + * inode [ug]id == -1 + */ + uopt->uid = make_kuid(current_user_ns(), overflowuid); + uopt->gid = make_kgid(current_user_ns(), overflowgid); + uopt->umask = 0; + uopt->fmode = UDF_INVALID_MODE; + uopt->dmode = UDF_INVALID_MODE; + uopt->nls_map = NULL; + uopt->session = 0xFFFFFFFF; + } +} + +static int udf_init_fs_context(struct fs_context *fc) +{ + struct udf_options *uopt; + + uopt = kzalloc(sizeof(*uopt), GFP_KERNEL); + if (!uopt) + return -ENOMEM; + + udf_init_options(fc, uopt); + + fc->fs_private = uopt; + fc->ops = &udf_context_ops; + + return 0; +} + +static void udf_free_fc(struct fs_context *fc) +{ + struct udf_options *uopt = fc->fs_private; + + unload_nls(uopt->nls_map); + kfree(fc->fs_private); +} + static int __init init_udf_fs(void) { int err; @@ -358,7 +425,7 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root) } /* - * udf_parse_options + * udf_parse_param * * PURPOSE * Parse mount options. @@ -401,12 +468,12 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root) * yield highly unpredictable results. * * PRE-CONDITIONS - * options Pointer to mount options string. - * uopts Pointer to mount options variable. + * fc fs_context with pointer to mount options variable. + * param Pointer to fs_parameter being parsed. * * POST-CONDITIONS - * <return> 1 Mount options parsed okay. - * <return> 0 Error parsing mount options. + * <return> 0 Mount options parsed okay. + * <return> errno Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski @@ -418,229 +485,193 @@ enum { Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad, Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, - Opt_rootdir, Opt_utf8, Opt_iocharset, - Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore, - Opt_fmode, Opt_dmode -}; - -static const match_table_t tokens = { - {Opt_novrs, "novrs"}, - {Opt_nostrict, "nostrict"}, - {Opt_bs, "bs=%u"}, - {Opt_unhide, "unhide"}, - {Opt_undelete, "undelete"}, - {Opt_noadinicb, "noadinicb"}, - {Opt_adinicb, "adinicb"}, - {Opt_shortad, "shortad"}, - {Opt_longad, "longad"}, - {Opt_uforget, "uid=forget"}, - {Opt_uignore, "uid=ignore"}, - {Opt_gforget, "gid=forget"}, - {Opt_gignore, "gid=ignore"}, - {Opt_gid, "gid=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_session, "session=%u"}, - {Opt_lastblock, "lastblock=%u"}, - {Opt_anchor, "anchor=%u"}, - {Opt_volume, "volume=%u"}, - {Opt_partition, "partition=%u"}, - {Opt_fileset, "fileset=%u"}, - {Opt_rootdir, "rootdir=%u"}, - {Opt_utf8, "utf8"}, - {Opt_iocharset, "iocharset=%s"}, - {Opt_fmode, "mode=%o"}, - {Opt_dmode, "dmode=%o"}, - {Opt_err, NULL} + Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_fmode, Opt_dmode }; -static int udf_parse_options(char *options, struct udf_options *uopt, - bool remount) +static const struct fs_parameter_spec udf_param_spec[] = { + fsparam_flag ("novrs", Opt_novrs), + fsparam_flag ("nostrict", Opt_nostrict), + fsparam_u32 ("bs", Opt_bs), + fsparam_flag ("unhide", Opt_unhide), + fsparam_flag ("undelete", Opt_undelete), + fsparam_flag_no ("adinicb", Opt_adinicb), + fsparam_flag ("shortad", Opt_shortad), + fsparam_flag ("longad", Opt_longad), + fsparam_string ("gid", Opt_gid), + fsparam_string ("uid", Opt_uid), + fsparam_u32 ("umask", Opt_umask), + fsparam_u32 ("session", Opt_session), + fsparam_u32 ("lastblock", Opt_lastblock), + fsparam_u32 ("anchor", Opt_anchor), + fsparam_u32 ("volume", Opt_volume), + fsparam_u32 ("partition", Opt_partition), + fsparam_u32 ("fileset", Opt_fileset), + fsparam_u32 ("rootdir", Opt_rootdir), + fsparam_flag ("utf8", Opt_utf8), + fsparam_string ("iocharset", Opt_iocharset), + fsparam_u32 ("mode", Opt_fmode), + fsparam_u32 ("dmode", Opt_dmode), + {} + }; + +static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param) { - char *p; - int option; unsigned int uv; - - uopt->novrs = 0; - uopt->session = 0xFFFFFFFF; - uopt->lastblock = 0; - uopt->anchor = 0; - - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - substring_t args[MAX_OPT_ARGS]; - int token; - unsigned n; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_novrs: - uopt->novrs = 1; - break; - case Opt_bs: - if (match_int(&args[0], &option)) - return 0; - n = option; - if (n != 512 && n != 1024 && n != 2048 && n != 4096) - return 0; - uopt->blocksize = n; - uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); - break; - case Opt_unhide: - uopt->flags |= (1 << UDF_FLAG_UNHIDE); - break; - case Opt_undelete: - uopt->flags |= (1 << UDF_FLAG_UNDELETE); - break; - case Opt_noadinicb: + unsigned int n; + struct udf_options *uopt = fc->fs_private; + struct fs_parse_result result; + int token; + bool remount = (fc->purpose & FS_CONTEXT_FOR_RECONFIGURE); + + token = fs_parse(fc, udf_param_spec, param, &result); + if (token < 0) + return token; + + switch (token) { + case Opt_novrs: + uopt->flags |= (1 << UDF_FLAG_NOVRS); + break; + case Opt_bs: + n = result.uint_32; + if (n != 512 && n != 1024 && n != 2048 && n != 4096) + return -EINVAL; + uopt->blocksize = n; + uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); + break; + case Opt_unhide: + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + break; + case Opt_undelete: + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + break; + case Opt_adinicb: + if (result.negated) uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_adinicb: + else uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_shortad: - uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_longad: - uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_gid: - if (match_uint(args, &uv)) - return 0; - uopt->gid = make_kgid(current_user_ns(), uv); - if (!gid_valid(uopt->gid)) - return 0; + break; + case Opt_shortad: + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_longad: + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_gid: + if (kstrtoint(param->string, 10, &uv) == 0) { + kgid_t gid = make_kgid(current_user_ns(), uv); + if (!gid_valid(gid)) + return -EINVAL; + uopt->gid = gid; uopt->flags |= (1 << UDF_FLAG_GID_SET); - break; - case Opt_uid: - if (match_uint(args, &uv)) - return 0; - uopt->uid = make_kuid(current_user_ns(), uv); - if (!uid_valid(uopt->uid)) - return 0; + } else if (!strcmp(param->string, "forget")) { + uopt->flags |= (1 << UDF_FLAG_GID_FORGET); + } else if (!strcmp(param->string, "ignore")) { + /* this option is superseded by gid=<number> */ + ; + } else { + return -EINVAL; + } + break; + case Opt_uid: + if (kstrtoint(param->string, 10, &uv) == 0) { + kuid_t uid = make_kuid(current_user_ns(), uv); + if (!uid_valid(uid)) + return -EINVAL; + uopt->uid = uid; uopt->flags |= (1 << UDF_FLAG_UID_SET); - break; - case Opt_umask: - if (match_octal(args, &option)) - return 0; - uopt->umask = option; - break; - case Opt_nostrict: - uopt->flags &= ~(1 << UDF_FLAG_STRICT); - break; - case Opt_session: - if (match_int(args, &option)) - return 0; - uopt->session = option; - if (!remount) - uopt->flags |= (1 << UDF_FLAG_SESSION_SET); - break; - case Opt_lastblock: - if (match_int(args, &option)) - return 0; - uopt->lastblock = option; - if (!remount) - uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET); - break; - case Opt_anchor: - if (match_int(args, &option)) - return 0; - uopt->anchor = option; - break; - case Opt_volume: - case Opt_partition: - case Opt_fileset: - case Opt_rootdir: - /* Ignored (never implemented properly) */ - break; - case Opt_utf8: - if (!remount) { - unload_nls(uopt->nls_map); - uopt->nls_map = NULL; - } - break; - case Opt_iocharset: - if (!remount) { - unload_nls(uopt->nls_map); - uopt->nls_map = NULL; - } - /* When nls_map is not loaded then UTF-8 is used */ - if (!remount && strcmp(args[0].from, "utf8") != 0) { - uopt->nls_map = load_nls(args[0].from); - if (!uopt->nls_map) { - pr_err("iocharset %s not found\n", - args[0].from); - return 0; - } - } - break; - case Opt_uforget: + } else if (!strcmp(param->string, "forget")) { uopt->flags |= (1 << UDF_FLAG_UID_FORGET); - break; - case Opt_uignore: - case Opt_gignore: - /* These options are superseeded by uid=<number> */ - break; - case Opt_gforget: - uopt->flags |= (1 << UDF_FLAG_GID_FORGET); - break; - case Opt_fmode: - if (match_octal(args, &option)) - return 0; - uopt->fmode = option & 0777; - break; - case Opt_dmode: - if (match_octal(args, &option)) - return 0; - uopt->dmode = option & 0777; - break; - default: - pr_err("bad mount option \"%s\" or missing value\n", p); - return 0; + } else if (!strcmp(param->string, "ignore")) { + /* this option is superseded by uid=<number> */ + ; + } else { + return -EINVAL; + } + break; + case Opt_umask: + uopt->umask = result.uint_32; + break; + case Opt_nostrict: + uopt->flags &= ~(1 << UDF_FLAG_STRICT); + break; + case Opt_session: + uopt->session = result.uint_32; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_SESSION_SET); + break; + case Opt_lastblock: + uopt->lastblock = result.uint_32; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET); + break; + case Opt_anchor: + uopt->anchor = result.uint_32; + break; + case Opt_volume: + case Opt_partition: + case Opt_fileset: + case Opt_rootdir: + /* Ignored (never implemented properly) */ + break; + case Opt_utf8: + if (!remount) { + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; } + break; + case Opt_iocharset: + if (!remount) { + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; + } + /* When nls_map is not loaded then UTF-8 is used */ + if (!remount && strcmp(param->string, "utf8") != 0) { + uopt->nls_map = load_nls(param->string); + if (!uopt->nls_map) { + errorf(fc, "iocharset %s not found", + param->string); + return -EINVAL;; + } + } + break; + case Opt_fmode: + uopt->fmode = result.uint_32 & 0777; + break; + case Opt_dmode: + uopt->dmode = result.uint_32 & 0777; + break; + default: + return -EINVAL; } - return 1; + return 0; } -static int udf_remount_fs(struct super_block *sb, int *flags, char *options) +static int udf_reconfigure(struct fs_context *fc) { - struct udf_options uopt; + struct udf_options *uopt = fc->fs_private; + struct super_block *sb = fc->root->d_sb; struct udf_sb_info *sbi = UDF_SB(sb); + int readonly = fc->sb_flags & SB_RDONLY; int error = 0; - if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT)) + if (!readonly && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT)) return -EACCES; sync_filesystem(sb); - uopt.flags = sbi->s_flags; - uopt.uid = sbi->s_uid; - uopt.gid = sbi->s_gid; - uopt.umask = sbi->s_umask; - uopt.fmode = sbi->s_fmode; - uopt.dmode = sbi->s_dmode; - uopt.nls_map = NULL; - - if (!udf_parse_options(options, &uopt, true)) - return -EINVAL; - write_lock(&sbi->s_cred_lock); - sbi->s_flags = uopt.flags; - sbi->s_uid = uopt.uid; - sbi->s_gid = uopt.gid; - sbi->s_umask = uopt.umask; - sbi->s_fmode = uopt.fmode; - sbi->s_dmode = uopt.dmode; + sbi->s_flags = uopt->flags; + sbi->s_uid = uopt->uid; + sbi->s_gid = uopt->gid; + sbi->s_umask = uopt->umask; + sbi->s_fmode = uopt->fmode; + sbi->s_dmode = uopt->dmode; write_unlock(&sbi->s_cred_lock); - if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) + if (readonly == sb_rdonly(sb)) goto out_unlock; - if (*flags & SB_RDONLY) + if (readonly) udf_close_lvid(sb); else udf_open_lvid(sb); @@ -864,7 +895,7 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block) int ret; struct timestamp *ts; - outstr = kmalloc(128, GFP_NOFS); + outstr = kmalloc(128, GFP_KERNEL); if (!outstr) return -ENOMEM; @@ -1539,6 +1570,20 @@ out_bh: return ret; } +static bool udf_lvid_valid(struct super_block *sb, + struct logicalVolIntegrityDesc *lvid) +{ + u32 parts, impuselen; + + parts = le32_to_cpu(lvid->numOfPartitions); + impuselen = le32_to_cpu(lvid->lengthOfImpUse); + if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize || + sizeof(struct logicalVolIntegrityDesc) + impuselen + + 2 * parts * sizeof(u32) > sb->s_blocksize) + return false; + return true; +} + /* * Find the prevailing Logical Volume Integrity Descriptor. */ @@ -1549,7 +1594,6 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ struct udf_sb_info *sbi = UDF_SB(sb); struct logicalVolIntegrityDesc *lvid; int indirections = 0; - u32 parts, impuselen; while (++indirections <= UDF_MAX_LVID_NESTING) { final_bh = NULL; @@ -1571,32 +1615,27 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ if (!final_bh) return; - brelse(sbi->s_lvid_bh); - sbi->s_lvid_bh = final_bh; - lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data; + if (udf_lvid_valid(sb, lvid)) { + brelse(sbi->s_lvid_bh); + sbi->s_lvid_bh = final_bh; + } else { + udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), " + "ignoring.\n", + le32_to_cpu(lvid->numOfPartitions), + le32_to_cpu(lvid->lengthOfImpUse)); + } + if (lvid->nextIntegrityExt.extLength == 0) - goto check; + return; loc = leea_to_cpu(lvid->nextIntegrityExt); } udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n", UDF_MAX_LVID_NESTING); -out_err: brelse(sbi->s_lvid_bh); sbi->s_lvid_bh = NULL; - return; -check: - parts = le32_to_cpu(lvid->numOfPartitions); - impuselen = le32_to_cpu(lvid->lengthOfImpUse); - if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize || - sizeof(struct logicalVolIntegrityDesc) + impuselen + - 2 * parts * sizeof(u32) > sb->s_blocksize) { - udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), " - "ignoring.\n", parts, impuselen); - goto out_err; - } } /* @@ -1946,7 +1985,7 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, return -EINVAL; } sbi->s_last_block = uopt->lastblock; - if (!uopt->novrs) { + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_NOVRS)) { /* Check that it is NSR02 compliant */ nsr = udf_check_vsd(sb); if (!nsr) { @@ -2084,23 +2123,15 @@ u64 lvid_get_unique_id(struct super_block *sb) return ret; } -static int udf_fill_super(struct super_block *sb, void *options, int silent) +static int udf_fill_super(struct super_block *sb, struct fs_context *fc) { int ret = -EINVAL; struct inode *inode = NULL; - struct udf_options uopt; + struct udf_options *uopt = fc->fs_private; struct kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; bool lvid_open = false; - - uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); - /* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */ - uopt.uid = make_kuid(current_user_ns(), overflowuid); - uopt.gid = make_kgid(current_user_ns(), overflowgid); - uopt.umask = 0; - uopt.fmode = UDF_INVALID_MODE; - uopt.dmode = UDF_INVALID_MODE; - uopt.nls_map = NULL; + int silent = fc->sb_flags & SB_SILENT; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -2110,25 +2141,23 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) mutex_init(&sbi->s_alloc_mutex); - if (!udf_parse_options((char *)options, &uopt, false)) - goto parse_options_failure; - fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; - sbi->s_flags = uopt.flags; - sbi->s_uid = uopt.uid; - sbi->s_gid = uopt.gid; - sbi->s_umask = uopt.umask; - sbi->s_fmode = uopt.fmode; - sbi->s_dmode = uopt.dmode; - sbi->s_nls_map = uopt.nls_map; + sbi->s_flags = uopt->flags; + sbi->s_uid = uopt->uid; + sbi->s_gid = uopt->gid; + sbi->s_umask = uopt->umask; + sbi->s_fmode = uopt->fmode; + sbi->s_dmode = uopt->dmode; + sbi->s_nls_map = uopt->nls_map; + uopt->nls_map = NULL; rwlock_init(&sbi->s_cred_lock); - if (uopt.session == 0xFFFFFFFF) + if (uopt->session == 0xFFFFFFFF) sbi->s_session = udf_get_last_session(sb); else - sbi->s_session = uopt.session; + sbi->s_session = uopt->session; udf_debug("Multi-session=%d\n", sbi->s_session); @@ -2139,16 +2168,16 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; - if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) { - ret = udf_load_vrs(sb, &uopt, silent, &fileset); + if (uopt->flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) { + ret = udf_load_vrs(sb, uopt, silent, &fileset); } else { - uopt.blocksize = bdev_logical_block_size(sb->s_bdev); - while (uopt.blocksize <= 4096) { - ret = udf_load_vrs(sb, &uopt, silent, &fileset); + uopt->blocksize = bdev_logical_block_size(sb->s_bdev); + while (uopt->blocksize <= 4096) { + ret = udf_load_vrs(sb, uopt, silent, &fileset); if (ret < 0) { if (!silent && ret != -EACCES) { pr_notice("Scanning with blocksize %u failed\n", - uopt.blocksize); + uopt->blocksize); } brelse(sbi->s_lvid_bh); sbi->s_lvid_bh = NULL; @@ -2161,7 +2190,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) } else break; - uopt.blocksize <<= 1; + uopt->blocksize <<= 1; } } if (ret < 0) { @@ -2266,8 +2295,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) error_out: iput(sbi->s_vat_inode); -parse_options_failure: - unload_nls(uopt.nls_map); + unload_nls(uopt->nls_map); if (lvid_open) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index f9a60bc1ab..08ec8756b9 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -23,6 +23,7 @@ #define UDF_FLAG_STRICT 5 #define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNHIDE 7 +#define UDF_FLAG_NOVRS 8 #define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */ #define UDF_FLAG_GID_FORGET 12 #define UDF_FLAG_UID_SET 13 diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c index 758163af39..78ecc63360 100644 --- a/fs/udf/udftime.c +++ b/fs/udf/udftime.c @@ -46,13 +46,18 @@ udf_disk_stamp_to_time(struct timespec64 *dest, struct timestamp src) dest->tv_sec = mktime64(year, src.month, src.day, src.hour, src.minute, src.second); dest->tv_sec -= offset * 60; - dest->tv_nsec = 1000 * (src.centiseconds * 10000 + - src.hundredsOfMicroseconds * 100 + src.microseconds); + /* * Sanitize nanosecond field since reportedly some filesystems are * recorded with bogus sub-second values. */ - dest->tv_nsec %= NSEC_PER_SEC; + if (src.centiseconds < 100 && src.hundredsOfMicroseconds < 100 && + src.microseconds < 100) { + dest->tv_nsec = 1000 * (src.centiseconds * 10000 + + src.hundredsOfMicroseconds * 100 + src.microseconds); + } else { + dest->tv_nsec = 0; + } } void |