Commit Graph

730 Commits

Author SHA1 Message Date
Jan Kara
f54aa97fb7 udf: Fix off-by-one error when discarding preallocation
The condition determining whether the preallocation can be used had
an off-by-one error so we didn't discard preallocation when new
allocation was just following it. This can then confuse code in
inode_getblk().

CC: stable@vger.kernel.org
Fixes: 16d0556568 ("udf: Discard preallocation before extending file with a hole")
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:35 +01:00
Jan Kara
36ec52ea03 udf: Fix file corruption when appending just after end of preallocated extent
When we append new block just after the end of preallocated extent, the
code in inode_getblk() wrongly determined we're going to use the
preallocated extent which resulted in adding block into a wrong logical
offset in the file. Sequence like this manifests it:

xfs_io -f -c "pwrite 0x2cacf 0xd122" -c "truncate 0x2dd6f" \
  -c "pwrite 0x27fd9 0x69a9" -c "pwrite 0x32981 0x7244" <file>

The code that determined the use of preallocated extent is actually
stale because udf_do_extend_file() does not create preallocation anymore
so after calling that function we are sure there's no usable
preallocation. Just remove the faulty condition.

CC: stable@vger.kernel.org
Fixes: 16d0556568 ("udf: Discard preallocation before extending file with a hole")
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:35 +01:00
Jan Kara
36580ed087 udf: Do not allocate blocks on page writeback
Now when we allocate blocks on write page fault there should be no block
allocation happening on page writeback. So just ignore the 'create' flag
passed to udf_get_block(). Note that we can spot dirty buffers without
underlying blocks allocated in writeback when we race with expanding
truncate. However in that case these buffers do not contain valid data
so we can safely ignore them and we would just create ourselves problem
when to trim the tail extent.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:35 +01:00
Jan Kara
3c21204818 udf: Allocate blocks on write page fault
Currently if file with holes is mapped, udf allocates blocks for dirtied
pages during page writeback. This however creates problems when to
truncate final extent to proper size and currently we leave the last
extent untruncated which violates UDF standard. So allocate blocks on
write page fault instead. In that case the last extent gets truncated
the file is closed and everything is happy.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:35 +01:00
Jan Kara
b9a861fd52 udf: Protect truncate and file type conversion with invalidate_lock
Protect truncate and file type conversion in udf_file_write_iter() with
invalidate lock. That will allow us to serialize these paths with page
faults so that the page fault can determine the file type in a racefree
way.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:35 +01:00
Jan Kara
96eeaaaea5 udf: Simplify error handling in udf_file_write_iter()
When udf_expand_file_adinicb() fails, we can now use the standard exit
path instead of implementing our own.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:34 +01:00
Jan Kara
2d532616c7 udf: Push i_data_sem locking into udf_extend_file()
Push i_data_sem locking into udf_extend_file(). It somewhat simplifies
the code around it.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:34 +01:00
Jan Kara
6a3b37e0ea udf: Push i_data_sem locking into udf_expand_file_adinicb()
The checks we do in udf_setsize() and udf_file_write_iter() are safe to
do only with i_rwsem locked as it stabilizes both file type and file
size. Hence we don't need to lock i_data_sem before we enter
udf_expand_file_adinicb() which simplifies the locking somewhat.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:34 +01:00
Jan Kara
f950fd0529 udf: Protect rename against modification of moved directory
When we are renaming a directory to a different directory, we need to
update '..' entry in the moved directory. However nothing prevents moved
directory from being modified and even converted from the in-ICB format
to the normal format which results in a crash. Fix the problem by
locking the moved directory.

