summaryrefslogtreecommitdiff
path: root/fs/udf
diff options
context:
space:
mode:
Diffstat (limited to 'fs/udf')
-rw-r--r--fs/udf/balloc.c23
-rw-r--r--fs/udf/dir.c30
-rw-r--r--fs/udf/ecma_167.h2
-rw-r--r--fs/udf/file.c20
-rw-r--r--fs/udf/ialloc.c4
-rw-r--r--fs/udf/inode.c87
-rw-r--r--fs/udf/namei.c71
-rw-r--r--fs/udf/super.c573
-rw-r--r--fs/udf/symlink.c34
-rw-r--r--fs/udf/truncate.c2
-rw-r--r--fs/udf/udf_sb.h3
11 files changed, 448 insertions, 401 deletions
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index f5de2030e769..807c493ed0cd 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -74,9 +74,9 @@ static int read_block_bitmap(struct super_block *sb,
return 0;
}
-static int __load_block_bitmap(struct super_block *sb,
- struct udf_bitmap *bitmap,
- unsigned int block_group)
+static int load_block_bitmap(struct super_block *sb,
+ struct udf_bitmap *bitmap,
+ unsigned int block_group)
{
int retval = 0;
int nr_groups = bitmap->s_nr_groups;
@@ -103,23 +103,6 @@ static int __load_block_bitmap(struct super_block *sb,
return block_group;
}
-static inline int load_block_bitmap(struct super_block *sb,
- struct udf_bitmap *bitmap,
- unsigned int block_group)
-{
- int slot;
-
- slot = __load_block_bitmap(sb, bitmap, block_group);
-
- if (slot < 0)
- return slot;
-
- if (!bitmap->s_block_bitmap[slot])
- return -EIO;
-
- return slot;
-}
-
static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt)
{
struct udf_sb_info *sbi = UDF_SB(sb);
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
index f6533f93851b..5023dfe191e8 100644
--- a/fs/udf/dir.c
+++ b/fs/udf/dir.c
@@ -60,14 +60,14 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
* identifying beginning of dir entry (names are under user control),
* we need to scan the directory from the beginning.
*/
- if (!inode_eq_iversion(dir, file->f_version)) {
+ if (!inode_eq_iversion(dir, *(u64 *)file->private_data)) {
emit_pos = nf_pos;
nf_pos = 0;
} else {
pos_valid = true;
}
- fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+ fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
if (!fname) {
ret = -ENOMEM;
goto out;
@@ -122,15 +122,37 @@ out_iter:
udf_fiiter_release(&iter);
out:
if (pos_valid)
- file->f_version = inode_query_iversion(dir);
+ *(u64 *)file->private_data = inode_query_iversion(dir);
kfree(fname);
return ret;
}
+static int udf_dir_open(struct inode *inode, struct file *file)
+{
+ file->private_data = kzalloc(sizeof(u64), GFP_KERNEL);
+ if (!file->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static int udf_dir_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static loff_t udf_dir_llseek(struct file *file, loff_t offset, int whence)
+{
+ return generic_llseek_cookie(file, offset, whence,
+ (u64 *)file->private_data);
+}
+
/* readdir and lookup functions */
const struct file_operations udf_dir_operations = {
- .llseek = generic_file_llseek,
+ .open = udf_dir_open,
+ .release = udf_dir_release,
+ .llseek = udf_dir_llseek,
.read = generic_read_dir,
.iterate_shared = udf_readdir,
.unlocked_ioctl = udf_ioctl,
diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h
index de17a97e8667..415b050b977d 100644
--- a/fs/udf/ecma_167.h
+++ b/fs/udf/ecma_167.h
@@ -471,7 +471,7 @@ struct fileIdentDesc {
uint8_t lengthFileIdent;
struct long_ad icb;
__le16 lengthOfImpUse;
- uint8_t impUse[];
+ /* uint8_t impUse[]; */
/* uint8_t fileIdent[]; */
/* uint8_t padding[]; */
} __packed;
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 94daaaf76f71..412fe7c4d348 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -39,7 +39,7 @@ static vm_fault_t udf_page_mkwrite(struct vm_fault *vmf)
struct vm_area_struct *vma = vmf->vma;
struct inode *inode = file_inode(vma->vm_file);
struct address_space *mapping = inode->i_mapping;
- struct page *page = vmf->page;
+ struct folio *folio = page_folio(vmf->page);
loff_t size;
unsigned int end;
vm_fault_t ret = VM_FAULT_LOCKED;
@@ -48,31 +48,31 @@ static vm_fault_t udf_page_mkwrite(struct vm_fault *vmf)
sb_start_pagefault(inode->i_sb);
file_update_time(vma->vm_file);
filemap_invalidate_lock_shared(mapping);
- lock_page(page);
+ folio_lock(folio);
size = i_size_read(inode);
- if (page->mapping != inode->i_mapping || page_offset(page) >= size) {
- unlock_page(page);
+ if (folio->mapping != inode->i_mapping || folio_pos(folio) >= size) {
+ folio_unlock(folio);
ret = VM_FAULT_NOPAGE;
goto out_unlock;
}
/* Space is already allocated for in-ICB file */
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
goto out_dirty;
- if (page->index == size >> PAGE_SHIFT)
+ if (folio->index == size >> PAGE_SHIFT)
end = size & ~PAGE_MASK;
else
end = PAGE_SIZE;
- err = __block_write_begin(page, 0, end, udf_get_block);
+ err = __block_write_begin(folio, 0, end, udf_get_block);
if (err) {
- unlock_page(page);
+ folio_unlock(folio);
ret = vmf_fs_error(err);
goto out_unlock;
}
- block_commit_write(page, 0, end);
+ block_commit_write(&folio->page, 0, end);
out_dirty:
- set_page_dirty(page);
- wait_for_stable_page(page);
+ folio_mark_dirty(folio);
+ folio_wait_stable(folio);
out_unlock:
filemap_invalidate_unlock_shared(mapping);
sb_end_pagefault(inode->i_sb);
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
index 6b558cbbeb6b..5f1f969f4134 100644
--- a/fs/udf/ialloc.c
+++ b/fs/udf/ialloc.c
@@ -100,8 +100,8 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else
iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
- inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
- iinfo->i_crtime = inode->i_mtime;
+ simple_inode_init_ts(inode);
+ iinfo->i_crtime = inode_get_mtime(inode);
if (unlikely(insert_inode_locked(inode) < 0)) {
make_bad_inode(inode);
iput(inode);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index e98c198f85b9..4386dd845e40 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -208,19 +208,14 @@ static int udf_writepages(struct address_space *mapping,
return write_cache_pages(mapping, wbc, udf_adinicb_writepage, NULL);
}
-static void udf_adinicb_readpage(struct page *page)
+static void udf_adinicb_read_folio(struct folio *folio)
{
- struct inode *inode = page->mapping->host;
- char *kaddr;
+ struct inode *inode = folio->mapping->host;
struct udf_inode_info *iinfo = UDF_I(inode);
loff_t isize = i_size_read(inode);
- kaddr = kmap_local_page(page);
- memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr, isize);
- memset(kaddr + isize, 0, PAGE_SIZE - isize);
- flush_dcache_page(page);
- SetPageUptodate(page);
- kunmap_local(kaddr);
+ folio_fill_tail(folio, 0, iinfo->i_data + iinfo->i_lenEAttr, isize);
+ folio_mark_uptodate(folio);
}
static int udf_read_folio(struct file *file, struct folio *folio)
@@ -228,7 +223,7 @@ static int udf_read_folio(struct file *file, struct folio *folio)
struct udf_inode_info *iinfo = UDF_I(file_inode(file));
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
- udf_adinicb_readpage(&folio->page);
+ udf_adinicb_read_folio(folio);
folio_unlock(folio);
return 0;
}
@@ -251,14 +246,14 @@ static void udf_readahead(struct readahead_control *rac)
static int udf_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len,
- struct page **pagep, void **fsdata)
+ struct folio **foliop, void **fsdata)
{
struct udf_inode_info *iinfo = UDF_I(file_inode(file));
- struct page *page;
+ struct folio *folio;
int ret;
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
- ret = block_write_begin(mapping, pos, len, pagep,
+ ret = block_write_begin(mapping, pos, len, foliop,
udf_get_block);
if (unlikely(ret))
udf_write_failed(mapping, pos + len);
@@ -266,31 +261,32 @@ static int udf_write_begin(struct file *file, struct address_space *mapping,
}
if (WARN_ON_ONCE(pos >= PAGE_SIZE))
return -EIO;
- page = grab_cache_page_write_begin(mapping, 0);
- if (!page)
- return -ENOMEM;
- *pagep = page;
- if (!PageUptodate(page))
- udf_adinicb_readpage(page);
+ folio = __filemap_get_folio(mapping, 0, FGP_WRITEBEGIN,
+ mapping_gfp_mask(mapping));
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
+ *foliop = folio;
+ if (!folio_test_uptodate(folio))
+ udf_adinicb_read_folio(folio);
return 0;
}
static int udf_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
- struct page *page, void *fsdata)
+ struct folio *folio, void *fsdata)
{
struct inode *inode = file_inode(file);
loff_t last_pos;
if (UDF_I(inode)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
- return generic_write_end(file, mapping, pos, len, copied, page,
+ return generic_write_end(file, mapping, pos, len, copied, folio,
fsdata);
last_pos = pos + copied;
if (last_pos > inode->i_size)
i_size_write(inode, last_pos);
- set_page_dirty(page);
- unlock_page(page);
- put_page(page);
+ folio_mark_dirty(folio);
+ folio_unlock(folio);
+ folio_put(folio);
return copied;
}
@@ -363,7 +359,7 @@ int udf_expand_file_adinicb(struct inode *inode)
return PTR_ERR(folio);
if (!folio_test_uptodate(folio))
- udf_adinicb_readpage(&folio->page);
+ udf_adinicb_read_folio(folio);
down_write(&iinfo->i_data_sem);
memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
iinfo->i_lenAlloc);
@@ -814,6 +810,7 @@ static int inode_getblk(struct inode *inode, struct udf_map_rq *map)
}
map->oflags = UDF_BLK_MAPPED;
map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc, offset);
+ ret = 0;
goto out_free;
}
@@ -1278,8 +1275,6 @@ int udf_setsize(struct inode *inode, loff_t newsize)
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return -EINVAL;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- return -EPERM;
iinfo = UDF_I(inode);
if (newsize > inode->i_size) {
@@ -1325,7 +1320,7 @@ set_size:
return err;
}
update_time:
- inode->i_mtime = inode_set_ctime_current(inode);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
if (IS_SYNC(inode))
udf_sync_inode(inode);
else
@@ -1354,7 +1349,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode)
int bs = inode->i_sb->s_blocksize;
int ret = -EIO;
uint32_t uid, gid;
- struct timespec64 ctime;
+ struct timespec64 ts;
reread:
if (iloc->partitionReferenceNum >= sbi->s_partitions) {
@@ -1531,10 +1526,12 @@ reread:
inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
(inode->i_sb->s_blocksize_bits - 9);
- udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime);
- udf_disk_stamp_to_time(&inode->i_mtime, fe->modificationTime);
- udf_disk_stamp_to_time(&ctime, fe->attrTime);
- inode_set_ctime_to_ts(inode, ctime);
+ udf_disk_stamp_to_time(&ts, fe->accessTime);
+ inode_set_atime_to_ts(inode, ts);
+ udf_disk_stamp_to_time(&ts, fe->modificationTime);
+ inode_set_mtime_to_ts(inode, ts);
+ udf_disk_stamp_to_time(&ts, fe->attrTime);
+ inode_set_ctime_to_ts(inode, ts);
iinfo->i_unique = le64_to_cpu(fe->uniqueID);
iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr);
@@ -1546,11 +1543,13 @@ reread:
inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
(inode->i_sb->s_blocksize_bits - 9);
- udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime);
- udf_disk_stamp_to_time(&inode->i_mtime, efe->modificationTime);
+ udf_disk_stamp_to_time(&ts, efe->accessTime);
+ inode_set_atime_to_ts(inode, ts);
+ udf_disk_stamp_to_time(&ts, efe->modificationTime);
+ inode_set_mtime_to_ts(inode, ts);
+ udf_disk_stamp_to_time(&ts, efe->attrTime);
+ inode_set_ctime_to_ts(inode, ts);
udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime);
- udf_disk_stamp_to_time(&ctime, efe->attrTime);
- inode_set_ctime_to_ts(inode, ctime);
iinfo->i_unique = le64_to_cpu(efe->uniqueID);
iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr);
@@ -1825,8 +1824,8 @@ static int udf_update_inode(struct inode *inode, int do_sync)
inode->i_sb->s_blocksize - sizeof(struct fileEntry));
fe->logicalBlocksRecorded = cpu_to_le64(lb_recorded);
- udf_time_to_disk_stamp(&fe->accessTime, inode->i_atime);
- udf_time_to_disk_stamp(&fe->modificationTime, inode->i_mtime);
+ udf_time_to_disk_stamp(&fe->accessTime, inode_get_atime(inode));
+ udf_time_to_disk_stamp(&fe->modificationTime, inode_get_mtime(inode));
udf_time_to_disk_stamp(&fe->attrTime, inode_get_ctime(inode));
memset(&(fe->impIdent), 0, sizeof(struct regid));
strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER);
@@ -1856,12 +1855,14 @@ static int udf_update_inode(struct inode *inode, int do_sync)
cpu_to_le32(inode->i_sb->s_blocksize);
}
- udf_adjust_time(iinfo, inode->i_atime);
- udf_adjust_time(iinfo, inode->i_mtime);
+ udf_adjust_time(iinfo, inode_get_atime(inode));
+ udf_adjust_time(iinfo, inode_get_mtime(inode));
udf_adjust_time(iinfo, inode_get_ctime(inode));
- udf_time_to_disk_stamp(&efe->accessTime, inode->i_atime);
- udf_time_to_disk_stamp(&efe->modificationTime, inode->i_mtime);
+ udf_time_to_disk_stamp(&efe->accessTime,
+ inode_get_atime(inode));
+ udf_time_to_disk_stamp(&efe->modificationTime,
+ inode_get_mtime(inode));
udf_time_to_disk_stamp(&efe->createTime, iinfo->i_crtime);
udf_time_to_disk_stamp(&efe->attrTime, inode_get_ctime(inode));
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 605f182da42c..2cb49b6b0716 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;
@@ -125,8 +125,6 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
udf_fiiter_release(&iter);
inode = udf_iget(dir->i_sb, &loc);
- if (IS_ERR(inode))
- return ERR_CAST(inode);
}
return d_splice_alias(inode, dentry);
@@ -230,8 +228,6 @@ static int udf_fiiter_add_entry(struct inode *dir, struct dentry *dentry,
char name[UDF_NAME_LEN_CS0];
if (dentry) {
- if (!dentry->d_name.len)
- return -EINVAL;
namelen = udf_put_filename(dir->i_sb, dentry->d_name.name,
dentry->d_name.len,
name, UDF_NAME_LEN_CS0);
@@ -365,7 +361,7 @@ static int udf_add_nondir(struct dentry *dentry, struct inode *inode)
*(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse =
cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
udf_fiiter_write_fi(&iter, NULL);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
mark_inode_dirty(dir);
udf_fiiter_release(&iter);
udf_add_fid_counter(dir->i_sb, false, 1);
@@ -471,7 +467,7 @@ static int udf_mkdir(struct mnt_idmap *idmap, struct inode *dir,
udf_fiiter_release(&iter);
udf_add_fid_counter(dir->i_sb, true, 1);
inc_nlink(dir);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
mark_inode_dirty(dir);
d_instantiate_new(dentry, inode);
@@ -521,10 +517,14 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry)
inode->i_nlink);
clear_nlink(inode);
inode->i_size = 0;
- inode_dec_link_count(dir);
+ if (dir->i_nlink >= 3)
+ inode_dec_link_count(dir);
+ else
+ udf_warn(inode->i_sb, "parent dir link count too low (%u)\n",
+ dir->i_nlink);
udf_add_fid_counter(dir->i_sb, true, -1);
- dir->i_mtime = inode_set_ctime_to_ts(dir,
- inode_set_ctime_current(inode));
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)));
mark_inode_dirty(dir);
ret = 0;
end_rmdir:
@@ -555,7 +555,7 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
set_nlink(inode, 1);
}
udf_fiiter_delete_entry(&iter);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
mark_inode_dirty(dir);
inode_dec_link_count(inode);
udf_add_fid_counter(dir->i_sb, false, -1);
@@ -570,7 +570,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 = {};
@@ -583,17 +583,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);
@@ -748,7 +751,7 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir,
udf_add_fid_counter(dir->i_sb, false, 1);
inode_set_ctime_current(inode);
mark_inode_dirty(inode);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
mark_inode_dirty(dir);
ihold(inode);
d_instantiate(dentry, inode);
@@ -766,7 +769,7 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry);
struct udf_fileident_iter oiter, niter, diriter;
- bool has_diriter = false;
+ bool has_diriter = false, is_dir = false;
int retval;
struct kernel_lb_addr tloc;
@@ -788,7 +791,20 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto out_oiter;
+ retval = -EFSCORRUPTED;
+ if (new_inode->i_nlink != 2)
+ goto out_oiter;
}
+ retval = -EFSCORRUPTED;
+ if (old_dir->i_nlink < 3)
+ goto out_oiter;
+ is_dir = true;
+ } else if (new_inode) {
+ retval = -EFSCORRUPTED;
+ if (new_inode->i_nlink < 1)
+ goto out_oiter;
+ }
+ if (is_dir && old_dir != new_dir) {
retval = udf_fiiter_find_entry(old_inode, &dotdot_name,
&diriter);
if (retval == -ENOENT) {
@@ -866,8 +882,8 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
udf_add_fid_counter(old_dir->i_sb, S_ISDIR(new_inode->i_mode),
-1);
}
- old_dir->i_mtime = inode_set_ctime_current(old_dir);
- new_dir->i_mtime = inode_set_ctime_current(new_dir);
+ inode_set_mtime_to_ts(old_dir, inode_set_ctime_current(old_dir));
+ inode_set_mtime_to_ts(new_dir, inode_set_ctime_current(new_dir));
mark_inode_dirty(old_dir);
mark_inode_dirty(new_dir);
@@ -876,7 +892,9 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
cpu_to_lelb(UDF_I(new_dir)->i_location);
udf_fiiter_write_fi(&diriter, NULL);
udf_fiiter_release(&diriter);
+ }
+ if (is_dir) {
inode_dec_link_count(old_dir);
if (new_inode)
inode_dec_link_count(new_inode);
@@ -897,7 +915,6 @@ out_oiter:
static struct dentry *udf_get_parent(struct dentry *child)
{
struct kernel_lb_addr tloc;
- struct inode *inode = NULL;
struct udf_fileident_iter iter;
int err;
@@ -907,11 +924,7 @@ static struct dentry *udf_get_parent(struct dentry *child)
tloc = lelb_to_cpu(iter.fi.icb.extLocation);
udf_fiiter_release(&iter);
- inode = udf_iget(child->d_sb, &tloc);
- if (IS_ERR(inode))
- return ERR_CAST(inode);
-
- return d_obtain_alias(inode);
+ return d_obtain_alias(udf_iget(child->d_sb, &tloc));
}
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 20dff9ed2471..b2f168b0a0d1 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;
@@ -359,7 +426,7 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root)
}
/*
- * udf_parse_options
+ * udf_parse_param
*
* PURPOSE
* Parse mount options.
@@ -402,12 +469,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
@@ -419,229 +486,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);
@@ -865,7 +896,7 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
int ret;
struct timestamp *ts;
- outstr = kmalloc(128, GFP_NOFS);
+ outstr = kzalloc(128, GFP_KERNEL);
if (!outstr)
return -ENOMEM;
@@ -891,11 +922,11 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
ret = udf_dstrCS0toChar(sb, outstr, 31, pvoldesc->volIdent, 32);
if (ret < 0) {
- strcpy(UDF_SB(sb)->s_volume_ident, "InvalidName");
+ strscpy_pad(UDF_SB(sb)->s_volume_ident, "InvalidName");
pr_warn("incorrect volume identification, setting to "
"'InvalidName'\n");
} else {
- strncpy(UDF_SB(sb)->s_volume_ident, outstr, ret);
+ strscpy_pad(UDF_SB(sb)->s_volume_ident, outstr);
}
udf_debug("volIdent[] = '%s'\n", UDF_SB(sb)->s_volume_ident);
@@ -1409,7 +1440,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
struct genericPartitionMap *gpm;
uint16_t ident;
struct buffer_head *bh;
- unsigned int table_len;
+ unsigned int table_len, part_map_count;
int ret;
bh = udf_read_tagged(sb, block, block, &ident);
@@ -1430,7 +1461,16 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
"logical volume");
if (ret)
goto out_bh;
- ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps));
+
+ part_map_count = le32_to_cpu(lvd->numPartitionMaps);
+ if (part_map_count > table_len / sizeof(struct genericPartitionMap1)) {
+ udf_err(sb, "error loading logical volume descriptor: "
+ "Too many partition maps (%u > %u)\n", part_map_count,
+ table_len / (unsigned)sizeof(struct genericPartitionMap1));
+ ret = -EIO;
+ goto out_bh;
+ }
+ ret = udf_sb_alloc_partition_maps(sb, part_map_count);
if (ret)
goto out_bh;
@@ -1555,6 +1595,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.
*/
@@ -1565,7 +1619,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;
@@ -1587,32 +1640,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;
- }
}
/*
@@ -1962,7 +2010,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) {
@@ -2100,23 +2148,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)
@@ -2126,25 +2166,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);
@@ -2155,16 +2193,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;
@@ -2177,7 +2215,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) {
@@ -2282,8 +2320,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/symlink.c b/fs/udf/symlink.c
index f7eaf7b14594..fe03745d09b1 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -99,18 +99,17 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
static int udf_symlink_filler(struct file *file, struct folio *folio)
{
- struct page *page = &folio->page;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
struct buffer_head *bh = NULL;
unsigned char *symlink;
int err = 0;
- unsigned char *p = page_address(page);
+ unsigned char *p = folio_address(folio);
struct udf_inode_info *iinfo = UDF_I(inode);
/* We don't support symlinks longer than one block */
if (inode->i_size > inode->i_sb->s_blocksize) {
err = -ENAMETOOLONG;
- goto out_unlock;
+ goto out;
}
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
@@ -120,24 +119,15 @@ static int udf_symlink_filler(struct file *file, struct folio *folio)
if (!bh) {
if (!err)
err = -EFSCORRUPTED;
- goto out_err;
+ goto out;
}
symlink = bh->b_data;
}
err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
brelse(bh);
- if (err)
- goto out_err;
-
- SetPageUptodate(page);
- unlock_page(page);
- return 0;
-
-out_err:
- SetPageError(page);
-out_unlock:
- unlock_page(page);
+out:
+ folio_end_read(folio, err == 0);
return err;
}
@@ -147,12 +137,12 @@ static int udf_symlink_getattr(struct mnt_idmap *idmap,
{
struct dentry *dentry = path->dentry;
struct inode *inode = d_backing_inode(dentry);
- struct page *page;
+ struct folio *folio;
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
- page = read_mapping_page(inode->i_mapping, 0, NULL);
- if (IS_ERR(page))
- return PTR_ERR(page);
+ folio = read_mapping_folio(inode->i_mapping, 0, NULL);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
/*
* UDF uses non-trivial encoding of symlinks so i_size does not match
* number of characters reported by readlink(2) which apparently some
@@ -162,8 +152,8 @@ static int udf_symlink_getattr(struct mnt_idmap *idmap,
* let's report the length of string returned by readlink(2) for
* st_size.
*/
- stat->size = strlen(page_address(page));
- put_page(page);
+ stat->size = strlen(folio_address(folio));
+ folio_put(folio);
return 0;
}
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index 4f33a4a48886..b4071c9cf8c9 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -115,7 +115,7 @@ void udf_truncate_tail_extent(struct inode *inode)
}
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
- if (ret == 0)
+ if (ret >= 0)
iinfo->i_lenExtents = inode->i_size;
brelse(epos.bh);
}
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h
index 9af6ff7f9747..08ec8756b948 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
@@ -86,7 +87,7 @@ struct udf_virtual_data {
struct udf_bitmap {
__u32 s_extPosition;
int s_nr_groups;
- struct buffer_head *s_block_bitmap[];
+ struct buffer_head *s_block_bitmap[] __counted_by(s_nr_groups);
};
struct udf_part_map {