Reported-by: syzbot+aebf90eea2671c43112a@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:34 +01:00
Jan Kara
32f123a3f3 udf: Fold udf_getblk() into udf_bread()
udf_getblk() has a single call site. Fold it there.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
541e047b14 udf: Use udf_map_block() in udf_getblk()
Use the new function udf_map_block() in udf_getblk().

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
8094252178 udf: Add flag to disable block preallocation
In some cases we don't want to create block preallocation when
allocating blocks. Add a flag to udf_map_rq controlling the behavior.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
b3c03fcef2 udf: Pass mapping request into inode_getblk()
Pass struct udf_map_rq into inode_getblk() instead of unfolding it and
the putting the results back.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
364a6665d5 udf: Fold udf_block_map() into udf_map_block()
udf_block_map() has now only a single caller. Fold it there.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
15a08f5162 udf: Convert udf_symlink_filler() to use udf_bread()
Convert udf_symlink_filler() to use udf_bread() instead of mapping and
reading buffer head manually.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
f33321b29b udf: Do not call udf_block_map() on ICB files
Currently udf_symlink_filler() called udf_block_map() even on files
which have data stored inside the ICB. This is invalid as we cannot map
blocks for such files (although so far the error got silently ignored).
The call happened because we could not call block mapping function once
we've acquired i_data_sem and determined whether the file has data
stored in the ICB. For symlinks the situation is luckily simple as they
get never modified so file type never changes once it is set. Hence we
can check the file type even without i_data_sem. Just drop the
i_data_sem locking and move block mapping to where it is needed.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
08931b7893 udf: Use udf_bread() in udf_load_vat()
Use udf_bread() instead of mapping and loadign buffer head manually in
udf_load_vat().

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
4215db46d5 udf: Use udf_bread() in udf_get_pblock_virt15()
Use udf_bread() instead of mapping and reading buffer head manually in
udf_get_pblock_virt15().

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
f3a30be777 udf: Factor out block mapping into udf_map_block()
Create new block mapping function udf_map_block() that takes new
udf_map_rq structure describing mapping request. We will convert other
places to use this function for block mapping.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
de80dae03c udf: Move incrementing of goal block directly into inode_getblk()
inode_getblk() sets goal block for the next allocation to the currently
allocated block. This is obviously one less than what the goal block
should be which we fixup in udf_get_block(). Just set the right goal
block directly in inode_getblk().

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:33 +01:00
Jan Kara
101ee137d3 udf: Drop VARCONV support
UDF was supporting a strange mode where the media was containing 7
blocks of unknown data for every 32 blocks of the filesystem. I have yet
to see the media that would need such conversion (maybe it comes from
packet writing times) and the conversions have been inconsistent in the
code. In particular any write will write to a wrong block and corrupt
the media. This is an indication and no user actually needs this so
let's just drop the support instead of trying to fix it.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:32 +01:00
Jan Kara
bd904f3c74 udf: Unify types in anchor block detection
When detecting last recorded block and from it derived anchor block
position, we were mixing unsigned long, u32, and sector_t types. Since
udf supports only 32-bit block numbers this is harmless but sometimes
makes things awkward. Convert everything to udf_pblk_t and also handle
the situation when block device size would not fit into udf_pblk_t.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:32 +01:00
Jan Kara
1ea1cd11c7 udf: Fix directory iteration for longer tail extents
When directory's last extent has more that one block and its length is
not multiple of a block side, the code wrongly decided to move to the
next extent instead of processing the last partial block. This led to
directory corruption. Fix the rounding issue.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:32 +01:00
Jan Kara
ee454ad2fc udf: Propagate errors from udf_advance_blk()
When we spot directory corruption when trying to load next directory
extent, we didn't propagate the error up properly, leading to possibly
indefinite looping on corrupted directories. Fix the problem by
propagating the error properly.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:32 +01:00
Jan Kara
3bea4ae1c9 udf: Zero udf name padding
Padding of name in the directory entry needs to be zeroed out. Fix it.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-26 16:46:32 +01:00
Colin Ian King
1fb40763a5 udf: remove redundant variable netype
The variable netype is assigned a value that is never read, the assignment
is redundant the variable can be removed.

Message-Id: <20230105134925.45599-1-colin.i.king@gmail.com>
Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
85a37983ec udf: Detect system inodes linked into directory hierarchy
When UDF filesystem is corrupted, hidden system inodes can be linked
into directory hierarchy which is an avenue for further serious
corruption of the filesystem and kernel confusion as noticed by syzbot
fuzzed images. Refuse to access system inodes linked into directory
hierarchy and vice versa.

CC: stable@vger.kernel.org
Reported-by: syzbot+38695a20b8addcbc1084@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
fc8033a34a udf: Preserve link count of system files
System files in UDF filesystem have link count 0. To not confuse VFS we
fudge the link count to be 1 when reading such inodes however we forget
to restore the link count of 0 when writing such inodes. Fix that.

CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
256fe4162f udf: Do not update file length for failed writes to inline files
When write to inline file fails (or happens only partly), we still
updated length of inline data as if the whole write succeeded. Fix the
update of length of inline data to happen only if the write succeeds.

Reported-by: syzbot+0937935b993956ba28ab@syzkaller.appspotmail.com
CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Colin Ian King
02113feaf6 udf: Fix spelling mistake "lenght" -> "length"
There is a spelling mistake in a udf_err message. Fix it.

Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Message-Id: <20221230231452.5821-1-colin.i.king@gmail.com>
2023-01-09 10:39:53 +01:00
Jan Kara
b316c443b4 udf: Keep i_lenExtents consistent with the total length of extents
When rounding the last extent to blocksize in inode_getblk() we forgot
to update also i_lenExtents to match the new extent length. This
inconsistency can later confuse some assertion checks.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
e57191a8d4 udf: Move setting of i_lenExtents into udf_do_extend_file()
When expanding file for a write into a hole, we were not updating total
length of inode's extents properly. Move the update of i_lenExtents into
udf_do_extend_file() so that both expanding of file by truncate and
expanding of file by writing beyond EOF properly update the length of
extents. As a bonus, we also correctly update the length of extents when
only part of extents can be written.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
0aba4860b0 udf: Allocate name buffer in directory iterator on heap
Currently we allocate name buffer in directory iterators (struct
udf_fileident_iter) on stack. These structures are relatively large
(some 360 bytes on 64-bit architectures). For udf_rename() which needs
to keep three of these structures in parallel the stack usage becomes
rather heavy - 1536 bytes in total. Allocate the name buffer in the
iterator from heap to avoid excessive stack usage.

Link: https://lore.kernel.org/all/202212200558.lK9x1KW0-lkp@intel.com
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
19fd80de0a udf: Handle error when adding extent to a file
When adding extent to a file fails, so far we've silently squelshed the
error. Make sure to propagate it up properly.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:53 +01:00
Jan Kara
2b10074d91 udf: Handle error when adding extent to symlink
When adding extent describing symlink data fails, make sure to handle
the error properly, propagate it up and free the already allocated
block.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
33e9a53cd9 udf: Handle error when expanding directory
When there is an error when adding extent to the directory to expand it,
make sure to propagate the error up properly. This is not expected to
happen currently but let's make the code more futureproof.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
53cafe1d6d udf: Do not bother merging very long extents
When merging very long extents we try to push as much length as possible
to the first extent. However this is unnecessarily complicated and not
really worth the trouble. Furthermore there was a bug in the logic
resulting in corrupting extents in the file as syzbot reproducer shows.
So just don't bother with the merging of extents that are too long
together.

CC: stable@vger.kernel.org
Reported-by: syzbot+60f291a24acecb3c2bd5@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
70bfb3a8d6 udf: Truncate added extents on failed expansion
When a file expansion failed because we didn't have enough space for
indirect extents make sure we truncate extents created so far so that we
don't leave extents beyond EOF.

CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
1e0290d61a udf: Remove old directory iteration code
Remove old directory iteration code that is now unused.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
e9109a92d2 udf: Convert udf_rename() to new directory iteration code
Convert udf_rename() to use new directory iteration code.

Reported-by: syzbot+0eaad3590d65102b9391@syzkaller.appspotmail.com
Reported-by: syzbot+b7fc73213bc2361ab650@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
dbfb102d16 udf: Convert udf_link() to new directory iteration code
Convert udf_link() to use new directory iteration code for adding entry
into the directory.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
00bce6f792 udf: Convert udf_mkdir() to new directory iteration code
Convert udf_mkdir() to new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
ef91f9998b udf: Convert udf_add_nondir() to new directory iteration
Convert udf_add_nondir() to new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
f284480340 udf: Implement adding of dir entries using new iteration code
Implement function udf_fiiter_add_entry() adding new directory entries
using new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
6ec01a8020 udf: Convert udf_unlink() to new directory iteration code
Convert udf_unlink() to new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
d11ffa8d3e udf: Convert udf_rmdir() to new directory iteration code
Convert udf_rmdir() to use new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
4cca7e3df7 udf: Provide function to mark entry as deleted using new directory iteration code
Provide function udf_fiiter_delete_entry() to mark directory entry as
deleted using new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
afb525f466 udf: Convert empty_dir() to new directory iteration code
Convert empty_dir() to new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:52 +01:00
Jan Kara
9b06fbef42 udf: Convert udf_get_parent() to new directory iteration code
Convert udf_get_parent() to use udf_fiiter_find_entry().

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:51 +01:00
Jan Kara
200918b34d udf: Convert udf_lookup() to use new directory iteration code
Convert udf_lookup() to use udf_fiiter_find_entry() for looking up
directory entries.

Signed-off-by: Jan Kara <jack@suse.cz>
2023-01-09 10:39:51 +01:00