Introduction
============
The fsck.ubifs provides a way to fix inconsistent UBIFS image(which is
corrupted by hardware exceptions or UBIFS realization bugs) and makes
filesystem become consistent, just like fsck tools(eg. fsck.ext4,
fsck.f2fs, fsck.fat, etc.) do.
Why do we need fsck.ubifs?
--------------------------
The inconsistent UBIFS image is produced by mainly two aspects:
- *Hardware exceptions*: Some of them are ecc uncorrectable errors(eg.
[1][2]), some of them are caused by intermittent writing protection
(unstable voltage).
- *UBIFS realization bugs*: Some of them are known bugs which are
fixable (eg. [3][4][5][6][7][8][9][10][11][12][13], etc.), some of
them are unknown bugs(eg. [14]), some of them are hard to fix(eg. [15]).
Once the UBIFS image becomes inconsistent, userspace applications won't
work properly, as we all know, UBIFS is mainly applied in embedded
system, which could affect many domains(eg. communications, IoT, family
network, etc.). The only way to rescue device is formating UBI device
and mkfs, which will lost all userdata, and it could be intolerable for
some important situations.
So, the fsck.ubifs is urgent for UBIFS, even it has been born for 15
years, and it's not too late to do it for there will be more embedded
devices in the future IOT world.
Manuals
=======
There are four modes for fsck.ubifs:
1. normal mode(no options): Check the filesystem, ask user whether to
fix the problem as long as inconsistent data is found during fs
checking.
2. safe mode(-a option): Check and automatic safely repair the
filesystem, if there are any data dropping operations needed by
fixing, fsck will fail.
3. danger mode(-y option): Answer 'yes' to all questions. There are two
sub modes:
a) Default submode(no options): Check and automatic repair the
filesystem according to TNC, data dropping will be reported. If
TNC/master/log is corrupted, fsck will fail.
b) rebuild submode(-b option): Check and automatic forcibly repair
the filesystem, turns to rebuild filesystem if TNC/master/log is
corrupted. Always make fsck successful.
4. check mode(-n option): Make no changes to the filesystem, only check
the filesystem. This mode doesn't check space, because unclean LEBs
cannot be rewritten in read-only mode.
The exit code returned by fsck.ubifs is compatible with FSCK(8), which
is the sum of the following conditions:
0 - No errors
1 - File system errors corrected
2 - System should be rebooted
4 - File system errors left uncorrected
8 - Operational error
16 - Usage or syntax error
32 - Fsck canceled by user request
128 - Shared library error
Designment
==========
There are 2 working modes for fsck: rebuild mode and non-rebuild mode.
The main idea is that construct all files by scanning the entire
filesystem, then check the consistency of metadata(file meta information,
space statistics, etc.) according to the files. The file(xattr is treated
as a file) is organized as:
file tree(rbtree, inum indexed)
/ \
file1 file2
/ \
file3 file4
file {
inode node // each file has 1 inode node
dentry (sub rb_tree, sqnum indexed)
// '/' has no dentries, otherwise at least 1 dentry is required.
trun node // the newest one truncation node
data (sub rb_tree, block number indexed)
// Each file may have 0 or many data nodes
xattrs (sub rb_tree, inum indexed)
// Each file may have 0 or many xattr files
}
Step 0. Both two modes need to read the superblock firstly, fsck fails if
superblock is corrupted, because fsck has no idea about the location
of each area(master, log, main, etc.) when the layout is lost.
A. Rebuild mode(Similar to [16]):
Step 1. Scan nodes(inode node/dentry node/data node/truncation node) from all
LEBs.
a) Corrupted LEBs(eg. garbage data, corrupted empty space) are dropped
during scanning.
b) Corrupted nodes(eg. incorrect crc, bad inode size, bad dentry name
length, etc.) are dropped during scanning.
c) Valid inode nodes(nlink > 0) and dentry nodes(inum != 0) are put
into two valid trees(valid_inos & valid_dents) separately.
d) Deleted inode nodes (nlink is 0) and deleted dentry nodes(inum is 0)
are put into two deleted trees(del_inos & del_dents) separately.
e) Other nodes(data nodes/truncation node) are put into corresponding
file, if the file doesn't exist, insert a new file into the file
tree.
Step 2. Traverse nodes from deleted trees, remove inode nodes and dentry nodes
with smaller sqnum from valid trees. valid_inos - del_inos = left_inos,
valid_dents - del_dents = left_dents.
This step handles the deleting case, for example, file A is deleted,
deleted inode node and deleted dentry node are written, if we ignore
the deleted nodes, file A can be recovered after rebuilding because
undeleted inode node and undeleted dentry node can be scanned. There's
an exception, if deleted inode node and deleted dentry node are
reclaimed(by gc) after deletion, file A is recovered. So deleted data
or files could be recovered by rebuild mode.
Step 3. Traverse left_inos and left_dents, insert inode node and dentry nodes
into the corresponding file.
Step 4. Traverse all files, drop invalid files, move xattr files into the
corresponding host file's subtree. Invalid files such as:
a) File has no inode node or inode nlink is zero
b) Non-consistent file types between inode node and dentry nodes
c) File has no dentry nodes(excepts '/')
d) Encrypted file has no xattr information
e) Non regular file has data nodes
f) Directory/xattr file has more than one dentries
g) Xattr file has no host inode, or the host inode is a xattr
h) Non-xattr file's parent is not a directory
i) etc.
Step 5. Extract reachable directory entries tree. Make sure that all files can
be searched from '/', unreachable file is deleted. Since all xattr
files are attached to the corresponding host file, only non-xattr
files should be checked. Luckily, directory file only has one dentry,
the reachable checking of a dentry becomes easy. Traverse all
dentries for each file, check whether the dentry is reachable, if not,
remove dentry from the file. If the file has no dentries, the file is
unreachable.
Step 6. Correct the file information. Traverse all files and calculate
information(nlink, size, xattr_cnt, etc.) for each file just like
check_leaf(in linux kernel) does, correct the inode node based on the
calculated information.
Step 7. Record used LEBs. Traverse all files'(including effective nodes from
deletion trees in step 2) position, after this step fsck knows which
LEB is empty.
Step 8. Re-write data. Read data from LEB and write back data, make sure that
all LEB is ended with empty data(0xFF). It will prevent failed gc
scanning in the next mounting.
Step 9. Build TNC. Construct TNC according to all files' nodes, just like mkfs
does(refer to add_to_index in mkfs), then write TNC(refer to
write_index in mkfs) on flash. (If there are no files, create a new
root dir file.)
Step 10.Build LPT. Construct LPT according to all nodes' position and length,
just like mkfs does, then write LPT(refer to write_lpt) on flash.
Step 11.Clean up log area and orphan area. Log area and orphan area can be
erased.
Step 12.Write master node. Since all meta areas are ready, master node can be
updated.
B. Non-rebuild mode:
Step 1. Read master & init lpt.
a) Scan master nodes failed or master node is invalid (which is not
caused by invalid space statistics), danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild mode, other modes
will exit. Fsck cannot find the right TNC/LPT if the master node is
invalid, which affects subsequent steps, so this problem must be
fixed.
b) Invalid space statistics in master node, set %FR_LPT_INCORRECT for
for lpt status and ignore the error.
c) LPT node is corrupted, set %FR_LPT_CORRUPTED for lpt status and
ignore the error.
Step 2. Replay journal.
I. Scan log LEBs to get all buds.
a) Nodes in log LEBs are invalid/corrupted, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to rebuild
mode, other modes will exit. Corrupted log LEB could fail
ubifs_consolidate_log, which may lead to commit failure by out of
space in the log area, so this problem must be fixed.
II. Scan bud LEBs to get all nodes.
a) Nodes in bud LEBs are invalid/corrupted, danger mode and normal
mode with 'yes' answer will drop bud LEB and set
%FR_LPT_INCORRECT for lpt status, other modes will exit.
Corrupted LEB will make gc failed, so this problem must be
fixed.
III. Record isize into size tree according to data/truncation/inode
nodes.
IV. Apply nodes to TNC & LPT, update property for bud LEBs.
a) Corrupted/Invalid node searched from TNC, skip node and set
%FR_LPT_INCORRECT in lpt status for danger mode and normal mode
with 'yes' answer, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
b) Corrupted/Invalid index node read from TNC, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
c) Corrupted/Invalid lpt node, Set %FR_LPT_CORRUPTED for lpt status
and ignore the error.
d) Incorrect LEB property: Set %FR_LPT_INCORRECT for lpt status and
ignore the error.
e) If lpt status is not empty, skip updating lpt, because incorrect
LEB property could trigger assertion failure in ubifs_change_lp.
Step 3. Handle orphan nodes.
I. Scan orphan LEB to get all orphan nodes.
a) Corrupted/Invalid orphan node: danger mode and normal mode with
'yes' answer will drop orphan LEB, other modes will exit.
Corrupted orphan area could lead to mounting/committing failure,
so this problem must be fixed.
II. Parse orphan node, find the original inode for each inum.
a) Corrupted/Invalid node searched from TNC, skip node for danger
mode and normal mode with 'yes' answer, other modes will exit.
b) Corrupted/Invalid index node read from TNC, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
III. Remove inode for each inum, update TNC & LPT.
a) Corrupted/Invalid node searched from TNC, skip node for danger
mode and normal mode with 'yes' answer, other modes will exit.
b) Corrupted/Invalid index node read from TNC, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
c) Corrupted/Invalid lpt node, Set %FR_LPT_CORRUPTED for lpt
status and ignore the error.
d) Incorrect LEB property: Set %FR_LPT_INCORRECT for lpt status
and ignore the error.
e) If lpt status is not empty, skip updating lpt, because
incorrect LEB property could trigger assertion failure in
ubifs_change_lp.
Step 4. Consolidate log area.
a) Corrupted data in log LEBs, danger mode with rebuild_fs and normal
mode with 'yes' answer will turn to rebuild filesystem, other modes
will exit. It could make commit failed by out of space in log area,
so this problem must be fixed.
Step 5. Recover isize.
I. Traverse size tree, lookup corresponding inode from TNC.
a) Corrupted/Invalid node searched from TNC, skip node for danger
mode and normal mode with 'yes' answer, other modes will exit.
b) Corrupted/Invalid index node read from TNC, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
II. Update isize for inode. Keep <inum, isize> in size tree for check
mode, remove <inum, isize> from the size tree and update inode
node in place for other modes.
Step 6. Traverse TNC and construct files.
I. Traverse TNC, check whether the leaf node is valid, remove invalid
nodes, construct file for valid node and insert the file into the
file tree.
a) Corrupted/Invalid node searched from TNC, remove corresponding
TNC branch for danger mode and normal mode with 'yes' answer,
other modes will exit. The space statistics depend on a valid
TNC, so this problem must be fixed.
b) Corrupted/Invalid index node read from TNC, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. The space statistics
depend on a valid TNC, so this problem must be fixed.
II. Scan all LEBs(contain TNC) for non check mode(unclean LEBs cannot
be fixed in read-only mode, so scanning may fail in check mode,
then space statistics won't be checked in check mode), remove TNC
branch which points to corrupted LEB.
a) Corrupted data is found by scanning. If the current node is
index node, danger mode with rebuild_fs and normal mode with
'yes' answer will turn to rebuild filesystem, other modes will
exit; If the current node is non-index node, danger mode and
normal mode with 'yes' answer will remove all TNC branches which
point to the corrupted LEB, other modes will exit. The space
statistics depend on valid LEB scanning, so this problem must
be fixed.
b) LEB contains both index and non-index nodes, danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to
rebuild filesystem, other modes will exit. Invalid LEB will make
gc failed, so this problem must be fixed.
Step 7. Update files' size for check mode. Update files' size according to the
size tree for check mode.
Step 8. Check and handle invalid files. Similar to rebuild mode, but the
methods of handling are different:
a) Move unattached(file has no dentries) regular file into disconnected
list for safe mode, danger mode and normal mode with 'yes' answer,
let subsequent steps to handle them with lost+found. Other modes
will exit. Disconnected file affects the result of calculated
information(which will be used in subsequent steps) for its' parent
file(eg. nlink, size), so this problem must be fixed.
b) Make file type be consistent between inode, detries and data nodes
by deleting dentries or data nodes, for danger mode and normal mode
with 'yes' answer, other modes will exit.
c) Delete file for other invalid cases(eg. file has no inode) in
danger mode and normal mode with 'yes' answer, other modes will
exit.
Step 9. Extract reachable directory entries tree. Similar to rebuild mode, but
the methods of handling are different:
a) Remove unreachable dentry for danger mode and normal mode with 'yes'
answer, other modes will exit. Unreachable dentry affects the
calculated information(which will be used in subsequent steps) for
its' file(eg. nlink), so this problem must be fixed.
b) Delete unreachable non-regular file for danger mode and normal mode
with 'yes' answer, other modes will exit. Unreachable file affects
the calculated information(which will be used in subsequent steps)
for its' parent file(eg. nlink, size), so this problem must be
fixed.
c) Move unreachable regular file into disconnected list for safe mode,
danger mode and normal mode with 'yes' answer, let subsequent steps
to handle them with lost+found. Other modes will exit. Disconnected
file affects the calculated information(which will be used in
subsequent steps) for its' parent file(eg. nlink, size), so this
problem must be fixed.
Step 10.Correct the file information. Similar to rebuild mode, but the methods
of handling are different:
a) Correct the file information for safe mode, danger mode and normal
mode with 'yes' answer, other modes will exit. Incorrect file
information affects the new creations(which will be used in handling
lost+found), so this problem must be fixed.
Step 11.Check whether the TNC is empty. Empty TNC is equal to corrupted TNC,
which means that zero child count for root znode. If TNC is empty(All
nodes are invalid and are deleted from TNC), turn to rebuild mode for
danger mode with rebuild_fs and normal mode with 'yes' answer, other
modes will exit.
Step 12.Check and correct the space statistics.
I. Exit for check mode, if %FR_LPT_CORRUPTED or %FR_LPT_INCORRECT is
set in lpt status, the exit code should have %FSCK_UNCORRECTED.
II. Check lpt status, if %FR_LPT_CORRUPTED is set in lpt status, normal
mode with 'no' answer will exit, other modes will rebuild lpt. New
creations could be done in subsequent steps, which depends on
correct space statistics, so this problem must be fixed.
III. Traverse LPT nodes, check the correctness of nnode and pnode,
compare LEB scanning result with LEB properties.
a) LPT node is corrupted, normal mode with 'no' answer will exit,
rebuild lpt for other modes. New creations could be done in
subsequent steps, which depends on the correct space
statistics, so this problem must be fixed.
b) Incorrect nnode/pnode, normal mode with 'no' answer will exit,
other other modes will correct the nnode/pnode. New creations
could be done in subsequent steps, which depends on correct
space statistics, so this problem must be fixed.
c) Inconsistent comparing result, normal mode with 'no' answer
will exit, other modes will correct the space statistics. New
creations could be done in subsequent steps, which depends on
correct space statistics, so this problem must be fixed.
IV. Compare LPT area scanning result with lprops table information.
a) LPT area is corrupted, normal mode with 'no' answer will exit,
rebuild lpt for other modes. Commit could fail in doing LPT gc
caused by scanning corrupted data, so this problem must be
fixed.
b) Inconsistent comparing result, normal mode with 'no' answer
will exit, other modes will correct the lprops table
information. Commit could fail in writing LPT with %ENOSPC
return code caused by incorrect space statistics in the LPT
area, so this problem must be fixed.
Step 13.Do commit, commit problem fixing modifications to disk. The index size
checking depends on this step.
Step 14.Check and correct the index size. Check and correct the index size by
traversing TNC just like dbg_check_idx_size does. This step should be
executed after first committing, because 'c->calc_idx_sz' can be
changed in 'ubifs_tnc_start_commit' and the initial value of
'c->calc_idx_sz' read from the disk is untrusted. Correct the index
size for safe mode, danger mode and normal mode with 'yes' answer,
other modes will exit. New creations could be done in subsequent steps,
which depends on the correct index size, so this problem must be fixed.
Step 15.Check and create the root dir. Check whether the root dir exists,
create a new one if it is not found, for safe mode, danger mode and
normal mode with 'yes' answer, other modes will exit. Mounting depends
on the root dir, so this problem must be fixed.
Step 16.Check and create the lost+found.
I. If the root dir is encrypted, set lost+found as invalid. Because it
is impossible to check whether the lost+found exists in an encrypted
directory.
II. Search the lost+found under root dir.
a) Found a lost+found, lost+found is a non-encrypted directory, set
lost+found as valid, otherwise set lost+found as invalid.
b) Not found the lost+found, create a new one. If creation is
failed by %ENOSPC, set lost+found as invalid.
Step 17.Handle each file from the disconnected list.
I. If lost+found is invalid, delete file for danger mode and normal
mode with 'yes' answer, other modes will skip and set the exit code
with %FSCK_UNCORRECTED.
II. If lost+found is valid, link disconnected file under lost+found
directory with the name of the corresponding inode number
(INO_<inum>_<index>, index(starts from 0) is used to handle the
conflicted names).
a) Fails in handling conflicted file names, delete file for danger
mode and normal mode with 'yes' answer, other modes will skip
and set the exit code with %FSCK_UNCORRECTED.
b) Fails in linking caused by %ENOSPC, delete file for danger mode
and normal mode with 'yes' answer, other modes will skip and set
the exit code with %FSCK_UNCORRECTED.
Step 18.Do final commit, commit problem fixing modifications to disk and clear
%UBIFS_MST_DIRTY flag for master node.
Advantages
==========
1. Can be used for any UBIFS image, fsck has nothing to do with kernel version.
2. Fsck is tolerant with power-cut, fsck will always succeed in a certain mode
without changing mode even power-cut happens in checking and repairing. In
other words, fsck won't let UBIFS image become worse in abnormal situations.
3. It is compatible with FSCK(8), the exit code returned by fsck.ubifs is same
as FSCK, the command options used by fsck are supported in fsck.ubifs too.
4. The UBIFS image can be fixed as long as the super block is not corrupted.
5. Encrypted UBIFS image is supported, because dentry name and data content of
file are not necessary for fsck.
Limitations
===========
1. UBIFS image file is not supported(Not like ext4). The UBIFS image file is
not equal to UBI volume, empty LEBs are not included in image file, so UBIFS
cannot allocate empty space when file recovering is needed. Another reason
is that atomic LEB changing is not supported by image file.
2. Authenticated UBIFS image is not supported, UBIFS metadata(TNC/LPT) parsing
depends on the authentication key which is not supported in fsck options.
Testing
=======
We have seven testcases for fsck.ubifs on encryption/non-encryption
situations(See details in tests/ubifs_tools-tests/README.txt):
1) authentication_refuse: Currently authenticated UBIFS image is not
supported for fsck.ubifs.
2) random_corrupted_fsck: Inject random corruption on UBIFS image
by writting random data on kinds of mtd devices (eg. nand, nor),
check the consistency of UBIFS after fsck.
This testcase simulate random bad UBIFS image caused by hardware
exceptions(eg. ecc uncorrectable, unwritten), and makes sure that
fsck.ubifs could make UBIFS be consistent after repairing UBIFS
image.
3) cycle_corrupted_fsck_fault_inject: Inject memory/io fault while
doing fsck for corrupted UBIFS images.
This testcase mainly checks whether fsck.ubifs has problems (eg.
UAF, null-ptr-def, etc.) in random error paths. Besides, it
provides a similar way to simulate powercut during fsck, and
checks whether the fsck.ubifs can fix an UBIFS image after many
rounds interrupted by kinds of errors.
I have run this case with valgrind[17] in my local machine to
check kinds of memory errors(eg. rw OOB, null-ptr-def).
4) cycle_powercut_mount_fsck: Inject powercut while doing fsstress
on mounted UBIFS, check the consistency of UBIFS after fsck.
This testscase mainly makes sure that fsck.ubifs can make UBIFS
image be consistent in common stress cases and powercut cases.
5) powercut_fsck_mount: Inject powercut while doing fsstress on
mounted UBIFS for kinds of flashes (eg. nand, nor).
This testcase mainly makes sure that fsck.ubifs can make UBIFS
image be consistent on different flashes (eg. nand, nor). Because
the min_io_size of nor flash is 1, the UBIFS image on nor flash
will be different from nand flash after doing powercut, so we need
make sure fsck.ubifs can handle these two types of flash.
6) cycle_mount_fsck_check: Do fsstress and fsck ubifs image, make
sure all files(and their data) are not lost after fsck.
This testcase mainly checks whether fsck.ubifs could corrupt the
filesystem content in common case.
7) fsck_bad_image: For kinds of inconsistent UBIFS images(which
can simulate corruptions caused by some potentional UBIFS bug),
check the result of fsck.
This testcase mainly checks whether the behavior is in expected
after repairing specific inconsistent UBIFS image.
There is one testcase for mkfs.ubifs on encryption/non-encryption
situations:
1) build_fs_from_dir: Initialize UBIFS image from a given directory,
then check whether the fs content in mounted UBIFS is consistent
with the original directory. Both UBI volume and file are chosen as
storage mediums to test. This testcase mainly ensures that
mkfs.ubifs can format an UBIFS image as user expected.
Besides, I fix all corrupted UBIFS images(history problems in our
product line) by fsck.ubifs.
Environment: qemu, -smp 4, -m 16384/32768, nandsim/mtdram
Code coverage[18]:
fsck - Line 87.4%, functions 98.0%
libubifs - Line 78.4%, functions 89.1%
mkfs - Line 71.6%, functions 97.8%
Notice
------
Before running testcases, please make sure that your linux kernel is
updated to the latest version(v6.9), and following patches shoule be
applied, otherwise some of testcases may fail.
P1: https://lore.kernel.org/linux-mtd/[email protected]/
P2: https://lore.kernel.org/linux-mtd/[email protected]/
P3: https://lore.kernel.org/linux-mtd/[email protected]/
Patches
=======
1~15: Some bugfixes and cleanups for mkfs.ubifs, split common libs into
'common' directory.
16: Import linux kernel implementations into ubifs-utils/libubifs.
Prepare to replace implementations of UBIFS with linux kernel.
17~32: Add some basic libs(bitops, mutex, rwsem, etc.) which will be used
by libubifs.
33: Add README for common directory to describe where these basic
libs come from.
34~56: Adapt libubifs to userspace, delete codes which won't be used by
fsck/mkfs.
57~60: Add basic framework fsck.
61~75: Implement rebuild mode for fsck.
76~97: Implement other modes(normal, danger, check) for fsck.
98: Add docs for fsck.
99~110:Add testcases for fsck/mkfs.
Links
=====
[1] https://lore.kernel.org/linux-mtd/[email protected]/
[2] https://lore.kernel.org/linux-mtd/CAMxq0fNSWrUFMmmTs8Ri9gFOvS+KQJvZN3-_KuiqXi9bbmCB0Q@mail.gmail.com/
[3] https://lore.kernel.org/linux-mtd/[email protected]/
[4] https://lore.kernel.org/linux-mtd/[email protected]/T/#u
[5] https://lore.kernel.org/linux-mtd/[email protected]/
[6] https://lore.kernel.org/linux-mtd/[email protected]/
[7] https://lore.kernel.org/linux-mtd/[email protected]/
[8] https://lore.kernel.org/linux-mtd/[email protected]/
[9] https://lore.kernel.org/linux-mtd/[email protected]/
[10] https://lore.kernel.org/linux-mtd/[email protected]/
[11] https://lore.kernel.org/linux-mtd/[email protected]/
[12] https://lore.kernel.org/linux-mtd/[email protected]/
[13] https://lore.kernel.org/linux-mtd/[email protected]/
[14] https://linux-mtd.infradead.narkive.com/bfcHzD0j/ubi-ubifs-corruptions-during-random-power-cuts
[15] https://lore.kernel.org/linux-mtd/[email protected]/T/#u
[16] https://lore.kernel.org/linux-mtd/[email protected]/
[17] https://valgrind.org/
[18] https://bugzilla.kernel.org/show_bug.cgi?id=218924
Huang Xiaojia (1):
fsck.ubifs: Check and create the lost+found
Xiang Yang (1):
fsck.ubifs: Check and correct files' information
Zhihao Cheng (108):
mkfs.ubifs: Clear direct_write property when closing target
mkfs.ubifs: Initialize 'cipher_name' as NULL
ubifs-utils: Split common source files from mkfs.ubifs
ubifs-utils: Decouple mkfs.ubifs.h out of other modules
ubifs-utils: Define PROGRAM_NAME as variable
ubifs-utils: Clean up error message printing functions
ubifs-utils: Move 'debug_level' into ubifs_info structure
mkfs.ubifs: Fix wrong xattr entry type
mkfs.ubifs: Fix incorrect dir size calculation in encryption scenario
mkfs.ubifs: Close libubi in error handling paths
mkfs.ubifs: Fix missed closing out_fd
mkfs.ubifs: Fix memleak for 'output' in error paths
ubifs-utils: Add 'dev_name' into 'ubifs_info' structure
ubifs-utils: Add 'dev_fd' and 'libubi' into 'ubifs_info' structure
ubifs-utils: Extract UBI opening/closing/volume_check_empty functions
into a new source file
ubifs-utils: Import UBIFS libs from linux kernel
mtd-utils: Extract list implementation to common lib and add list_sort
support
mtd-utils: Extract rbtree implementation to common lib
ubifs-utils: Add compiler attributes implementations
ubifs-utils: Add linux type definitions
ubifs-utils: Add linux kernel error conversion definitions
ubifs-utils: Add linux kernel memory allocation implementations
ubifs-utils: Add atomic implementations
ubifs-utils: Add bit operations implementations
ubifs-utils: Add spinlock implementations
ubifs-utils: Add mutexlock implementations
ubifs-utils: Add rwsem implementations
ubifs-utils: Add sorting implementations
ubifs-utils: Add implementations for linux kernel printing functions
ubifs-utils: Add linux hexdump implementations lib
libubi: Add new interface ubi_leb_map()
ubifs-utils: Add common definitions in linux kernel
ubifs-utils: Add descriptions for new lib files in common/README
ubifs-utils: Adapt ubifs header file in libubifs
ubifs-utils: Adapt super.c in libubifs
ubifs-utils: Adapt io.c in libubifs
ubifs-utils: Adapt lpt subsystem in libubifs
ubifs-utils: Adapt tnc subsystem in libubifs
ubifs-utils: Adapt log.c in libubifs
ubifs-utils: Adapt recovery subsystem in libubifs
ubifs-utils: Adapt sb.c in libubifs
ubifs-utils: Adapt auth.c in libubifs
ubifs-utils: Adapt dir.c in libubifs
ubifs-utils: Adapt journal.c in libubifs
ubifs-utils: Adapt budget.c in libubifs
ubifs-utils: Adapt commit.c in libubifs
ubifs-utils: Adapt debug subsystem in libubifs
ubifs-utils: Adapt key.h in libubifs
ubifs-utils: Adapt master.c in libubifs
ubifs-utils: Adapt misc.h in libubifs
ubifs-utils: Adapt orphan.c in libubifs
ubifs-utils: Adapt gc subsystem in libubifs
ubifs-utils: Move ubifs-media.h in libubifs
ubifs-utils: Add descriptions for new lib files in libubifs/README
ubifs-utils: Replace ubifs related source code with linux kernel
implementation
ubifs-utils: open_ubi: Set errno if the target is not char device
fsck.ubifs: Add fsck support
fsck.ubifs: Add inconsistent problem handling asking function
fsck.ubifs: Distinguish reasons when certain failures happen
fsck.ubifs: Load filesystem information from UBI volume
fsck.ubifs: Add node parsing functions
fsck.ubifs: Add file organization realization
fsck.ubifs: Add rebuilding filesystem support
fsck.ubifs: rebuild_fs: Remove deleted nodes from valid node tree
fsck.ubifs: rebuild_fs: Add valid nodes into file
fsck.ubifs: rebuild_fs: Filter invalid files
fsck.ubifs: rebuild_fs: Extract reachable directory entries tree
fsck.ubifs: rebuild_fs: Check and correct files' information
fsck.ubifs: rebuild_fs: Record used LEBs
fsck.ubifs: rebuild_fs: Re-write data
fsck.ubifs: rebuild_fs: Create new root dir if there are no scanned
files
fsck.ubifs: rebuild_fs: Build TNC
fsck.ubifs: rebuild_fs: Build LPT
fsck.ubifs: rebuild_fs: Clean up log and orphan area
fsck.ubifs: rebuild_fs: Write master node
fsck.ubifs: Read master node & init lpt
fsck.ubifs: Replay journal
fsck.ubifs: Handle orphan nodes
fsck.ubifs: Consolidate log
fsck.ubifs: Recover isize
fsck.ubifs: Move common functions and data structures into
fsck.ubifs.c
fsck.ubifs: Traverse TNC and construct files
fsck.ubifs: Ensure that TNC LEB can be scanned successful
fsck.ubifs: Update files' size for check mode
fsck.ubifs: Check and handle invalid files
fsck.ubifs: Check and handle unreachable files
fsck.ubifs: Check whether the TNC is empty
fsck.ubifs: Move common functions and data structures into
check_space.c
fsck.ubifs: check and correct the space statistics
fsck.ubifs: Commit problem fixing modifications to disk
fsck.ubifs: Check and correct the index size
ubifs-utils: libubifs: Support some file operations
fsck.ubifs: Check and create the root dir
fsck.ubifs: Handle disconnected files
fsck.ubifs: Do final committing
fsck.ubifs: Add README to describe fsck
tests: Add common libs for testing fsck.ubifs/mkfs.ubifs
tests: ubifs_tools: fsck_tests: Add authentication refusing test
tests: ubifs_tools: fsck_tests: Add cycle mount+fsck test
tests: ubifs_tools: fsck_tests: Add powercut+fsck+mount test
tests: ubifs_tools: fsck_tests: Add corrupt+fsck+fault_inject test
tests: ubifs_tools: fsck_tests: Add cycle_powercut+fsck test
tests: ubifs_tools: fsck_tests: Add random_corrupt+fsck test
tests: ubifs_tools: fsck_tests: Add corrupted images
tests: ubifs_tools: fsck_tests: Add bad images fixing test
tests: ubifs_tools: mkfs_tests: Add fs content check test
tests: ubifs_tools: Add run_all script
tests: ubifs_tools: Add README
.gitignore | 11 +
Makefile.am | 2 +
configure.ac | 12 +-
include/crc32.h | 5 +
include/libubi.h | 15 +
include/list.h | 263 ++
{jffsX-utils => include}/rbtree.h | 32 +
jffsX-utils/Makemodule.am | 7 +-
jffsX-utils/compr.c | 49 -
jffsX-utils/compr.h | 5 +-
lib/Makemodule.am | 4 +
lib/libubi.c | 10 +
lib/list_sort.c | 246 ++
{jffsX-utils => lib}/rbtree.c | 38 +
tests/ubifs_tools-tests/Makemodule.am | 66 +
tests/ubifs_tools-tests/README.txt | 303 ++
.../fsck_tests/authentication_refuse.sh.in | 66 +
.../cycle_corrupted_fsck_fault_inject.sh.in | 225 ++
.../fsck_tests/cycle_mount_fsck_check.sh.in | 144 +
.../fsck_tests/cycle_powercut_mount_fsck.sh.in | 144 +
.../fsck_tests/fsck_bad_image.sh.in | 355 +++
.../fsck_tests/powercut_fsck_mount.sh.in | 144 +
.../fsck_tests/random_corrupted_fsck.sh.in | 206 ++
.../ubifs_tools-tests/images/corrupted_data_leb.gz | Bin 0 -> 9536 bytes
.../ubifs_tools-tests/images/corrupted_idx_leb.gz | Bin 0 -> 5082 bytes
tests/ubifs_tools-tests/images/dentry_key.gz | Bin 0 -> 5088 bytes
tests/ubifs_tools-tests/images/dentry_nlen.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/dentry_type.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/dir_lost.gz | Bin 0 -> 5088 bytes
.../images/dir_lost_duplicated.gz | Bin 0 -> 5347 bytes
.../images/dir_lost_not_recover.gz | Bin 0 -> 5396 bytes
tests/ubifs_tools-tests/images/dir_many_dentry.gz | Bin 0 -> 5114 bytes
tests/ubifs_tools-tests/images/empty_tnc.gz | Bin 0 -> 4954 bytes
tests/ubifs_tools-tests/images/good.gz | Bin 0 -> 4960 bytes
tests/ubifs_tools-tests/images/index_size.gz | Bin 0 -> 5070 bytes
tests/ubifs_tools-tests/images/inode_data.gz | Bin 0 -> 5015 bytes
tests/ubifs_tools-tests/images/inode_mode.gz | Bin 0 -> 5109 bytes
tests/ubifs_tools-tests/images/inode_nlink.gz | Bin 0 -> 5110 bytes
tests/ubifs_tools-tests/images/inode_size.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/inode_xcnt.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/journal_bud.gz | Bin 0 -> 5015 bytes
tests/ubifs_tools-tests/images/journal_log.gz | Bin 0 -> 4927 bytes
tests/ubifs_tools-tests/images/lpt_dirty.gz | Bin 0 -> 5056 bytes
tests/ubifs_tools-tests/images/lpt_flags.gz | Bin 0 -> 5060 bytes
tests/ubifs_tools-tests/images/lpt_free.gz | Bin 0 -> 5046 bytes
tests/ubifs_tools-tests/images/lpt_pos.gz | Bin 0 -> 5070 bytes
tests/ubifs_tools-tests/images/ltab_dirty.gz | Bin 0 -> 5104 bytes
tests/ubifs_tools-tests/images/ltab_free.gz | Bin 0 -> 5072 bytes
.../images/master_highest_inum.gz | Bin 0 -> 4813 bytes
tests/ubifs_tools-tests/images/master_lpt.gz | Bin 0 -> 4808 bytes
tests/ubifs_tools-tests/images/master_tnc.gz | Bin 0 -> 4805 bytes
.../ubifs_tools-tests/images/master_total_dead.gz | Bin 0 -> 4817 bytes
.../ubifs_tools-tests/images/master_total_dirty.gz | Bin 0 -> 4814 bytes
.../ubifs_tools-tests/images/master_total_free.gz | Bin 0 -> 4813 bytes
tests/ubifs_tools-tests/images/orphan_node.gz | Bin 0 -> 5379 bytes
tests/ubifs_tools-tests/images/root_dir.gz | Bin 0 -> 5058 bytes
tests/ubifs_tools-tests/images/sb_fanout.gz | Bin 0 -> 5031 bytes
tests/ubifs_tools-tests/images/sb_fmt_version.gz | Bin 0 -> 5032 bytes
tests/ubifs_tools-tests/images/sb_leb_size.gz | Bin 0 -> 5033 bytes
tests/ubifs_tools-tests/images/sb_log_lebs.gz | Bin 0 -> 5031 bytes
tests/ubifs_tools-tests/images/sb_min_io_size.gz | Bin 0 -> 5035 bytes
.../ubifs_tools-tests/images/soft_link_data_len.gz | Bin 0 -> 5112 bytes
.../images/soft_link_inode_mode.gz | Bin 0 -> 5121 bytes
tests/ubifs_tools-tests/images/tnc_lv0_key.gz | Bin 0 -> 5118 bytes
tests/ubifs_tools-tests/images/tnc_lv0_len.gz | Bin 0 -> 5130 bytes
tests/ubifs_tools-tests/images/tnc_lv0_pos.gz | Bin 0 -> 5118 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_key.gz | Bin 0 -> 5140 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_len.gz | Bin 0 -> 5145 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz | Bin 0 -> 5125 bytes
tests/ubifs_tools-tests/images/xent_host.gz | Bin 0 -> 5108 bytes
tests/ubifs_tools-tests/images/xentry_key.gz | Bin 0 -> 5085 bytes
tests/ubifs_tools-tests/images/xentry_nlen.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/xentry_type.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/xinode_flags.gz | Bin 0 -> 5112 bytes
tests/ubifs_tools-tests/images/xinode_key.gz | Bin 0 -> 5110 bytes
tests/ubifs_tools-tests/images/xinode_mode.gz | Bin 0 -> 5112 bytes
tests/ubifs_tools-tests/lib/common.sh.in | 359 +++
.../mkfs_tests/build_fs_from_dir.sh.in | 174 ++
tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in | 65 +
ubifs-utils/Makemodule.am | 121 +-
ubifs-utils/common/README | 14 +
ubifs-utils/common/atomic.h | 133 +
ubifs-utils/common/bitops.c | 37 +
ubifs-utils/common/bitops.h | 152 +
ubifs-utils/common/compiler_attributes.h | 79 +
ubifs-utils/{mkfs.ubifs => common}/compr.c | 19 +-
ubifs-utils/{mkfs.ubifs => common}/compr.h | 8 -
ubifs-utils/{mkfs.ubifs => common}/crc16.c | 0
ubifs-utils/{mkfs.ubifs => common}/crc16.h | 0
ubifs-utils/{mkfs.ubifs => common}/crypto.c | 6 +-
ubifs-utils/{mkfs.ubifs => common}/crypto.h | 0
ubifs-utils/common/defs.h | 123 +
ubifs-utils/{mkfs.ubifs => common}/devtable.c | 94 +-
.../{mkfs.ubifs/mkfs.ubifs.h => common/devtable.h} | 85 +-
ubifs-utils/{mkfs.ubifs => common}/fscrypt.c | 29 +-
ubifs-utils/{mkfs.ubifs => common}/fscrypt.h | 19 +-
.../{mkfs.ubifs => common}/hashtable/hashtable.c | 10 +-
.../{mkfs.ubifs => common}/hashtable/hashtable.h | 0
.../hashtable/hashtable_itr.c | 0
.../hashtable/hashtable_itr.h | 0
.../hashtable/hashtable_private.h | 0
ubifs-utils/common/hexdump.c | 218 ++
ubifs-utils/common/kmem.c | 64 +
ubifs-utils/common/kmem.h | 56 +
ubifs-utils/common/linux_err.h | 62 +
ubifs-utils/common/linux_types.h | 92 +
ubifs-utils/common/mutex.h | 18 +
ubifs-utils/common/rwsem.h | 19 +
ubifs-utils/{mkfs.ubifs => common}/sign.c | 177 +-
ubifs-utils/common/sign.h | 39 +
ubifs-utils/common/sort.c | 274 ++
ubifs-utils/common/sort.h | 20 +
ubifs-utils/common/spinlock.h | 14 +
ubifs-utils/fsck.ubifs/.gitignore | 1 +
ubifs-utils/fsck.ubifs/README.txt | 388 +++
ubifs-utils/fsck.ubifs/check_files.c | 555 ++++
ubifs-utils/fsck.ubifs/check_space.c | 686 +++++
ubifs-utils/fsck.ubifs/extract_files.c | 1574 ++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 636 ++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 393 +++
ubifs-utils/fsck.ubifs/handle_disconnected.c | 196 ++
ubifs-utils/fsck.ubifs/load_fs.c | 261 ++
ubifs-utils/fsck.ubifs/problem.c | 377 +++
ubifs-utils/fsck.ubifs/rebuild_fs.c | 1453 +++++++++
ubifs-utils/libubifs/README | 30 +
ubifs-utils/libubifs/auth.c | 175 ++
ubifs-utils/libubifs/budget.c | 595 ++++
ubifs-utils/libubifs/commit.c | 383 +++
ubifs-utils/libubifs/debug.c | 1033 +++++++
ubifs-utils/libubifs/debug.h | 173 ++
ubifs-utils/libubifs/dir.c | 390 +++
ubifs-utils/libubifs/find.c | 970 +++++++
ubifs-utils/libubifs/gc.c | 1021 +++++++
ubifs-utils/libubifs/io.c | 1088 +++++++
ubifs-utils/libubifs/journal.c | 633 ++++
ubifs-utils/libubifs/key.h | 492 ++++
ubifs-utils/libubifs/log.c | 750 +++++
ubifs-utils/libubifs/lprops.c | 864 ++++++
ubifs-utils/libubifs/lpt.c | 2337 +++++++++++++++
ubifs-utils/libubifs/lpt_commit.c | 1812 ++++++++++++
ubifs-utils/libubifs/master.c | 489 ++++
ubifs-utils/libubifs/misc.h | 225 ++
ubifs-utils/libubifs/orphan.c | 644 ++++
ubifs-utils/libubifs/recovery.c | 1404 +++++++++
ubifs-utils/libubifs/replay.c | 1229 ++++++++
ubifs-utils/libubifs/sb.c | 512 ++++
ubifs-utils/libubifs/scan.c | 372 +++
ubifs-utils/libubifs/super.c | 702 +++++
ubifs-utils/libubifs/tnc.c | 3070 ++++++++++++++++++++
ubifs-utils/libubifs/tnc_commit.c | 1117 +++++++
ubifs-utils/libubifs/tnc_misc.c | 452 +++
.../mtd => ubifs-utils/libubifs}/ubifs-media.h | 0
ubifs-utils/libubifs/ubifs.h | 1924 ++++++++++++
ubifs-utils/mkfs.ubifs/README | 9 -
ubifs-utils/mkfs.ubifs/defs.h | 90 -
ubifs-utils/mkfs.ubifs/key.h | 222 --
ubifs-utils/mkfs.ubifs/lpt.c | 590 ----
ubifs-utils/mkfs.ubifs/lpt.h | 28 -
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 643 ++--
ubifs-utils/mkfs.ubifs/sign.h | 80 -
ubifs-utils/mkfs.ubifs/ubifs.h | 471 ---
161 files changed, 36227 insertions(+), 2244 deletions(-)
create mode 100644 include/list.h
rename {jffsX-utils => include}/rbtree.h (79%)
create mode 100644 lib/list_sort.c
rename {jffsX-utils => lib}/rbtree.c (90%)
create mode 100644 tests/ubifs_tools-tests/Makemodule.am
create mode 100644 tests/ubifs_tools-tests/README.txt
create mode 100755 tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh.in
create mode 100755 tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh.in
create mode 100644 tests/ubifs_tools-tests/images/corrupted_data_leb.gz
create mode 100644 tests/ubifs_tools-tests/images/corrupted_idx_leb.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_key.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_nlen.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_type.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost_duplicated.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost_not_recover.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_many_dentry.gz
create mode 100644 tests/ubifs_tools-tests/images/empty_tnc.gz
create mode 100644 tests/ubifs_tools-tests/images/good.gz
create mode 100644 tests/ubifs_tools-tests/images/index_size.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_data.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_mode.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_nlink.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_size.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_xcnt.gz
create mode 100644 tests/ubifs_tools-tests/images/journal_bud.gz
create mode 100644 tests/ubifs_tools-tests/images/journal_log.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_flags.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_free.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/ltab_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/ltab_free.gz
create mode 100644 tests/ubifs_tools-tests/images/master_highest_inum.gz
create mode 100644 tests/ubifs_tools-tests/images/master_lpt.gz
create mode 100644 tests/ubifs_tools-tests/images/master_tnc.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_dead.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_free.gz
create mode 100644 tests/ubifs_tools-tests/images/orphan_node.gz
create mode 100644 tests/ubifs_tools-tests/images/root_dir.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_fanout.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_fmt_version.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_leb_size.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_log_lebs.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_min_io_size.gz
create mode 100644 tests/ubifs_tools-tests/images/soft_link_data_len.gz
create mode 100644 tests/ubifs_tools-tests/images/soft_link_inode_mode.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_key.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_len.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_key.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_len.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/xent_host.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_key.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_nlen.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_type.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_flags.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_key.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_mode.gz
create mode 100755 tests/ubifs_tools-tests/lib/common.sh.in
create mode 100755 tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh.in
create mode 100755 tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in
create mode 100644 ubifs-utils/common/README
create mode 100644 ubifs-utils/common/atomic.h
create mode 100644 ubifs-utils/common/bitops.c
create mode 100644 ubifs-utils/common/bitops.h
create mode 100644 ubifs-utils/common/compiler_attributes.h
rename ubifs-utils/{mkfs.ubifs => common}/compr.c (95%)
rename ubifs-utils/{mkfs.ubifs => common}/compr.h (91%)
rename ubifs-utils/{mkfs.ubifs => common}/crc16.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crc16.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crypto.c (98%)
rename ubifs-utils/{mkfs.ubifs => common}/crypto.h (100%)
create mode 100644 ubifs-utils/common/defs.h
rename ubifs-utils/{mkfs.ubifs => common}/devtable.c (84%)
rename ubifs-utils/{mkfs.ubifs/mkfs.ubifs.h => common/devtable.h} (53%)
rename ubifs-utils/{mkfs.ubifs => common}/fscrypt.c (90%)
rename ubifs-utils/{mkfs.ubifs => common}/fscrypt.h (91%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable.c (99%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_itr.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_itr.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_private.h (100%)
create mode 100644 ubifs-utils/common/hexdump.c
create mode 100644 ubifs-utils/common/kmem.c
create mode 100644 ubifs-utils/common/kmem.h
create mode 100644 ubifs-utils/common/linux_err.h
create mode 100644 ubifs-utils/common/linux_types.h
create mode 100644 ubifs-utils/common/mutex.h
create mode 100644 ubifs-utils/common/rwsem.h
rename ubifs-utils/{mkfs.ubifs => common}/sign.c (66%)
create mode 100644 ubifs-utils/common/sign.h
create mode 100644 ubifs-utils/common/sort.c
create mode 100644 ubifs-utils/common/sort.h
create mode 100644 ubifs-utils/common/spinlock.h
create mode 100644 ubifs-utils/fsck.ubifs/.gitignore
create mode 100644 ubifs-utils/fsck.ubifs/README.txt
create mode 100644 ubifs-utils/fsck.ubifs/check_files.c
create mode 100644 ubifs-utils/fsck.ubifs/check_space.c
create mode 100644 ubifs-utils/fsck.ubifs/extract_files.c
create mode 100644 ubifs-utils/fsck.ubifs/fsck.ubifs.c
create mode 100644 ubifs-utils/fsck.ubifs/fsck.ubifs.h
create mode 100644 ubifs-utils/fsck.ubifs/handle_disconnected.c
create mode 100644 ubifs-utils/fsck.ubifs/load_fs.c
create mode 100644 ubifs-utils/fsck.ubifs/problem.c
create mode 100644 ubifs-utils/fsck.ubifs/rebuild_fs.c
create mode 100644 ubifs-utils/libubifs/README
create mode 100644 ubifs-utils/libubifs/auth.c
create mode 100644 ubifs-utils/libubifs/budget.c
create mode 100644 ubifs-utils/libubifs/commit.c
create mode 100644 ubifs-utils/libubifs/debug.c
create mode 100644 ubifs-utils/libubifs/debug.h
create mode 100644 ubifs-utils/libubifs/dir.c
create mode 100644 ubifs-utils/libubifs/find.c
create mode 100644 ubifs-utils/libubifs/gc.c
create mode 100644 ubifs-utils/libubifs/io.c
create mode 100644 ubifs-utils/libubifs/journal.c
create mode 100644 ubifs-utils/libubifs/key.h
create mode 100644 ubifs-utils/libubifs/log.c
create mode 100644 ubifs-utils/libubifs/lprops.c
create mode 100644 ubifs-utils/libubifs/lpt.c
create mode 100644 ubifs-utils/libubifs/lpt_commit.c
create mode 100644 ubifs-utils/libubifs/master.c
create mode 100644 ubifs-utils/libubifs/misc.h
create mode 100644 ubifs-utils/libubifs/orphan.c
create mode 100644 ubifs-utils/libubifs/recovery.c
create mode 100644 ubifs-utils/libubifs/replay.c
create mode 100644 ubifs-utils/libubifs/sb.c
create mode 100644 ubifs-utils/libubifs/scan.c
create mode 100644 ubifs-utils/libubifs/super.c
create mode 100644 ubifs-utils/libubifs/tnc.c
create mode 100644 ubifs-utils/libubifs/tnc_commit.c
create mode 100644 ubifs-utils/libubifs/tnc_misc.c
rename {include/mtd => ubifs-utils/libubifs}/ubifs-media.h (100%)
create mode 100644 ubifs-utils/libubifs/ubifs.h
delete mode 100644 ubifs-utils/mkfs.ubifs/README
delete mode 100644 ubifs-utils/mkfs.ubifs/defs.h
delete mode 100644 ubifs-utils/mkfs.ubifs/key.h
delete mode 100644 ubifs-utils/mkfs.ubifs/lpt.c
delete mode 100644 ubifs-utils/mkfs.ubifs/lpt.h
delete mode 100644 ubifs-utils/mkfs.ubifs/sign.h
delete mode 100644 ubifs-utils/mkfs.ubifs/ubifs.h
--
2.13.6
The 'output' is allocated in get_options(), don't forget to free it
in error paths, move 'output' freeing out of close_target(), which
simplifies the logic of close_target().
Fixes: 36ec51948e0ec ("Add mkfs.ubifs")
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 949187ea..f66c6a46 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -2898,8 +2898,6 @@ static int close_target(void)
if (close(out_fd) == -1)
return sys_errmsg("cannot close the target '%s'", output);
}
- if (output)
- free(output);
return 0;
}
@@ -3096,6 +3094,7 @@ int main(int argc, char *argv[])
printf("Success!\n");
out:
+ free(output);
close_ubi();
crypto_cleanup();
return err;
--
2.13.6
Closing 'out_fd' is missed in handling paths in open_target(), fix it
by adding closing operations before returning.
Fixes: a48340c335dab ("mkfs.ubifs: use libubi to format UBI volume")
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 6a4a4588..949187ea 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -2860,12 +2860,16 @@ static int open_target(void)
if (out_fd == -1)
return sys_errmsg("cannot open the UBI volume '%s'",
output);
- if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1))
+ if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
+ close(out_fd);
return sys_errmsg("ubi_set_property(set direct_write) failed");
+ }
if (!yes && check_volume_empty()) {
- if (!prompt("UBI volume is not empty. Format anyways?", false))
+ if (!prompt("UBI volume is not empty. Format anyways?", false)) {
+ close(out_fd);
return errmsg("UBI volume is not empty");
+ }
}
} else {
out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC,
--
2.13.6
Embed new member 'dev_name' into 'ubifs_info' structure, then global
variable 'output' can be removed from mkfs.ubifs.c. Next patches will
import UBIFS libs from linux kernel, which could print messages that
contain device information, so this patch can distinguish different
devices according to messages.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/common/ubifs.h | 2 ++
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 29 ++++++++++++++---------------
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/ubifs-utils/common/ubifs.h b/ubifs-utils/common/ubifs.h
index 58aaba9b..502a39ae 100644
--- a/ubifs-utils/common/ubifs.h
+++ b/ubifs-utils/common/ubifs.h
@@ -277,6 +277,7 @@ struct ubifs_znode
* 2 - files, 3 - more details
* @program_type: used to identify the type of current program
* @program_name: program name
+ * @dev_name: device name
*
* @jhead_cnt: count of journal heads
* @max_bud_bytes: maximum number of bytes allowed in buds
@@ -368,6 +369,7 @@ struct ubifs_info
int debug_level;
int program_type;
const char *program_name;
+ char *dev_name;
int jhead_cnt;
long long max_bud_bytes;
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index f66c6a46..1cafbf3e 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -145,7 +145,6 @@ static char *root;
static int root_len;
static struct fscrypt_context *root_fctx;
static struct stat root_st;
-static char *output;
static int out_fd;
static int out_ubi;
static int squash_owner;
@@ -407,10 +406,10 @@ static int validate_options(void)
{
int tmp;
- if (!output)
+ if (!c->dev_name)
return errmsg("no output file or UBI volume specified");
if (root) {
- tmp = is_contained(output, root);
+ tmp = is_contained(c->dev_name, root);
if (tmp < 0)
return errmsg("failed to perform output file root check");
else if (tmp)
@@ -641,7 +640,7 @@ static int get_options(int argc, char**argv)
return errmsg("bad maximum LEB count");
break;
case 'o':
- output = xstrdup(optarg);
+ c->dev_name = xstrdup(optarg);
break;
case 'D':
tbl_file = optarg;
@@ -835,13 +834,13 @@ static int get_options(int argc, char**argv)
}
}
- if (optind != argc && !output)
- output = xstrdup(argv[optind]);
+ if (optind != argc && !c->dev_name)
+ c->dev_name = xstrdup(argv[optind]);
- if (!output)
+ if (!c->dev_name)
return errmsg("not output device or file specified");
- out_ubi = !open_ubi(output);
+ out_ubi = !open_ubi(c->dev_name);
if (out_ubi) {
c->min_io_size = c->di.min_io_size;
@@ -921,7 +920,7 @@ static int get_options(int argc, char**argv)
printf("\tmin_io_size: %d\n", c->min_io_size);
printf("\tleb_size: %d\n", c->leb_size);
printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt);
- printf("\toutput: %s\n", output);
+ printf("\toutput: %s\n", c->dev_name);
printf("\tjrn_size: %llu\n", c->max_bud_bytes);
printf("\treserved: %llu\n", c->rp_size);
switch (c->default_compr) {
@@ -2855,11 +2854,11 @@ static int check_volume_empty(void)
static int open_target(void)
{
if (out_ubi) {
- out_fd = open(output, O_RDWR | O_EXCL);
+ out_fd = open(c->dev_name, O_RDWR | O_EXCL);
if (out_fd == -1)
return sys_errmsg("cannot open the UBI volume '%s'",
- output);
+ c->dev_name);
if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
close(out_fd);
return sys_errmsg("ubi_set_property(set direct_write) failed");
@@ -2872,11 +2871,11 @@ static int open_target(void)
}
}
} else {
- out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC,
+ out_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
if (out_fd == -1)
return sys_errmsg("cannot create output file '%s'",
- output);
+ c->dev_name);
}
return 0;
}
@@ -2896,7 +2895,7 @@ static int close_target(void)
if (ubi && ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
return sys_errmsg("ubi_set_property(clear direct_write) failed");
if (close(out_fd) == -1)
- return sys_errmsg("cannot close the target '%s'", output);
+ return sys_errmsg("cannot close the target '%s'", c->dev_name);
}
return 0;
}
@@ -3094,7 +3093,7 @@ int main(int argc, char *argv[])
printf("Success!\n");
out:
- free(output);
+ free(c->dev_name);
close_ubi();
crypto_cleanup();
return err;
--
2.13.6
Add linux kernel error conversion definitions, because there are
many error type conversions(eg. PTR_ERR, ERR_PTR) used in UBIFS
linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/linux_err.h | 62 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+)
create mode 100644 ubifs-utils/common/linux_err.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 0f9c0fa6..69192ffd 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -1,6 +1,7 @@
common_SOURCES = \
ubifs-utils/common/compiler_attributes.h \
ubifs-utils/common/linux_types.h \
+ ubifs-utils/common/linux_err.h \
ubifs-utils/common/defs.h \
ubifs-utils/common/crc16.h \
ubifs-utils/common/crc16.c \
diff --git a/ubifs-utils/common/linux_err.h b/ubifs-utils/common/linux_err.h
new file mode 100644
index 00000000..5c6ddc3f
--- /dev/null
+++ b/ubifs-utils/common/linux_err.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_ERR_H
+#define _LINUX_ERR_H
+
+/* Adapted from include/linux/err.h */
+
+#include <stdbool.h>
+
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a normal
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+#define MAX_ERRNO 4095
+
+#define IS_ERR_VALUE(x) ((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void * ERR_PTR(long error)
+{
+ return (void *) error;
+}
+
+static inline long PTR_ERR(const void *ptr)
+{
+ return (long) ptr;
+}
+
+static inline bool IS_ERR(const void *ptr)
+{
+ return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+ return !ptr || IS_ERR_VALUE((unsigned long)ptr);
+}
+
+/**
+ * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type
+ * @ptr: The pointer to cast.
+ *
+ * Explicitly cast an error-valued pointer to another pointer type in such a
+ * way as to make it clear that's what's going on.
+ */
+static inline void * ERR_CAST(const void *ptr)
+{
+ /* cast away the const */
+ return (void *) ptr;
+}
+
+static inline int PTR_ERR_OR_ZERO(const void *ptr)
+{
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+ else
+ return 0;
+}
+
+#endif /* _LINUX_ERR_H */
--
2.13.6
The libubi could be opened in get_options(), don't forget to close it
in error handling paths in main(). Also close libubi in error handling
paths in open_ubi(). To implement that, extract libubi_close() into a
new helper function close_ubi().
Besides, invoke crypto_cleanup() in error handling paths in main().
Fixes: a48340c335dab ("mkfs.ubifs: use libubi to format UBI volume")
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 38 ++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 25c49967..6a4a4588 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -521,11 +521,21 @@ static long long get_bytes(const char *str)
return bytes;
}
+
+static void close_ubi(void)
+{
+ if (ubi) {
+ libubi_close(ubi);
+ ubi = NULL;
+ }
+}
+
/**
- * open_ubi - open the UBI volume.
+ * open_ubi - open the libubi.
* @node: name of the UBI volume character device to fetch information about
*
- * Returns %0 in case of success and %-1 in case of failure
+ * This function opens libubi, and initialize device & volume information
+ * according to @node. Returns %0 in case of success and %-1 in case of failure.
*/
static int open_ubi(const char *node)
{
@@ -537,10 +547,14 @@ static int open_ubi(const char *node)
ubi = libubi_open();
if (!ubi)
return -1;
- if (ubi_get_vol_info(ubi, node, &c->vi))
+ if (ubi_get_vol_info(ubi, node, &c->vi)) {
+ close_ubi();
return -1;
- if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di))
+ }
+ if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) {
+ close_ubi();
return -1;
+ }
return 0;
}
@@ -2880,8 +2894,6 @@ static int close_target(void)
if (close(out_fd) == -1)
return sys_errmsg("cannot close the target '%s'", output);
}
- if (ubi)
- libubi_close(ubi);
if (output)
free(output);
return 0;
@@ -3062,25 +3074,25 @@ int main(int argc, char *argv[])
err = get_options(argc, argv);
if (err)
- return err;
+ goto out;
err = open_target();
if (err)
- return err;
+ goto out;
err = mkfs();
if (err) {
close_target();
- return err;
+ goto out;
}
err = close_target();
- if (err)
- return err;
- if (verbose)
+ if (verbose && !err)
printf("Success!\n");
+out:
+ close_ubi();
crypto_cleanup();
- return 0;
+ return err;
}
--
2.13.6
Add linux type definitions, because there are many types
(eg. u8/u16/u64) used in UBIFS linux kernel libs. Besides
move type conversions (eg. cpu_to_le16, cpu_to_le32, etc.)
into type definitions header file.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/crypto.c | 1 +
ubifs-utils/common/defs.h | 30 -------------
ubifs-utils/common/fscrypt.c | 3 +-
ubifs-utils/common/fscrypt.h | 7 +--
ubifs-utils/common/linux_types.h | 89 +++++++++++++++++++++++++++++++++++++
ubifs-utils/common/sign.c | 2 +-
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 9 ++--
8 files changed, 104 insertions(+), 38 deletions(-)
create mode 100644 ubifs-utils/common/linux_types.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index d58570fe..0f9c0fa6 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -1,5 +1,6 @@
common_SOURCES = \
ubifs-utils/common/compiler_attributes.h \
+ ubifs-utils/common/linux_types.h \
ubifs-utils/common/defs.h \
ubifs-utils/common/crc16.h \
ubifs-utils/common/crc16.c \
diff --git a/ubifs-utils/common/crypto.c b/ubifs-utils/common/crypto.c
index 2ecd8da1..e4ef3491 100644
--- a/ubifs-utils/common/crypto.c
+++ b/ubifs-utils/common/crypto.c
@@ -23,6 +23,7 @@
#include <string.h>
#include <assert.h>
+#include "linux_types.h"
#include "fscrypt.h"
#include "defs.h"
#include "ubifs.h"
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index cafc94af..dd3b806e 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -9,7 +9,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
-#include <byteswap.h>
#include <errno.h>
#include "ubifs.h"
@@ -27,37 +26,8 @@ enum { MKFS_PROGRAM_TYPE = 0 };
printf("%s: %s: " fmt "\n", PROGRAM_NAME, __FUNCTION__, ##__VA_ARGS__); \
} while(0)
-#define t16(x) ({ \
- uint16_t __b = (x); \
- (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \
-})
-
-#define t32(x) ({ \
- uint32_t __b = (x); \
- (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \
-})
-
-#define t64(x) ({ \
- uint64_t __b = (x); \
- (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \
-})
-
-#define cpu_to_le16(x) ((__le16){t16(x)})
-#define cpu_to_le32(x) ((__le32){t32(x)})
-#define cpu_to_le64(x) ((__le64){t64(x)})
-
-#define le16_to_cpu(x) (t16((x)))
-#define le32_to_cpu(x) (t32((x)))
-#define le64_to_cpu(x) (t64((x)))
-
#define unlikely(x) (x)
-struct qstr
-{
- char *name;
- size_t len;
-};
-
/**
* fls - find last (most-significant) bit set
* @x: the word to search
diff --git a/ubifs-utils/common/fscrypt.c b/ubifs-utils/common/fscrypt.c
index 895d5c72..f39faa76 100644
--- a/ubifs-utils/common/fscrypt.c
+++ b/ubifs-utils/common/fscrypt.c
@@ -20,6 +20,7 @@
#include <endian.h>
+#include "linux_types.h"
#include "fscrypt.h"
#include "defs.h"
#include "ubifs.h"
@@ -88,7 +89,7 @@ unsigned int fscrypt_fname_encrypted_size(struct fscrypt_context *fctx,
return round_up(ilen, padding);
}
-int encrypt_path(void **outbuf, void *data, unsigned int data_len,
+int encrypt_path(void **outbuf, const void *data, unsigned int data_len,
unsigned int max_namelen, struct fscrypt_context *fctx)
{
void *inbuf, *crypt_key;
diff --git a/ubifs-utils/common/fscrypt.h b/ubifs-utils/common/fscrypt.h
index b8a599de..4a073e97 100644
--- a/ubifs-utils/common/fscrypt.h
+++ b/ubifs-utils/common/fscrypt.h
@@ -107,7 +107,7 @@ struct fscrypt_context *inherit_fscrypt_context(struct fscrypt_context *fctx);
void free_fscrypt_context(struct fscrypt_context *fctx);
unsigned int fscrypt_fname_encrypted_size(struct fscrypt_context *fctx,
unsigned int ilen);
-int encrypt_path(void **outbuf, void *data, unsigned int data_len,
+int encrypt_path(void **outbuf, const void *data, unsigned int data_len,
unsigned int max_namelen, struct fscrypt_context *fctx);
int encrypt_data_node(struct fscrypt_context *fctx, unsigned int block_no,
struct ubifs_data_node *dn, size_t length);
@@ -138,8 +138,9 @@ static inline void free_fscrypt_context(struct fscrypt_context *fctx)
assert(!fctx);
}
-static inline int encrypt_path(void **outbuf, void *data, unsigned int data_len,
- unsigned int max_namelen, struct fscrypt_context *fctx)
+static inline int encrypt_path(void **outbuf, const void *data,
+ unsigned int data_len, unsigned int max_namelen,
+ struct fscrypt_context *fctx)
{
(void)outbuf;
(void)data;
diff --git a/ubifs-utils/common/linux_types.h b/ubifs-utils/common/linux_types.h
new file mode 100644
index 00000000..556b2e25
--- /dev/null
+++ b/ubifs-utils/common/linux_types.h
@@ -0,0 +1,89 @@
+#ifndef __LINUX_TYPES_H__
+#define __LINUX_TYPES_H__
+
+#include <linux/types.h>
+#include <sys/types.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "compiler_attributes.h"
+
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+
+typedef __s64 time64_t;
+
+struct qstr {
+ const char *name;
+ size_t len;
+};
+
+struct fscrypt_name {
+ struct qstr disk_name;
+};
+
+#define fname_name(p) ((p)->disk_name.name)
+#define fname_len(p) ((p)->disk_name.len)
+
+#define t16(x) ({ \
+ uint16_t __b = (x); \
+ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \
+})
+
+#define t32(x) ({ \
+ uint32_t __b = (x); \
+ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \
+})
+
+#define t64(x) ({ \
+ uint64_t __b = (x); \
+ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \
+})
+
+#define cpu_to_le16(x) ((__le16){t16(x)})
+#define cpu_to_le32(x) ((__le32){t32(x)})
+#define cpu_to_le64(x) ((__le64){t64(x)})
+
+#define le16_to_cpu(x) (t16((x)))
+#define le32_to_cpu(x) (t32((x)))
+#define le64_to_cpu(x) (t64((x)))
+
+#define check_mul_overflow(a, b, d) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ typeof(d) __d = (d); \
+ (void) (&__a == &__b); \
+ (void) (&__a == __d); \
+ __builtin_mul_overflow(__a, __b, __d); \
+})
+
+static inline __must_check size_t array_size(size_t a, size_t b)
+{
+ size_t bytes;
+ if (check_mul_overflow(a, b, &bytes))
+ return SIZE_MAX;
+
+ return bytes;
+}
+
+static inline int int_log2(unsigned int arg)
+{
+ int l = 0;
+
+ arg >>= 1;
+ while (arg) {
+ l++;
+ arg >>= 1;
+ }
+ return l;
+}
+
+#undef PAGE_SIZE
+#define PAGE_SIZE (getpagesize())
+#undef PAGE_SHIFT
+#define PAGE_SHIFT (int_log2(PAGE_SIZE))
+
+#endif
diff --git a/ubifs-utils/common/sign.c b/ubifs-utils/common/sign.c
index 7530503a..dfbc96bf 100644
--- a/ubifs-utils/common/sign.c
+++ b/ubifs-utils/common/sign.c
@@ -28,7 +28,7 @@
#include <openssl/conf.h>
#include <err.h>
-#include "compiler_attributes.h"
+#include "linux_types.h"
#include "sign.h"
#include "defs.h"
#include "ubifs.h"
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 2181595e..c2f5a29d 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -46,6 +46,7 @@
#include <zstd.h>
#endif
+#include "linux_types.h"
#include "defs.h"
#include "crypto.h"
#include "fscrypt.h"
@@ -1207,12 +1208,14 @@ static int add_xattr(struct ubifs_ino_node *host_ino, struct stat *st,
struct ubifs_ino_node *ino;
struct ubifs_dent_node *xent;
struct qstr nm;
+ char *tmp_name;
union ubifs_key xkey, nkey;
int len, ret;
nm.len = strlen(name);
- nm.name = xmalloc(nm.len + 1);
- memcpy(nm.name, name, nm.len + 1);
+ tmp_name = xmalloc(nm.len + 1);
+ memcpy(tmp_name, name, nm.len + 1);
+ nm.name = tmp_name;
host_ino->xattr_cnt++;
host_ino->xattr_size += CALC_DENT_SIZE(nm.len);
@@ -1240,7 +1243,7 @@ static int add_xattr(struct ubifs_ino_node *host_ino, struct stat *st,
xent->inum = cpu_to_le64(inum);
- ret = add_node(&xkey, nm.name, nm.len, xent, len);
+ ret = add_node(&xkey, tmp_name, nm.len, xent, len);
if (ret)
goto out;
--
2.13.6
Embed new members 'dev_fd' and 'libubi' into ubifs_info structure, so
that global variable 'ubi', 'out_fd' and 'out_ubi' could be removed
from mkfs.ubifs.c. Besides, add parameter in check_volume_empty().
Next patch will extract UBI opening/closing/check_volume_empty functions
into a new source file, these functions will be used in fsck.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/common/lpt.c | 10 ++--
ubifs-utils/common/ubifs.h | 13 ++++-
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 112 +++++++++++++++++++-----------------
3 files changed, 75 insertions(+), 60 deletions(-)
diff --git a/ubifs-utils/common/lpt.c b/ubifs-utils/common/lpt.c
index 9c1143f6..d07f569f 100644
--- a/ubifs-utils/common/lpt.c
+++ b/ubifs-utils/common/lpt.c
@@ -421,7 +421,7 @@ int create_lpt(struct ubifs_info *c)
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
- err = write_leb(lnum++, alen, buf);
+ err = write_leb(c, lnum++, alen, buf);
if (err)
goto out;
p = buf;
@@ -468,7 +468,7 @@ int create_lpt(struct ubifs_info *c)
set_ltab(c, lnum, c->leb_size - alen,
alen - len);
memset(p, 0xff, alen - len);
- err = write_leb(lnum++, alen, buf);
+ err = write_leb(c, lnum++, alen, buf);
if (err)
goto out;
p = buf;
@@ -515,7 +515,7 @@ int create_lpt(struct ubifs_info *c)
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
- err = write_leb(lnum++, alen, buf);
+ err = write_leb(c, lnum++, alen, buf);
if (err)
goto out;
p = buf;
@@ -538,7 +538,7 @@ int create_lpt(struct ubifs_info *c)
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
- err = write_leb(lnum++, alen, buf);
+ err = write_leb(c, lnum++, alen, buf);
if (err)
goto out;
p = buf;
@@ -558,7 +558,7 @@ int create_lpt(struct ubifs_info *c)
/* Write remaining buffer */
memset(p, 0xff, alen - len);
- err = write_leb(lnum, alen, buf);
+ err = write_leb(c, lnum, alen, buf);
if (err)
goto out;
diff --git a/ubifs-utils/common/ubifs.h b/ubifs-utils/common/ubifs.h
index 502a39ae..5a909f63 100644
--- a/ubifs-utils/common/ubifs.h
+++ b/ubifs-utils/common/ubifs.h
@@ -278,6 +278,8 @@ struct ubifs_znode
* @program_type: used to identify the type of current program
* @program_name: program name
* @dev_name: device name
+ * @dev_fd: opening handler for an UBI volume or an image file
+ * @libubi: opening handler for libubi
*
* @jhead_cnt: count of journal heads
* @max_bud_bytes: maximum number of bytes allowed in buds
@@ -370,6 +372,8 @@ struct ubifs_info
int program_type;
const char *program_name;
char *dev_name;
+ int dev_fd;
+ libubi_t libubi;
int jhead_cnt;
long long max_bud_bytes;
@@ -482,6 +486,13 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
(UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
}
-int write_leb(int lnum, int len, void *buf);
+int write_leb(struct ubifs_info *c, int lnum, int len, void *buf);
+
+/* super.c */
+int open_ubi(struct ubifs_info *c, const char *node);
+void close_ubi(struct ubifs_info *c);
+int open_target(struct ubifs_info *c);
+int close_target(struct ubifs_info *c);
+int check_volume_empty(struct ubifs_info *c);
#endif /* __UBIFS_H__ */
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 1cafbf3e..8bff44b2 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -136,7 +136,6 @@ struct inum_mapping {
*/
struct ubifs_info info_;
static struct ubifs_info *c = &info_;
-static libubi_t ubi;
int verbose;
int yes;
@@ -145,8 +144,6 @@ static char *root;
static int root_len;
static struct fscrypt_context *root_fctx;
static struct stat root_st;
-static int out_fd;
-static int out_ubi;
static int squash_owner;
static int do_create_inum_attr;
static char *context;
@@ -521,40 +518,42 @@ static long long get_bytes(const char *str)
return bytes;
}
-static void close_ubi(void)
+void close_ubi(struct ubifs_info *c)
{
- if (ubi) {
- libubi_close(ubi);
- ubi = NULL;
+ if (c->libubi) {
+ libubi_close(c->libubi);
+ c->libubi = NULL;
}
}
/**
* open_ubi - open the libubi.
+ * @c: the UBIFS file-system description object
* @node: name of the UBI volume character device to fetch information about
*
* This function opens libubi, and initialize device & volume information
* according to @node. Returns %0 in case of success and %-1 in case of failure.
*/
-static int open_ubi(const char *node)
+int open_ubi(struct ubifs_info *c, const char *node)
{
struct stat st;
if (stat(node, &st) || !S_ISCHR(st.st_mode))
return -1;
- ubi = libubi_open();
- if (!ubi)
- return -1;
- if (ubi_get_vol_info(ubi, node, &c->vi)) {
- close_ubi();
+ c->libubi = libubi_open();
+ if (!c->libubi)
return -1;
- }
- if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) {
- close_ubi();
- return -1;
- }
+ if (ubi_get_vol_info(c->libubi, node, &c->vi))
+ goto out_err;
+ if (ubi_get_dev_info1(c->libubi, c->vi.dev_num, &c->di))
+ goto out_err;
+
return 0;
+
+out_err:
+ close_ubi(c);
+ return -1;
}
static void select_default_compr(void)
@@ -840,9 +839,9 @@ static int get_options(int argc, char**argv)
if (!c->dev_name)
return errmsg("not output device or file specified");
- out_ubi = !open_ubi(c->dev_name);
+ open_ubi(c, c->dev_name);
- if (out_ubi) {
+ if (c->libubi) {
c->min_io_size = c->di.min_io_size;
c->leb_size = c->vi.leb_size;
if (c->max_leb_cnt == -1)
@@ -972,24 +971,25 @@ static void prepare_node(void *node, int len)
/**
* write_leb - copy the image of a LEB to the output target.
+ * @c: the UBIFS file-system description object
* @lnum: LEB number
* @len: length of data in the buffer
* @buf: buffer (must be at least c->leb_size bytes)
*/
-int write_leb(int lnum, int len, void *buf)
+int write_leb(struct ubifs_info *c, int lnum, int len, void *buf)
{
off_t pos = (off_t)lnum * c->leb_size;
dbg_msg(3, "LEB %d len %d", lnum, len);
memset(buf + len, 0xff, c->leb_size - len);
- if (out_ubi)
- if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size))
+ if (c->libubi)
+ if (ubi_leb_change_start(c->libubi, c->dev_fd, lnum, c->leb_size))
return sys_errmsg("ubi_leb_change_start failed");
- if (lseek(out_fd, pos, SEEK_SET) != pos)
+ if (lseek(c->dev_fd, pos, SEEK_SET) != pos)
return sys_errmsg("lseek failed seeking %lld", (long long)pos);
- if (write(out_fd, buf, c->leb_size) != c->leb_size)
+ if (write(c->dev_fd, buf, c->leb_size) != c->leb_size)
return sys_errmsg("write failed writing %d bytes at pos %lld",
c->leb_size, (long long)pos);
@@ -1002,7 +1002,7 @@ int write_leb(int lnum, int len, void *buf)
*/
static int write_empty_leb(int lnum)
{
- return write_leb(lnum, 0, leb_buf);
+ return write_leb(c, lnum, 0, leb_buf);
}
/**
@@ -1058,7 +1058,7 @@ static int write_node(void *node, int len, int lnum)
len = do_pad(leb_buf, len);
- return write_leb(lnum, len, leb_buf);
+ return write_leb(c, lnum, len, leb_buf);
}
/**
@@ -1172,7 +1172,7 @@ static int flush_nodes(void)
if (!head_offs)
return 0;
len = do_pad(leb_buf, head_offs);
- err = write_leb(head_lnum, len, leb_buf);
+ err = write_leb(c, head_lnum, len, leb_buf);
if (err)
return err;
set_lprops(head_lnum, head_offs, head_flags);
@@ -2686,7 +2686,7 @@ static int write_super(void)
len = do_pad(sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len));
- err = write_leb(UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ + len, sup);
+ err = write_leb(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ + len, sup);
if (err)
goto out;
@@ -2822,6 +2822,7 @@ static int write_orphan_area(void)
/**
* check_volume_empty - check if the UBI volume is empty.
+ * @c: the UBIFS file-system description object
*
* This function checks if the UBI volume is empty by looking if its LEBs are
* mapped or not.
@@ -2829,12 +2830,12 @@ static int write_orphan_area(void)
* Returns %0 in case of success, %1 is the volume is not empty,
* and a negative error code in case of failure.
*/
-static int check_volume_empty(void)
+int check_volume_empty(struct ubifs_info *c)
{
int lnum, err;
for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) {
- err = ubi_is_mapped(out_fd, lnum);
+ err = ubi_is_mapped(c->dev_fd, lnum);
if (err < 0)
return err;
if (err == 1)
@@ -2845,35 +2846,29 @@ static int check_volume_empty(void)
/**
* open_target - open the output target.
+ * @c: the UBIFS file-system description object
*
* Open the output target. The target can be an UBI volume
* or a file.
*
* Returns %0 in case of success and %-1 in case of failure.
*/
-static int open_target(void)
+int open_target(struct ubifs_info *c)
{
- if (out_ubi) {
- out_fd = open(c->dev_name, O_RDWR | O_EXCL);
+ if (c->libubi) {
+ c->dev_fd = open(c->dev_name, O_RDWR | O_EXCL);
- if (out_fd == -1)
+ if (c->dev_fd == -1)
return sys_errmsg("cannot open the UBI volume '%s'",
c->dev_name);
- if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
- close(out_fd);
+ if (ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
+ close(c->dev_fd);
return sys_errmsg("ubi_set_property(set direct_write) failed");
}
-
- if (!yes && check_volume_empty()) {
- if (!prompt("UBI volume is not empty. Format anyways?", false)) {
- close(out_fd);
- return errmsg("UBI volume is not empty");
- }
- }
} else {
- out_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
+ c->dev_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
- if (out_fd == -1)
+ if (c->dev_fd == -1)
return sys_errmsg("cannot create output file '%s'",
c->dev_name);
}
@@ -2883,18 +2878,19 @@ static int open_target(void)
/**
* close_target - close the output target.
+ * @c: the UBIFS file-system description object
*
* Close the output target. If the target was an UBI
* volume, also close libubi.
*
* Returns %0 in case of success and %-1 in case of failure.
*/
-static int close_target(void)
+int close_target(struct ubifs_info *c)
{
- if (out_fd >= 0) {
- if (ubi && ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
+ if (c->dev_fd >= 0) {
+ if (c->libubi && ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
return sys_errmsg("ubi_set_property(clear direct_write) failed");
- if (close(out_fd) == -1)
+ if (close(c->dev_fd) == -1)
return sys_errmsg("cannot close the target '%s'", c->dev_name);
}
return 0;
@@ -3077,24 +3073,32 @@ int main(int argc, char *argv[])
if (err)
goto out;
- err = open_target();
+ err = open_target(c);
if (err)
goto out;
+ if (!yes && check_volume_empty(c)) {
+ if (!prompt("UBI volume is not empty. Format anyways?", false)) {
+ close_target(c);
+ err = errmsg("UBI volume is not empty");
+ goto out;
+ }
+ }
+
err = mkfs();
if (err) {
- close_target();
+ close_target(c);
goto out;
}
- err = close_target();
+ err = close_target(c);
if (verbose && !err)
printf("Success!\n");
out:
free(c->dev_name);
- close_ubi();
+ close_ubi(c);
crypto_cleanup();
return err;
}
--
2.13.6
Add sorting implementations, because the sorting function
is used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 2 +
ubifs-utils/common/sort.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++
ubifs-utils/common/sort.h | 20 ++++
3 files changed, 296 insertions(+)
create mode 100644 ubifs-utils/common/sort.c
create mode 100644 ubifs-utils/common/sort.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index ece8a6e9..3329b6f9 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -10,6 +10,8 @@ common_SOURCES = \
ubifs-utils/common/rwsem.h \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
+ ubifs-utils/common/sort.h \
+ ubifs-utils/common/sort.c \
ubifs-utils/common/defs.h \
ubifs-utils/common/crc16.h \
ubifs-utils/common/crc16.c \
diff --git a/ubifs-utils/common/sort.c b/ubifs-utils/common/sort.c
new file mode 100644
index 00000000..d585836d
--- /dev/null
+++ b/ubifs-utils/common/sort.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A fast, small, non-recursive O(n log n) sort for the Linux kernel
+ *
+ * This performs n*log2(n) + 0.37*n + o(n) comparisons on average,
+ * and 1.5*n*log2(n) + O(n) in the (very contrived) worst case.
+ *
+ * Glibc qsort() manages n*log2(n) - 1.26*n for random inputs (1.63*n
+ * better) at the expense of stack usage and much larger code to avoid
+ * quicksort's O(n^2) worst case.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <linux/types.h>
+
+#include "sort.h"
+#include "linux_types.h"
+
+/**
+ * is_aligned - is this pointer & size okay for word-wide copying?
+ * @base: pointer to data
+ * @size: size of each element
+ * @align: required alignment (typically 4 or 8)
+ *
+ * Returns true if elements can be copied using word loads and stores.
+ * The size must be a multiple of the alignment.
+ *
+ * For some reason, gcc doesn't know to optimize "if (a & mask || b & mask)"
+ * to "if ((a | b) & mask)", so we do that by hand.
+ */
+__const __always_inline
+static bool is_aligned(const void *base, size_t size, unsigned char align)
+{
+ unsigned char lsbits = (unsigned char)size;
+
+ (void)base;
+ return (lsbits & (align - 1)) == 0;
+}
+
+/**
+ * swap_words_32 - swap two elements in 32-bit chunks
+ * @a: pointer to the first element to swap
+ * @b: pointer to the second element to swap
+ * @n: element size (must be a multiple of 4)
+ *
+ * Exchange the two objects in memory. This exploits base+index addressing,
+ * which basically all CPUs have, to minimize loop overhead computations.
+ *
+ * For some reason, on x86 gcc 7.3.0 adds a redundant test of n at the
+ * bottom of the loop, even though the zero flag is still valid from the
+ * subtract (since the intervening mov instructions don't alter the flags).
+ * Gcc 8.1.0 doesn't have that problem.
+ */
+static void swap_words_32(void *a, void *b, size_t n)
+{
+ do {
+ u32 t = *(u32 *)(a + (n -= 4));
+ *(u32 *)(a + n) = *(u32 *)(b + n);
+ *(u32 *)(b + n) = t;
+ } while (n);
+}
+
+/**
+ * swap_words_64 - swap two elements in 64-bit chunks
+ * @a: pointer to the first element to swap
+ * @b: pointer to the second element to swap
+ * @n: element size (must be a multiple of 8)
+ *
+ * Exchange the two objects in memory. This exploits base+index
+ * addressing, which basically all CPUs have, to minimize loop overhead
+ * computations.
+ *
+ * We'd like to use 64-bit loads if possible. If they're not, emulating
+ * one requires base+index+4 addressing which x86 has but most other
+ * processors do not.
+ */
+static void swap_words_64(void *a, void *b, size_t n)
+{
+ do {
+ u64 t = *(u64 *)(a + (n -= 8));
+ *(u64 *)(a + n) = *(u64 *)(b + n);
+ *(u64 *)(b + n) = t;
+ } while (n);
+}
+
+/**
+ * swap_bytes - swap two elements a byte at a time
+ * @a: pointer to the first element to swap
+ * @b: pointer to the second element to swap
+ * @n: element size
+ *
+ * This is the fallback if alignment doesn't allow using larger chunks.
+ */
+static void swap_bytes(void *a, void *b, size_t n)
+{
+ do {
+ char t = ((char *)a)[--n];
+ ((char *)a)[n] = ((char *)b)[n];
+ ((char *)b)[n] = t;
+ } while (n);
+}
+
+/*
+ * The values are arbitrary as long as they can't be confused with
+ * a pointer, but small integers make for the smallest compare
+ * instructions.
+ */
+#define SWAP_WORDS_64 (swap_r_func_t)0
+#define SWAP_WORDS_32 (swap_r_func_t)1
+#define SWAP_BYTES (swap_r_func_t)2
+#define SWAP_WRAPPER (swap_r_func_t)3
+
+struct wrapper {
+ cmp_func_t cmp;
+ swap_func_t swap;
+};
+
+/*
+ * The function pointer is last to make tail calls most efficient if the
+ * compiler decides not to inline this function.
+ */
+static void do_swap(void *a, void *b, size_t size, swap_r_func_t swap_func, const void *priv)
+{
+ if (swap_func == SWAP_WRAPPER) {
+ ((const struct wrapper *)priv)->swap(a, b, (int)size);
+ return;
+ }
+
+ if (swap_func == SWAP_WORDS_64)
+ swap_words_64(a, b, size);
+ else if (swap_func == SWAP_WORDS_32)
+ swap_words_32(a, b, size);
+ else if (swap_func == SWAP_BYTES)
+ swap_bytes(a, b, size);
+ else
+ swap_func(a, b, (int)size, priv);
+}
+
+#define _CMP_WRAPPER ((cmp_r_func_t)0L)
+
+static int do_cmp(const void *a, const void *b, cmp_r_func_t cmp, const void *priv)
+{
+ if (cmp == _CMP_WRAPPER)
+ return ((const struct wrapper *)priv)->cmp(a, b);
+ return cmp(a, b, priv);
+}
+
+/**
+ * parent - given the offset of the child, find the offset of the parent.
+ * @i: the offset of the heap element whose parent is sought. Non-zero.
+ * @lsbit: a precomputed 1-bit mask, equal to "size & -size"
+ * @size: size of each element
+ *
+ * In terms of array indexes, the parent of element j = @i/@size is simply
+ * (j-1)/2. But when working in byte offsets, we can't use implicit
+ * truncation of integer divides.
+ *
+ * Fortunately, we only need one bit of the quotient, not the full divide.
+ * @size has a least significant bit. That bit will be clear if @i is
+ * an even multiple of @size, and set if it's an odd multiple.
+ *
+ * Logically, we're doing "if (i & lsbit) i -= size;", but since the
+ * branch is unpredictable, it's done with a bit of clever branch-free
+ * code instead.
+ */
+__const __always_inline
+static size_t parent(size_t i, unsigned int lsbit, size_t size)
+{
+ i -= size;
+ i -= size & -(i & lsbit);
+ return i / 2;
+}
+
+/**
+ * sort_r - sort an array of elements
+ * @base: pointer to data to sort
+ * @num: number of elements
+ * @size: size of each element
+ * @cmp_func: pointer to comparison function
+ * @swap_func: pointer to swap function or NULL
+ * @priv: third argument passed to comparison function
+ *
+ * This function does a heapsort on the given array. You may provide
+ * a swap_func function if you need to do something more than a memory
+ * copy (e.g. fix up pointers or auxiliary data), but the built-in swap
+ * avoids a slow retpoline and so is significantly faster.
+ *
+ * Sorting time is O(n log n) both on average and worst-case. While
+ * quicksort is slightly faster on average, it suffers from exploitable
+ * O(n*n) worst-case behavior and extra memory requirements that make
+ * it less suitable for kernel use.
+ */
+void sort_r(void *base, size_t num, size_t size,
+ cmp_r_func_t cmp_func,
+ swap_r_func_t swap_func,
+ const void *priv)
+{
+ /* pre-scale counters for performance */
+ size_t n = num * size, a = (num/2) * size;
+ const unsigned int lsbit = size & -size; /* Used to find parent */
+
+ if (!a) /* num < 2 || size == 0 */
+ return;
+
+ /* called from 'sort' without swap function, let's pick the default */
+ if (swap_func == SWAP_WRAPPER && !((struct wrapper *)priv)->swap)
+ swap_func = NULL;
+
+ if (!swap_func) {
+ if (is_aligned(base, size, 8))
+ swap_func = SWAP_WORDS_64;
+ else if (is_aligned(base, size, 4))
+ swap_func = SWAP_WORDS_32;
+ else
+ swap_func = SWAP_BYTES;
+ }
+
+ /*
+ * Loop invariants:
+ * 1. elements [a,n) satisfy the heap property (compare greater than
+ * all of their children),
+ * 2. elements [n,num*size) are sorted, and
+ * 3. a <= b <= c <= d <= n (whenever they are valid).
+ */
+ for (;;) {
+ size_t b, c, d;
+
+ if (a) /* Building heap: sift down --a */
+ a -= size;
+ else if (n -= size) /* Sorting: Extract root to --n */
+ do_swap(base, base + n, size, swap_func, priv);
+ else /* Sort complete */
+ break;
+
+ /*
+ * Sift element at "a" down into heap. This is the
+ * "bottom-up" variant, which significantly reduces
+ * calls to cmp_func(): we find the sift-down path all
+ * the way to the leaves (one compare per level), then
+ * backtrack to find where to insert the target element.
+ *
+ * Because elements tend to sift down close to the leaves,
+ * this uses fewer compares than doing two per level
+ * on the way down. (A bit more than half as many on
+ * average, 3/4 worst-case.)
+ */
+ for (b = a; c = 2*b + size, (d = c + size) < n;)
+ b = do_cmp(base + c, base + d, cmp_func, priv) >= 0 ? c : d;
+ if (d == n) /* Special case last leaf with no sibling */
+ b = c;
+
+ /* Now backtrack from "b" to the correct location for "a" */
+ while (b != a && do_cmp(base + a, base + b, cmp_func, priv) >= 0)
+ b = parent(b, lsbit, size);
+ c = b; /* Where "a" belongs */
+ while (b != a) { /* Shift it into place */
+ b = parent(b, lsbit, size);
+ do_swap(base + b, base + c, size, swap_func, priv);
+ }
+ }
+}
+
+void sort(void *base, size_t num, size_t size,
+ cmp_func_t cmp_func,
+ swap_func_t swap_func)
+{
+ struct wrapper w = {
+ .cmp = cmp_func,
+ .swap = swap_func,
+ };
+
+ return sort_r(base, num, size, _CMP_WRAPPER, SWAP_WRAPPER, &w);
+}
diff --git a/ubifs-utils/common/sort.h b/ubifs-utils/common/sort.h
new file mode 100644
index 00000000..89829422
--- /dev/null
+++ b/ubifs-utils/common/sort.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SORT_H
+#define _LINUX_SORT_H
+
+typedef void (*swap_r_func_t)(void *a, void *b, int size, const void *priv);
+typedef void (*swap_func_t)(void *a, void *b, int size);
+
+typedef int (*cmp_r_func_t)(const void *a, const void *b, const void *priv);
+typedef int (*cmp_func_t)(const void *a, const void *b);
+
+void sort_r(void *base, size_t num, size_t size,
+ cmp_r_func_t cmp_func,
+ swap_r_func_t swap_func,
+ const void *priv);
+
+void sort(void *base, size_t num, size_t size,
+ cmp_func_t cmp_func,
+ swap_func_t swap_func);
+
+#endif
--
2.13.6
Current rbtree implementation code is put under jffs utils, extract it
into common lib, and add more rbtree operations(eg. rb_first_postorder,
rb_next_postorder, etc.).
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
{jffsX-utils => include}/rbtree.h | 32 ++++++++++++++++++++++++++++++++
jffsX-utils/Makemodule.am | 6 ++----
lib/Makemodule.am | 2 ++
{jffsX-utils => lib}/rbtree.c | 38 ++++++++++++++++++++++++++++++++++++++
4 files changed, 74 insertions(+), 4 deletions(-)
rename {jffsX-utils => include}/rbtree.h (79%)
rename {jffsX-utils => lib}/rbtree.c (90%)
diff --git a/jffsX-utils/rbtree.h b/include/rbtree.h
similarity index 79%
rename from jffsX-utils/rbtree.h
rename to include/rbtree.h
index 0d77b65f..89926e74 100644
--- a/jffsX-utils/rbtree.h
+++ b/include/rbtree.h
@@ -155,6 +155,38 @@ extern struct rb_node *rb_prev(struct rb_node *);
extern struct rb_node *rb_first(struct rb_root *);
extern struct rb_node *rb_last(struct rb_root *);
+/* Postorder iteration - always visit the parent after its children */
+extern struct rb_node *rb_first_postorder(const struct rb_root *);
+extern struct rb_node *rb_next_postorder(const struct rb_node *);
+
+#define rb_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? rb_entry(____ptr, type, member) : NULL; \
+ })
+
+/**
+ * rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of
+ * given type allowing the backing memory of @pos to be invalidated
+ *
+ * @pos: the 'type *' to use as a loop cursor.
+ * @n: another 'type *' to use as temporary storage
+ * @root: 'rb_root *' of the rbtree.
+ * @field: the name of the rb_node field within 'type'.
+ *
+ * rbtree_postorder_for_each_entry_safe() provides a similar guarantee as
+ * list_for_each_entry_safe() and allows the iteration to continue independent
+ * of changes to @pos by the body of the loop.
+ *
+ * Note, however, that it cannot handle other modifications that re-order the
+ * rbtree it is iterating over. This includes calling rb_erase() on @pos, as
+ * rb_erase() may rebalance the tree, causing us to miss some nodes.
+ */
+#define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \
+ for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \
+ pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \
+ typeof(*pos), field); 1; }); \
+ pos = n)
+
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
struct rb_root *root);
diff --git a/jffsX-utils/Makemodule.am b/jffsX-utils/Makemodule.am
index 2374b858..4ae4c57b 100644
--- a/jffsX-utils/Makemodule.am
+++ b/jffsX-utils/Makemodule.am
@@ -1,16 +1,14 @@
mkfs_jffs2_SOURCES = \
jffsX-utils/mkfs.jffs2.c \
- jffsX-utils/rbtree.h \
jffsX-utils/compr.h \
- jffsX-utils/rbtree.c \
jffsX-utils/compr.c \
jffsX-utils/compr_rtime.c \
jffsX-utils/compr.h \
- jffsX-utils/rbtree.h \
jffsX-utils/summary.h \
include/linux/jffs2.h \
include/mtd/jffs2-user.h \
- include/list.h
+ include/list.h \
+ include/rbtree.h
mkfs_jffs2_LDADD = libmtd.a $(ZLIB_LIBS) $(LZO_LIBS)
mkfs_jffs2_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS)
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 7f890daa..441532de 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -7,6 +7,8 @@ libmtd_a_SOURCES = \
include/common.h \
lib/list_sort.c \
include/list.h \
+ lib/rbtree.c \
+ include/rbtree.h \
lib/libcrc32.c \
include/crc32.h \
lib/libmtd_legacy.c \
diff --git a/jffsX-utils/rbtree.c b/lib/rbtree.c
similarity index 90%
rename from jffsX-utils/rbtree.c
rename to lib/rbtree.c
index 329e0986..32c87556 100644
--- a/jffsX-utils/rbtree.c
+++ b/lib/rbtree.c
@@ -388,3 +388,41 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new,
/* Copy the pointers/colour from the victim to the replacement */
*new = *victim;
}
+
+static struct rb_node *rb_left_deepest_node(const struct rb_node *node)
+{
+ for (;;) {
+ if (node->rb_left)
+ node = node->rb_left;
+ else if (node->rb_right)
+ node = node->rb_right;
+ else
+ return (struct rb_node *)node;
+ }
+}
+
+struct rb_node *rb_next_postorder(const struct rb_node *node)
+{
+ const struct rb_node *parent;
+ if (!node)
+ return NULL;
+ parent = rb_parent(node);
+
+ /* If we're sitting on node, we've already seen our children */
+ if (parent && node == parent->rb_left && parent->rb_right) {
+ /* If we are the parent's left node, go to the parent's right
+ * node then all the way down to the left */
+ return rb_left_deepest_node(parent->rb_right);
+ } else
+ /* Otherwise we are the parent's right node, and the parent
+ * should be next */
+ return (struct rb_node *)parent;
+}
+
+struct rb_node *rb_first_postorder(const struct rb_root *root)
+{
+ if (!root->rb_node)
+ return NULL;
+
+ return rb_left_deepest_node(root->rb_node);
+}
--
2.13.6
Current list implementation code is put under jffs utils, extract it into
common lib, and add more list operations(eg. list_move, list_splice, etc.).
Besides, add list sorting support in new source file lib/list_sort.c.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
include/list.h | 263 ++++++++++++++++++++++++++++++++++++++++++++++
jffsX-utils/Makemodule.am | 3 +-
jffsX-utils/compr.c | 49 ---------
jffsX-utils/compr.h | 5 +-
lib/Makemodule.am | 2 +
lib/list_sort.c | 246 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 514 insertions(+), 54 deletions(-)
create mode 100644 include/list.h
create mode 100644 lib/list_sort.c
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 00000000..d26c9d1c
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#ifndef __LIST_H__
+#define __LIST_H__
+
+#include <stddef.h>
+
+/*
+ * This undef is here because BSD 4.4 added some LIST_ macros into system
+ * header file sys/queue.h. This header is included in many other system
+ * headers and thus causes "macro redefined" warnings.
+ *
+ * As OS X is kind of a derivate of BSD, this affects OS X too.
+ *
+ * To use our own LIST_ macros (copied from kernel code), we have to
+ * at first undefine the conflicting system macros.
+ *
+ */
+#undef LIST_HEAD
+#undef LIST_HEAD_INIT
+
+/*
+ * Simple, generic doubly-linked list implementation.
+ */
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(list) list_head_init(list)
+static inline void list_head_init(struct list_head *list)
+{
+ list->next = list->prev = list;
+}
+
+static inline void list_head_destroy(struct list_head *list)
+{
+ list->next = list->prev = NULL;
+}
+
+static inline void __list_add(struct list_head *add,
+ struct list_head *prev, struct list_head *next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+static inline void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head->prev, head);
+}
+
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ list_head_init(entry);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+static inline void list_move_tail(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list, const struct list_head *head)
+{
+ return list->next == head;
+}
+
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+
+ first->prev = prev;
+ prev->next = first;
+
+ last->next = next;
+ next->prev = last;
+}
+
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head, head->next);
+}
+
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
+}
+
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head, head->next);
+ list_head_init(list);
+ }
+}
+
+#define list_entry(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/**
+ * list_entry_is_head - test if the entry points to the head of the list
+ * @pos: the type * to cursor
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry_is_head(pos, head, member) \
+ (&pos->member == (head))
+
+typedef int __attribute__((nonnull(2,3))) (*list_cmp_func_t)(void *,
+ const struct list_head *, const struct list_head *);
+__attribute__((nonnull(2,3)))
+void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp);
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_head within the struct.
+ */
+#define list_prev_entry(pos, member) \
+ list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_head within the struct.
+ */
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_last_entry(head, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_prev_entry(pos, member))
+
+#endif /* __LIST_H__ */
diff --git a/jffsX-utils/Makemodule.am b/jffsX-utils/Makemodule.am
index 266c12ef..2374b858 100644
--- a/jffsX-utils/Makemodule.am
+++ b/jffsX-utils/Makemodule.am
@@ -9,7 +9,8 @@ mkfs_jffs2_SOURCES = \
jffsX-utils/rbtree.h \
jffsX-utils/summary.h \
include/linux/jffs2.h \
- include/mtd/jffs2-user.h
+ include/mtd/jffs2-user.h \
+ include/list.h
mkfs_jffs2_LDADD = libmtd.a $(ZLIB_LIBS) $(LZO_LIBS)
mkfs_jffs2_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS)
diff --git a/jffsX-utils/compr.c b/jffsX-utils/compr.c
index 01176ebe..d408ef8a 100644
--- a/jffsX-utils/compr.c
+++ b/jffsX-utils/compr.c
@@ -17,55 +17,6 @@
extern int page_size;
-/* LIST IMPLEMENTATION (from linux/list.h) */
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-static inline void __list_del(struct list_head *prev, struct list_head *next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = (void *) 0;
- entry->prev = (void *) 0;
-}
-
-#define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-#define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-
/* Available compressors are on this list */
static LIST_HEAD(jffs2_compressor_list);
diff --git a/jffsX-utils/compr.h b/jffsX-utils/compr.h
index f1f59758..69699523 100644
--- a/jffsX-utils/compr.h
+++ b/jffsX-utils/compr.h
@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <stdint.h>
#include "linux/jffs2.h"
+#include "list.h"
#define CONFIG_JFFS2_RTIME
@@ -49,10 +50,6 @@
#define KERN_INFO
#define KERN_DEBUG
-struct list_head {
- struct list_head *next, *prev;
-};
-
void jffs2_set_compression_mode(int mode);
int jffs2_get_compression_mode(void);
int jffs2_set_compression_mode_name(const char *mode_name);
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 570896b4..7f890daa 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -5,6 +5,8 @@ libmtd_a_SOURCES = \
include/libfec.h \
lib/common.c \
include/common.h \
+ lib/list_sort.c \
+ include/list.h \
lib/libcrc32.c \
include/crc32.h \
lib/libmtd_legacy.c \
diff --git a/lib/list_sort.c b/lib/list_sort.c
new file mode 100644
index 00000000..d8734389
--- /dev/null
+++ b/lib/list_sort.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "list.h"
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+__attribute__((nonnull(2,3,4)))
+static struct list_head *merge(void *priv, list_cmp_func_t cmp,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head *head, **tail = &head;
+
+ for (;;) {
+ /* if equal, take 'a' -- important for sort stability */
+ if (cmp(priv, a, b) <= 0) {
+ *tail = a;
+ tail = &a->next;
+ a = a->next;
+ if (!a) {
+ *tail = b;
+ break;
+ }
+ } else {
+ *tail = b;
+ tail = &b->next;
+ b = b->next;
+ if (!b) {
+ *tail = a;
+ break;
+ }
+ }
+ }
+ return head;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure. This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+__attribute__((nonnull(2,3,4,5)))
+static void merge_final(void *priv, list_cmp_func_t cmp, struct list_head *head,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head *tail = head;
+ unsigned int count = 0;
+
+ for (;;) {
+ /* if equal, take 'a' -- important for sort stability */
+ if (cmp(priv, a, b) <= 0) {
+ tail->next = a;
+ a->prev = tail;
+ tail = a;
+ a = a->next;
+ if (!a)
+ break;
+ } else {
+ tail->next = b;
+ b->prev = tail;
+ tail = b;
+ b = b->next;
+ if (!b) {
+ b = a;
+ break;
+ }
+ }
+ }
+
+ /* Finish linking remainder of list b on to tail */
+ tail->next = b;
+ do {
+ /*
+ * If the merge is highly unbalanced (e.g. the input is
+ * already sorted), this loop may run many iterations.
+ * Continue callbacks to the client even though no
+ * element comparison is needed, so the client's cmp()
+ * routine can invoke cond_resched() periodically.
+ */
+ if (!++count)
+ cmp(priv, b, b);
+ b->prev = tail;
+ tail = b;
+ b = b->next;
+ } while (b);
+
+ /* And the final links to make a circular doubly-linked list */
+ tail->next = head;
+ head->prev = tail;
+}
+
+/**
+ * list_sort - sort a list
+ * @priv: private data, opaque to list_sort(), passed to @cmp
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * The comparison function @cmp must return > 0 if @a should sort after
+ * @b ("@a > @b" if you want an ascending sort), and <= 0 if @a should
+ * sort before @b *or* their original order should be preserved. It is
+ * always called with the element that came first in the input in @a,
+ * and list_sort is a stable sort, so it is not necessary to distinguish
+ * the @a < @b and @a == @b cases.
+ *
+ * This is compatible with two styles of @cmp function:
+ * - The traditional style which returns <0 / =0 / >0, or
+ * - Returning a boolean 0/1.
+ * The latter offers a chance to save a few cycles in the comparison
+ * (which is used by e.g. plug_ctx_cmp() in block/blk-mq.c).
+ *
+ * A good way to write a multi-word comparison is::
+ *
+ * if (a->high != b->high)
+ * return a->high > b->high;
+ * if (a->middle != b->middle)
+ * return a->middle > b->middle;
+ * return a->low > b->low;
+ *
+ *
+ * This mergesort is as eager as possible while always performing at least
+ * 2:1 balanced merges. Given two pending sublists of size 2^k, they are
+ * merged to a size-2^(k+1) list as soon as we have 2^k following elements.
+ *
+ * Thus, it will avoid cache thrashing as long as 3*2^k elements can
+ * fit into the cache. Not quite as good as a fully-eager bottom-up
+ * mergesort, but it does use 0.2*n fewer comparisons, so is faster in
+ * the common case that everything fits into L1.
+ *
+ *
+ * The merging is controlled by "count", the number of elements in the
+ * pending lists. This is beautifully simple code, but rather subtle.
+ *
+ * Each time we increment "count", we set one bit (bit k) and clear
+ * bits k-1 .. 0. Each time this happens (except the very first time
+ * for each bit, when count increments to 2^k), we merge two lists of
+ * size 2^k into one list of size 2^(k+1).
+ *
+ * This merge happens exactly when the count reaches an odd multiple of
+ * 2^k, which is when we have 2^k elements pending in smaller lists,
+ * so it's safe to merge away two lists of size 2^k.
+ *
+ * After this happens twice, we have created two lists of size 2^(k+1),
+ * which will be merged into a list of size 2^(k+2) before we create
+ * a third list of size 2^(k+1), so there are never more than two pending.
+ *
+ * The number of pending lists of size 2^k is determined by the
+ * state of bit k of "count" plus two extra pieces of information:
+ *
+ * - The state of bit k-1 (when k == 0, consider bit -1 always set), and
+ * - Whether the higher-order bits are zero or non-zero (i.e.
+ * is count >= 2^(k+1)).
+ *
+ * There are six states we distinguish. "x" represents some arbitrary
+ * bits, and "y" represents some arbitrary non-zero bits:
+ * 0: 00x: 0 pending of size 2^k; x pending of sizes < 2^k
+ * 1: 01x: 0 pending of size 2^k; 2^(k-1) + x pending of sizes < 2^k
+ * 2: x10x: 0 pending of size 2^k; 2^k + x pending of sizes < 2^k
+ * 3: x11x: 1 pending of size 2^k; 2^(k-1) + x pending of sizes < 2^k
+ * 4: y00x: 1 pending of size 2^k; 2^k + x pending of sizes < 2^k
+ * 5: y01x: 2 pending of size 2^k; 2^(k-1) + x pending of sizes < 2^k
+ * (merge and loop back to state 2)
+ *
+ * We gain lists of size 2^k in the 2->3 and 4->5 transitions (because
+ * bit k-1 is set while the more significant bits are non-zero) and
+ * merge them away in the 5->2 transition. Note in particular that just
+ * before the 5->2 transition, all lower-order bits are 11 (state 3),
+ * so there is one list of each smaller size.
+ *
+ * When we reach the end of the input, we merge all the pending
+ * lists, from smallest to largest. If you work through cases 2 to
+ * 5 above, you can see that the number of elements we merge with a list
+ * of size 2^k varies from 2^(k-1) (cases 3 and 5 when x == 0) to
+ * 2^(k+1) - 1 (second merge of case 5 when x == 2^(k-1) - 1).
+ */
+__attribute__((nonnull(2,3)))
+void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp)
+{
+ struct list_head *list = head->next, *pending = NULL;
+ size_t count = 0; /* Count of pending */
+
+ if (list == head->prev) /* Zero or one elements */
+ return;
+
+ /* Convert to a null-terminated singly-linked list. */
+ head->prev->next = NULL;
+
+ /*
+ * Data structure invariants:
+ * - All lists are singly linked and null-terminated; prev
+ * pointers are not maintained.
+ * - pending is a prev-linked "list of lists" of sorted
+ * sublists awaiting further merging.
+ * - Each of the sorted sublists is power-of-two in size.
+ * - Sublists are sorted by size and age, smallest & newest at front.
+ * - There are zero to two sublists of each size.
+ * - A pair of pending sublists are merged as soon as the number
+ * of following pending elements equals their size (i.e.
+ * each time count reaches an odd multiple of that size).
+ * That ensures each later final merge will be at worst 2:1.
+ * - Each round consists of:
+ * - Merging the two sublists selected by the highest bit
+ * which flips when count is incremented, and
+ * - Adding an element from the input as a size-1 sublist.
+ */
+ do {
+ size_t bits;
+ struct list_head **tail = &pending;
+
+ /* Find the least-significant clear bit in count */
+ for (bits = count; bits & 1; bits >>= 1)
+ tail = &(*tail)->prev;
+ /* Do the indicated merge */
+ if (bits) {
+ struct list_head *a = *tail, *b = a->prev;
+
+ a = merge(priv, cmp, b, a);
+ /* Install the merged result in place of the inputs */
+ a->prev = b->prev;
+ *tail = a;
+ }
+
+ /* Move one element from input list to pending */
+ list->prev = pending;
+ pending = list;
+ list = list->next;
+ pending->next = NULL;
+ count++;
+ } while (list);
+
+ /* End of input; merge together all the pending lists. */
+ list = pending;
+ pending = pending->prev;
+ for (;;) {
+ struct list_head *next = pending->prev;
+
+ if (!next)
+ break;
+ list = merge(priv, cmp, pending, list);
+ pending = next;
+ }
+ /* The final merge, rebuilding prev links */
+ merge_final(priv, cmp, head, pending, list);
+}
--
2.13.6
Add atomic implementations, because there are some atomic operations
(eg. atomic_long_xxx) used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/atomic.h | 133 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 134 insertions(+)
create mode 100644 ubifs-utils/common/atomic.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 13171b74..58162579 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -2,6 +2,7 @@ common_SOURCES = \
ubifs-utils/common/compiler_attributes.h \
ubifs-utils/common/linux_types.h \
ubifs-utils/common/linux_err.h \
+ ubifs-utils/common/atomic.h \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
diff --git a/ubifs-utils/common/atomic.h b/ubifs-utils/common/atomic.h
new file mode 100644
index 00000000..f287d435
--- /dev/null
+++ b/ubifs-utils/common/atomic.h
@@ -0,0 +1,133 @@
+//Source: http://golubenco.org/atomic-operations.html
+#ifndef __ATOMIC_H__
+#define __ATOMIC_H__
+
+/* Check GCC version, just to be safe */
+#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC_MINOR__ < 1)
+# error atomic.h works only with GCC newer than version 4.1
+#endif /* GNUC >= 4.1 */
+
+/**
+ * Atomic type.
+ */
+typedef struct {
+ volatile long counter;
+} atomic_long_t;
+
+#define ATOMIC_INIT(i) { (i) }
+
+/**
+ * Read atomic variable
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically reads the value of @v.
+ */
+#define atomic_long_read(v) ((v)->counter)
+
+/**
+ * Set atomic variable
+ * @param v pointer of type atomic_long_t
+ * @param i required value
+ */
+#define atomic_long_set(v,i) (((v)->counter) = (i))
+
+/**
+ * Add to the atomic variable
+ * @param i integer value to add
+ * @param v pointer of type atomic_long_t
+ */
+static inline void atomic_long_add( int i, atomic_long_t *v )
+{
+ (void)__sync_add_and_fetch(&v->counter, i);
+}
+
+/**
+ * Subtract the atomic variable
+ * @param i integer value to subtract
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically subtracts @i from @v.
+ */
+static inline void atomic_long_sub( int i, atomic_long_t *v )
+{
+ (void)__sync_sub_and_fetch(&v->counter, i);
+}
+
+/**
+ * Subtract value from variable and test result
+ * @param i integer value to subtract
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically subtracts @i from @v and returns
+ * true if the result is zero, or false for all
+ * other cases.
+ */
+static inline int atomic_long_sub_and_test( int i, atomic_long_t *v )
+{
+ return !(__sync_sub_and_fetch(&v->counter, i));
+}
+
+/**
+ * Increment atomic variable
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_long_inc( atomic_long_t *v )
+{
+ (void)__sync_fetch_and_add(&v->counter, 1);
+}
+
+/**
+ * @brief decrement atomic variable
+ * @param v: pointer of type atomic_long_t
+ *
+ * Atomically decrements @v by 1. Note that the guaranteed
+ * useful range of an atomic_long_t is only 24 bits.
+ */
+static inline void atomic_long_dec( atomic_long_t *v )
+{
+ (void)__sync_fetch_and_sub(&v->counter, 1);
+}
+
+/**
+ * @brief Decrement and test
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.
+ */
+static inline int atomic_long_dec_and_test( atomic_long_t *v )
+{
+ return !(__sync_sub_and_fetch(&v->counter, 1));
+}
+
+/**
+ * @brief Increment and test
+ * @param v pointer of type atomic_long_t
+ *
+ * Atomically increments @v by 1
+ * and returns true if the result is zero, or false for all
+ * other cases.
+ */
+static inline int atomic_long_inc_and_test( atomic_long_t *v )
+{
+ return !(__sync_add_and_fetch(&v->counter, 1));
+}
+
+/**
+ * @brief add and test if negative
+ * @param v pointer of type atomic_long_t
+ * @param i integer value to add
+ *
+ * Atomically adds @i to @v and returns true
+ * if the result is negative, or false when
+ * result is greater than or equal to zero.
+ */
+static inline int atomic_long_add_negative( int i, atomic_long_t *v )
+{
+ return (__sync_add_and_fetch(&v->counter, i) < 0);
+}
+
+#endif
--
2.13.6
Add rwsem implementations, because there are some rwsems
(eg. c->commit_sem) used in UBIFS linux kernel libs.
The rwsem is implementated based on pthread mutex.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/rwsem.h | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
create mode 100644 ubifs-utils/common/rwsem.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 9f33f0db..ece8a6e9 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -7,6 +7,7 @@ common_SOURCES = \
ubifs-utils/common/bitops.c \
ubifs-utils/common/spinlock.h \
ubifs-utils/common/mutex.h \
+ ubifs-utils/common/rwsem.h \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
diff --git a/ubifs-utils/common/rwsem.h b/ubifs-utils/common/rwsem.h
new file mode 100644
index 00000000..3761724b
--- /dev/null
+++ b/ubifs-utils/common/rwsem.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_RWSEM_H_
+#define __LINUX_RWSEM_H_
+
+#include <pthread.h>
+
+struct rw_semaphore {
+ pthread_mutex_t lock;
+};
+
+#define init_rwsem(x) pthread_mutex_init(&(x)->lock, NULL)
+
+#define down_read(x) pthread_mutex_lock(&(x)->lock)
+#define down_write(x) pthread_mutex_lock(&(x)->lock)
+#define up_read(x) pthread_mutex_unlock(&(x)->lock)
+#define up_write(x) pthread_mutex_unlock(&(x)->lock)
+#define down_write_trylock(x) (pthread_mutex_trylock(&(x)->lock) == 0)
+
+#endif
--
2.13.6
Add compiler attributes implementations, such as __packed, __unused,
__const, etc., which could be used in linux kernel libs. Besides, change
some existing attributes into linux kernel style.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/compiler_attributes.h | 79 ++++++++++++++++++++++++++++++++
ubifs-utils/common/fscrypt.h | 6 ++-
ubifs-utils/common/sign.c | 4 +-
4 files changed, 86 insertions(+), 4 deletions(-)
create mode 100644 ubifs-utils/common/compiler_attributes.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 90cc7005..d58570fe 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -1,4 +1,5 @@
common_SOURCES = \
+ ubifs-utils/common/compiler_attributes.h \
ubifs-utils/common/defs.h \
ubifs-utils/common/crc16.h \
ubifs-utils/common/crc16.c \
diff --git a/ubifs-utils/common/compiler_attributes.h b/ubifs-utils/common/compiler_attributes.h
new file mode 100644
index 00000000..bb65d3a9
--- /dev/null
+++ b/ubifs-utils/common/compiler_attributes.h
@@ -0,0 +1,79 @@
+#ifndef __COMPILER_ATTRIBUTES_H__
+#define __COMPILER_ATTRIBUTES_H__
+
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough do {} while (0)
+#endif
+
+#define __packed __attribute__((__packed__))
+#define __unused __attribute__((__unused__))
+#define __const __attribute__((__const__))
+#define __must_check __attribute__((__warn_unused_result__))
+#ifndef __force
+#define __force
+#endif
+
+/*
+ * Optional: only supported since clang >= 14.0
+ *
+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-error-function-attribute
+ */
+#if __has_attribute(__error__)
+# define __compiletime_error(msg) __attribute__((__error__(msg)))
+#else
+# define __compiletime_error(msg)
+#endif
+
+#ifndef __compiletime_error
+# define __compiletime_error(message)
+#endif
+
+#ifdef __OPTIMIZE__
+# define __compiletime_assert(condition, msg, prefix, suffix) \
+ do { \
+ extern void prefix ## suffix(void) __compiletime_error(msg); \
+ if (!(condition)) \
+ prefix ## suffix(); \
+ } while (0)
+#else
+# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
+#endif
+
+#define _compiletime_assert(condition, msg, prefix, suffix) \
+ __compiletime_assert(condition, msg, prefix, suffix)
+
+/**
+ * compiletime_assert - break build and emit msg if condition is false
+ * @condition: a compile-time constant condition to check
+ * @msg: a message to emit if condition is false
+ *
+ * In tradition of POSIX assert, this macro will break the build if the
+ * supplied condition is *false*, emitting the supplied error message if the
+ * compiler has support to do so.
+ */
+#define compiletime_assert(condition, msg) \
+ _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
+
+/**
+ * BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
+ * error message.
+ * @condition: the condition which the compiler should know is false.
+ *
+ * See BUILD_BUG_ON for description.
+ */
+#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
+
+/**
+ * BUILD_BUG_ON - break compile if a condition is true.
+ * @condition: the condition which the compiler should know is false.
+ *
+ * If you have some code which relies on certain constants being equal, or
+ * some other compile-time-evaluated condition, you should use BUILD_BUG_ON to
+ * detect if someone changes it.
+ */
+#define BUILD_BUG_ON(condition) \
+ BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
+
+#endif
diff --git a/ubifs-utils/common/fscrypt.h b/ubifs-utils/common/fscrypt.h
index 908a5041..b8a599de 100644
--- a/ubifs-utils/common/fscrypt.h
+++ b/ubifs-utils/common/fscrypt.h
@@ -25,6 +25,8 @@
#include <openssl/rand.h>
#endif
#include <assert.h>
+
+#include "compiler_attributes.h"
#include "ubifs.h"
#include "crypto.h"
@@ -79,7 +81,7 @@ struct fscrypt_context {
__u8 flags;
__u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
__u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
-} __attribute__((packed));
+} __packed;
/**
* For encrypted symlinks, the ciphertext length is stored at the beginning
@@ -88,7 +90,7 @@ struct fscrypt_context {
struct fscrypt_symlink_data {
__le16 len;
char encrypted_path[1];
-} __attribute__((packed));
+} __packed;
#ifndef FS_MAX_KEY_SIZE
diff --git a/ubifs-utils/common/sign.c b/ubifs-utils/common/sign.c
index 9c53e671..7530503a 100644
--- a/ubifs-utils/common/sign.c
+++ b/ubifs-utils/common/sign.c
@@ -28,6 +28,7 @@
#include <openssl/conf.h>
#include <err.h>
+#include "compiler_attributes.h"
#include "sign.h"
#include "defs.h"
#include "ubifs.h"
@@ -116,8 +117,7 @@ static void drain_openssl_errors(void)
static const char *key_pass;
-static int pem_pw_cb(char *buf, int len, __attribute__((unused)) int w,
- __attribute__((unused)) void *v)
+static int pem_pw_cb(char *buf, int len, __unused int w, __unused void *v)
{
int pwlen;
--
2.13.6
The size of directory should be the total length of encrypted entry name,
otherwise it could trigger errors while checking filesystem:
dbg_check_filesystem [ubifs]: directory inode 89 size is 352, but
calculated size is 400
Fixes: 4c55918dd747d ("Implement filename encryption")
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 35 +++++++++++++++++++----------------
1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 8bf073ce..25c49967 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -1723,15 +1723,17 @@ static void set_dent_cookie(struct ubifs_dent_node *dent)
* @name: directory entry name
* @inum: target inode number of the directory entry
* @type: type of the target inode
+ * @kname_len: the length of name stored in the directory entry node is
+ * returned here
*/
static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
- unsigned char type, struct fscrypt_context *fctx)
+ unsigned char type, struct fscrypt_context *fctx,
+ int *kname_len)
{
struct ubifs_dent_node *dent = node_buf;
union ubifs_key key;
struct qstr dname;
char *kname;
- int kname_len;
int len;
dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum,
@@ -1749,7 +1751,7 @@ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
set_dent_cookie(dent);
if (!fctx) {
- kname_len = dname.len;
+ *kname_len = dname.len;
kname = strdup(name);
if (!kname)
return errmsg("cannot allocate memory");
@@ -1765,18 +1767,18 @@ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
if (ret < 0)
return ret;
- kname_len = ret;
+ *kname_len = ret;
}
- dent_key_init(c, &key, dir_inum, kname, kname_len);
- dent->nlen = cpu_to_le16(kname_len);
- memcpy(dent->name, kname, kname_len);
- dent->name[kname_len] = '\0';
- len = UBIFS_DENT_NODE_SZ + kname_len + 1;
+ dent_key_init(c, &key, dir_inum, kname, *kname_len);
+ dent->nlen = cpu_to_le16(*kname_len);
+ memcpy(dent->name, kname, *kname_len);
+ dent->name[*kname_len] = '\0';
+ len = UBIFS_DENT_NODE_SZ + *kname_len + 1;
key_write(&key, dent->key);
- return add_node(&key, kname, kname_len, dent, len);
+ return add_node(&key, kname, *kname_len, dent, len);
}
/**
@@ -2026,7 +2028,7 @@ static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st,
{
struct dirent *entry;
DIR *dir = NULL;
- int err = 0;
+ int kname_len, err = 0;
loff_t size = UBIFS_INO_NODE_SZ;
char *name = NULL;
unsigned int nlink = 2;
@@ -2139,13 +2141,13 @@ static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st,
goto out_free;
}
- err = add_dent_node(dir_inum, entry->d_name, inum, type, fctx);
+ err = add_dent_node(dir_inum, entry->d_name, inum, type, fctx,
+ &kname_len);
if (err) {
free_fscrypt_context(new_fctx);
goto out_free;
}
- size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1,
- 8);
+ size += ALIGN(UBIFS_DENT_NODE_SZ + kname_len + 1, 8);
if (new_fctx)
free_fscrypt_context(new_fctx);
@@ -2210,13 +2212,14 @@ static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st,
goto out_free;
}
- err = add_dent_node(dir_inum, nh_elt->name, inum, type, fctx);
+ err = add_dent_node(dir_inum, nh_elt->name, inum, type, fctx,
+ &kname_len);
if (err) {
free_fscrypt_context(new_fctx);
goto out_free;
}
- size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8);
+ size += ALIGN(UBIFS_DENT_NODE_SZ + kname_len + 1, 8);
nh_elt = next_name_htbl_element(ph_elt, &itr);
if (new_fctx)
--
2.13.6
Add ubi_leb_map() implementation, it is used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
include/libubi.h | 15 +++++++++++++++
lib/libubi.c | 10 ++++++++++
2 files changed, 25 insertions(+)
diff --git a/include/libubi.h b/include/libubi.h
index e1e234e1..213948d5 100644
--- a/include/libubi.h
+++ b/include/libubi.h
@@ -487,6 +487,21 @@ int ubi_leb_unmap(int fd, int lnum);
*/
int ubi_is_mapped(int fd, int lnum);
+/**
+ * ubi_leb_map - map logical eraseblock to a physical eraseblock.
+ * @fd: volume character device file descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function maps an un-mapped logical eraseblock @lnum to a physical
+ * eraseblock. This means, that after a successful invocation of this
+ * function the logical eraseblock @lnum will be empty (contain only %0xFF
+ * bytes) and be mapped to a physical eraseblock, even if an unclean reboot
+ * happens.
+ *
+ * This function returns zero in case of success, %-1 in case of failures.
+ */
+int ubi_leb_map(int fd, int lnum);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libubi.c b/lib/libubi.c
index 6b57e50d..86736dd5 100644
--- a/lib/libubi.c
+++ b/lib/libubi.c
@@ -1364,3 +1364,13 @@ int ubi_is_mapped(int fd, int lnum)
{
return ioctl(fd, UBI_IOCEBISMAP, &lnum);
}
+
+int ubi_leb_map(int fd, int lnum)
+{
+ struct ubi_map_req r;
+
+ memset(&r, 0, sizeof(struct ubi_map_req));
+ r.lnum = lnum;
+
+ return ioctl(fd, UBI_IOCEBMAP, &r);
+}
--
2.13.6
Add common definitions in linux kernel, which are used in UBIFS linux
kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/common/defs.h | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index 548d9dfb..506f752c 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -11,8 +11,11 @@
#include <unistd.h>
#include <limits.h>
#include <errno.h>
+#include <time.h>
+#include <assert.h>
#include <execinfo.h>
+#include "linux_types.h"
#include "ubifs.h"
/* common.h requires the PROGRAM_NAME macro */
@@ -76,8 +79,41 @@ static inline void dump_stack(void)
free(strings);
}
+static inline u32 get_random_u32(void)
+{
+ srand(time(NULL));
+ return rand();
+}
+
+static inline time_t ktime_get_seconds()
+{
+ return time(NULL);
+}
+
+#define likely(x) (x)
#define unlikely(x) (x)
+#define cond_resched() do {} while(0)
+
+#define BUG() do { \
+ assert(0); \
+} while(0)
+#define BUG_ON(cond) do { \
+ assert(!cond); \
+} while(0)
+
+#define smp_wmb() do {} while(0)
+#define smp_rmb() do {} while(0)
+#define smp_mb__before_atomic() do {} while(0)
+#define smp_mb__after_atomic() do {} while(0)
+
+#define min3(x, y, z) min((typeof(x))min(x, y), z)
+
+static inline u64 div_u64(u64 dividend, u32 divisor)
+{
+ return dividend / divisor;
+}
+
#define do_div(n,base) ({ \
int __res; \
__res = ((unsigned long) n) % (unsigned) base; \
--
2.13.6
There are serveral new libs(eg. atomic, kmem, bitops. etc.) have
been imported into ubifs-utils, add descriptions for these libs
in README.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/common/README | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/ubifs-utils/common/README b/ubifs-utils/common/README
index 8c10fd4b..32d04e3a 100644
--- a/ubifs-utils/common/README
+++ b/ubifs-utils/common/README
@@ -7,3 +7,11 @@ Common Library
* defs.h is a bunch of definitions to smooth things over.
* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended.
* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/
+* atomic.h was downloaded from https://the-linux-channel.the-toffee-project.org/index.php?page=6-tutorials-linux-user-space-atomic-operations
+* bitops.h and bitops.c were copied from the linux kernel.
+* compiler_attributes.h was copied from the linux kernel.
+* linux_types.h was copied from the linux kernel.
+* linux_err.h was copied from the linux kernel.
+* hexdump.c was copied from the linux kernel.
+* kmem.h and kmem.c were partial copied from xfsprogs-dev (https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/)
+* sort.h and sort.c were copied from the linux kernel.
--
2.13.6
Adapt log.c in libubifs, compared with linux kernel implementations:
1. Remove debug related implementations(eg. dbg_check_bud_bytes), debug
functions are not needed by fsck, because fsck will check fs in
another way.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/log.c | 29 ++++++++---------------------
1 file changed, 8 insertions(+), 21 deletions(-)
diff --git a/ubifs-utils/libubifs/log.c b/ubifs-utils/libubifs/log.c
index b6ac9c42..0d459261 100644
--- a/ubifs-utils/libubifs/log.c
+++ b/ubifs-utils/libubifs/log.c
@@ -15,7 +15,13 @@
* journal.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
static int dbg_check_bud_bytes(struct ubifs_info *c);
@@ -737,26 +743,7 @@ out_free:
* ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in
* case of failure.
*/
-static int dbg_check_bud_bytes(struct ubifs_info *c)
+static int dbg_check_bud_bytes(__unused struct ubifs_info *c)
{
- int i, err = 0;
- struct ubifs_bud *bud;
- long long bud_bytes = 0;
-
- if (!dbg_is_chk_gen(c))
- return 0;
-
- spin_lock(&c->buds_lock);
- for (i = 0; i < c->jhead_cnt; i++)
- list_for_each_entry(bud, &c->jheads[i].buds_list, list)
- bud_bytes += c->leb_size - bud->start;
-
- if (c->bud_bytes != bud_bytes) {
- ubifs_err(c, "bad bud_bytes %lld, calculated %lld",
- c->bud_bytes, bud_bytes);
- err = -EINVAL;
- }
- spin_unlock(&c->buds_lock);
-
- return err;
+ return 0;
}
--
2.13.6
Adapt budget.c in libubifs, compared with linux kernel implementations:
1. Remove writeback related functions, there are no dirty pages/inodes
for UBIFS in userspace process.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/budget.c | 133 +++---------------------------------------
1 file changed, 7 insertions(+), 126 deletions(-)
diff --git a/ubifs-utils/libubifs/budget.c b/ubifs-utils/libubifs/budget.c
index d76eb7b3..5550c9af 100644
--- a/ubifs-utils/libubifs/budget.c
+++ b/ubifs-utils/libubifs/budget.c
@@ -18,9 +18,11 @@
* approximations are used.
*/
+#include "bitops.h"
#include "ubifs.h"
-#include <linux/writeback.h>
-#include <linux/math64.h>
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
/*
* When pessimistic budget calculations say that there is no enough space,
@@ -30,31 +32,6 @@
*/
#define MAX_MKSPC_RETRIES 3
-/*
- * The below constant defines amount of dirty pages which should be written
- * back at when trying to shrink the liability.
- */
-#define NR_TO_WRITE 16
-
-/**
- * shrink_liability - write-back some dirty pages/inodes.
- * @c: UBIFS file-system description object
- * @nr_to_write: how many dirty pages to write-back
- *
- * This function shrinks UBIFS liability by means of writing back some amount
- * of dirty inodes and their pages.
- *
- * Note, this function synchronizes even VFS inodes which are locked
- * (@i_mutex) by the caller of the budgeting function, because write-back does
- * not touch @i_mutex.
- */
-static void shrink_liability(struct ubifs_info *c, int nr_to_write)
-{
- down_read(&c->vfs_sb->s_umount);
- writeback_inodes_sb_nr(c->vfs_sb, nr_to_write, WB_REASON_FS_FREE_SPACE);
- up_read(&c->vfs_sb->s_umount);
-}
-
/**
* run_gc - run garbage collector.
* @c: UBIFS file-system description object
@@ -80,23 +57,6 @@ static int run_gc(struct ubifs_info *c)
}
/**
- * get_liability - calculate current liability.
- * @c: UBIFS file-system description object
- *
- * This function calculates and returns current UBIFS liability, i.e. the
- * amount of bytes UBIFS has "promised" to write to the media.
- */
-static long long get_liability(struct ubifs_info *c)
-{
- long long liab;
-
- spin_lock(&c->space_lock);
- liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth;
- spin_unlock(&c->space_lock);
- return liab;
-}
-
-/**
* make_free_space - make more free space on the file-system.
* @c: UBIFS file-system description object
*
@@ -117,24 +77,8 @@ static long long get_liability(struct ubifs_info *c)
static int make_free_space(struct ubifs_info *c)
{
int err, retries = 0;
- long long liab1, liab2;
do {
- liab1 = get_liability(c);
- /*
- * We probably have some dirty pages or inodes (liability), try
- * to write them back.
- */
- dbg_budg("liability %lld, run write-back", liab1);
- shrink_liability(c, NR_TO_WRITE);
-
- liab2 = get_liability(c);
- if (liab2 < liab1)
- return -EAGAIN;
-
- dbg_budg("new liability %lld (not shrunk)", liab2);
-
- /* Liability did not shrink again, try GC */
dbg_budg("Run GC");
err = run_gc(c);
if (!err)
@@ -254,12 +198,10 @@ long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
* This function checks whether current user is allowed to use reserved pool.
* Returns %1 current user is allowed to use reserved pool and %0 otherwise.
*/
-static int can_use_rp(struct ubifs_info *c)
+static int can_use_rp(__unused struct ubifs_info *c)
{
- if (uid_eq(current_fsuid(), c->rp_uid) || capable(CAP_SYS_RESOURCE) ||
- (!gid_eq(c->rp_gid, GLOBAL_ROOT_GID) && in_group_p(c->rp_gid)))
- return 1;
- return 0;
+ /* Fsck can always use reserved pool. */
+ return c->program_type == FSCK_PROGRAM_TYPE;
}
/**
@@ -556,49 +498,6 @@ void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
}
/**
- * ubifs_convert_page_budget - convert budget of a new page.
- * @c: UBIFS file-system description object
- *
- * This function converts budget which was allocated for a new page of data to
- * the budget of changing an existing page of data. The latter is smaller than
- * the former, so this function only does simple re-calculation and does not
- * involve any write-back.
- */
-void ubifs_convert_page_budget(struct ubifs_info *c)
-{
- spin_lock(&c->space_lock);
- /* Release the index growth reservation */
- c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
- /* Release the data growth reservation */
- c->bi.data_growth -= c->bi.page_budget;
- /* Increase the dirty data growth reservation instead */
- c->bi.dd_growth += c->bi.page_budget;
- /* And re-calculate the indexing space reservation */
- c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
- spin_unlock(&c->space_lock);
-}
-
-/**
- * ubifs_release_dirty_inode_budget - release dirty inode budget.
- * @c: UBIFS file-system description object
- * @ui: UBIFS inode to release the budget for
- *
- * This function releases budget corresponding to a dirty inode. It is usually
- * called when after the inode has been written to the media and marked as
- * clean. It also causes the "no space" flags to be cleared.
- */
-void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
- struct ubifs_inode *ui)
-{
- struct ubifs_budget_req req;
-
- memset(&req, 0, sizeof(struct ubifs_budget_req));
- /* The "no space" flags will be cleared because dd_growth is > 0 */
- req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8);
- ubifs_release_budget(c, &req);
-}
-
-/**
* ubifs_reported_space - calculate reported free space.
* @c: the UBIFS file-system description object
* @free: amount of free space
@@ -694,21 +593,3 @@ long long ubifs_get_free_space_nolock(struct ubifs_info *c)
free = 0;
return free;
}
-
-/**
- * ubifs_get_free_space - return amount of free space.
- * @c: UBIFS file-system description object
- *
- * This function calculates and returns amount of free space to report to
- * user-space.
- */
-long long ubifs_get_free_space(struct ubifs_info *c)
-{
- long long free;
-
- spin_lock(&c->space_lock);
- free = ubifs_get_free_space_nolock(c);
- spin_unlock(&c->space_lock);
-
- return free;
-}
--
2.13.6
Adapt master.c in libubifs, compared with linux kernel implementations:
1. Remove authentication related implementations
(eg. mst_node_check_hash), authentication is not supported in fsck
for now.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/master.c | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/ubifs-utils/libubifs/master.c b/ubifs-utils/libubifs/master.c
index 7adc37c1..cce1a415 100644
--- a/ubifs-utils/libubifs/master.c
+++ b/ubifs-utils/libubifs/master.c
@@ -10,7 +10,11 @@
/* This file implements reading and writing the master node */
+#include "linux_err.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
/**
* ubifs_compare_master_node - compare two UBIFS master nodes
@@ -21,7 +25,7 @@
* This function compares two UBIFS master nodes. Returns 0 if they are equal
* and nonzero if not.
*/
-int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
+int ubifs_compare_master_node(__unused struct ubifs_info *c, void *m1, void *m2)
{
int ret;
int behind;
@@ -61,20 +65,11 @@ int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
*
* Returns 0 if the hashes are equal, a negative error code otherwise.
*/
-static int mst_node_check_hash(const struct ubifs_info *c,
- const struct ubifs_mst_node *mst,
- const u8 *expected)
+static int mst_node_check_hash(__unused const struct ubifs_info *c,
+ __unused const struct ubifs_mst_node *mst,
+ __unused const u8 *expected)
{
- u8 calc[UBIFS_MAX_HASH_LEN];
- const void *node = mst;
-
- crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch),
- UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch),
- calc);
-
- if (ubifs_check_hash(c, expected, calc))
- return -EPERM;
-
+ // To be implemented
return 0;
}
--
2.13.6
Adapt misc.h in libubifs, compared with linux kernel implementations:
1. Remove some functions(eg. ubifs_compr_name, ubifs_wake_up_bgt) which
won't be used in fsck/mkfs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/misc.h | 75 +--------------------------------------------
1 file changed, 1 insertion(+), 74 deletions(-)
diff --git a/ubifs-utils/libubifs/misc.h b/ubifs-utils/libubifs/misc.h
index 615878e8..4b718068 100644
--- a/ubifs-utils/libubifs/misc.h
+++ b/ubifs-utils/libubifs/misc.h
@@ -50,18 +50,6 @@ static inline int ubifs_zn_cow(const struct ubifs_znode *znode)
}
/**
- * ubifs_wake_up_bgt - wake up background thread.
- * @c: UBIFS file-system description object
- */
-static inline void ubifs_wake_up_bgt(struct ubifs_info *c)
-{
- if (c->bgt && !c->need_bgt) {
- c->need_bgt = 1;
- wake_up_process(c->bgt);
- }
-}
-
-/**
* ubifs_tnc_find_child - find next child in znode.
* @znode: znode to search at
* @start: the zbranch index to start at
@@ -82,42 +70,6 @@ ubifs_tnc_find_child(struct ubifs_znode *znode, int start)
}
/**
- * ubifs_inode - get UBIFS inode information by VFS 'struct inode' object.
- * @inode: the VFS 'struct inode' pointer
- */
-static inline struct ubifs_inode *ubifs_inode(const struct inode *inode)
-{
- return container_of(inode, struct ubifs_inode, vfs_inode);
-}
-
-/**
- * ubifs_compr_present - check if compressor was compiled in.
- * @compr_type: compressor type to check
- * @c: the UBIFS file-system description object
- *
- * This function returns %1 of compressor of type @compr_type is present, and
- * %0 if not.
- */
-static inline int ubifs_compr_present(struct ubifs_info *c, int compr_type)
-{
- ubifs_assert(c, compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
- return !!ubifs_compressors[compr_type]->capi_name;
-}
-
-/**
- * ubifs_compr_name - get compressor name string by its type.
- * @compr_type: compressor type
- * @c: the UBIFS file-system description object
- *
- * This function returns compressor type string.
- */
-static inline const char *ubifs_compr_name(struct ubifs_info *c, int compr_type)
-{
- ubifs_assert(c, compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
- return ubifs_compressors[compr_type]->name;
-}
-
-/**
* ubifs_wbuf_sync - synchronize write-buffer.
* @wbuf: write-buffer to synchronize
*
@@ -135,21 +87,6 @@ static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf)
}
/**
- * ubifs_encode_dev - encode device node IDs.
- * @dev: UBIFS device node information
- * @rdev: device IDs to encode
- *
- * This is a helper function which encodes major/minor numbers of a device node
- * into UBIFS device node description. We use standard Linux "new" and "huge"
- * encodings.
- */
-static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev)
-{
- dev->new = cpu_to_le32(new_encode_dev(rdev));
- return sizeof(dev->new);
-}
-
-/**
* ubifs_add_dirt - add dirty space to LEB properties.
* @c: the UBIFS file-system description object
* @lnum: LEB to add dirty space for
@@ -209,7 +146,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
* @c: the UBIFS file-system description object
* @idx: index node
*/
-static inline void *ubifs_idx_key(const struct ubifs_info *c,
+static inline void *ubifs_idx_key(__unused const struct ubifs_info *c,
const struct ubifs_idx_node *idx)
{
return (void *)((struct ubifs_branch *)idx->branches)->key;
@@ -276,14 +213,4 @@ static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
return lnum;
}
-static inline int ubifs_xattr_max_cnt(struct ubifs_info *c)
-{
- int max_xattrs = (c->leb_size / 2) / UBIFS_INO_NODE_SZ;
-
- ubifs_assert(c, max_xattrs < c->max_orphans);
- return max_xattrs;
-}
-
-const char *ubifs_assert_action_name(struct ubifs_info *c);
-
#endif /* __UBIFS_MISC_H__ */
--
2.13.6
Adapt commit.c in libubifs, compared with linux kernel implementations:
1. Remove debug related implementations(eg. dbg_check_old_index), debug
functions are not needed by fsck, because fsck will check fs in
another way.
2. Remove ubifs background commiting related implementations, there will
be no background ubifs threads in fsck.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/commit.c | 370 ++----------------------------------------
1 file changed, 10 insertions(+), 360 deletions(-)
diff --git a/ubifs-utils/libubifs/commit.c b/ubifs-utils/libubifs/commit.c
index 5b3a8400..f3b61136 100644
--- a/ubifs-utils/libubifs/commit.c
+++ b/ubifs-utils/libubifs/commit.c
@@ -31,10 +31,13 @@
* cache.
*/
-#include <linux/freezer.h>
-#include <linux/kthread.h>
-#include <linux/slab.h>
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "debug.h"
+#include "defs.h"
+#include "misc.h"
/*
* nothing_to_commit - check if there is nothing to commit.
@@ -216,7 +219,6 @@ static int do_commit(struct ubifs_info *c)
out_cancel:
spin_lock(&c->cs_lock);
c->cmt_state = COMMIT_RESTING;
- wake_up(&c->cmt_wq);
dbg_cmt("commit end");
spin_unlock(&c->cs_lock);
return 0;
@@ -227,107 +229,12 @@ out:
ubifs_err(c, "commit failed, error %d", err);
spin_lock(&c->cs_lock);
c->cmt_state = COMMIT_BROKEN;
- wake_up(&c->cmt_wq);
spin_unlock(&c->cs_lock);
ubifs_ro_mode(c, err);
return err;
}
/**
- * run_bg_commit - run background commit if it is needed.
- * @c: UBIFS file-system description object
- *
- * This function runs background commit if it is needed. Returns zero in case
- * of success and a negative error code in case of failure.
- */
-static int run_bg_commit(struct ubifs_info *c)
-{
- spin_lock(&c->cs_lock);
- /*
- * Run background commit only if background commit was requested or if
- * commit is required.
- */
- if (c->cmt_state != COMMIT_BACKGROUND &&
- c->cmt_state != COMMIT_REQUIRED)
- goto out;
- spin_unlock(&c->cs_lock);
-
- down_write(&c->commit_sem);
- spin_lock(&c->cs_lock);
- if (c->cmt_state == COMMIT_REQUIRED)
- c->cmt_state = COMMIT_RUNNING_REQUIRED;
- else if (c->cmt_state == COMMIT_BACKGROUND)
- c->cmt_state = COMMIT_RUNNING_BACKGROUND;
- else
- goto out_cmt_unlock;
- spin_unlock(&c->cs_lock);
-
- return do_commit(c);
-
-out_cmt_unlock:
- up_write(&c->commit_sem);
-out:
- spin_unlock(&c->cs_lock);
- return 0;
-}
-
-/**
- * ubifs_bg_thread - UBIFS background thread function.
- * @info: points to the file-system description object
- *
- * This function implements various file-system background activities:
- * o when a write-buffer timer expires it synchronizes the appropriate
- * write-buffer;
- * o when the journal is about to be full, it starts in-advance commit.
- *
- * Note, other stuff like background garbage collection may be added here in
- * future.
- */
-int ubifs_bg_thread(void *info)
-{
- int err;
- struct ubifs_info *c = info;
-
- ubifs_msg(c, "background thread \"%s\" started, PID %d",
- c->bgt_name, current->pid);
- set_freezable();
-
- while (1) {
- if (kthread_should_stop())
- break;
-
- if (try_to_freeze())
- continue;
-
- set_current_state(TASK_INTERRUPTIBLE);
- /* Check if there is something to do */
- if (!c->need_bgt) {
- /*
- * Nothing prevents us from going sleep now and
- * be never woken up and block the task which
- * could wait in 'kthread_stop()' forever.
- */
- if (kthread_should_stop())
- break;
- schedule();
- continue;
- } else
- __set_current_state(TASK_RUNNING);
-
- c->need_bgt = 0;
- err = ubifs_bg_wbufs_sync(c);
- if (err)
- ubifs_ro_mode(c, err);
-
- run_bg_commit(c);
- cond_resched();
- }
-
- ubifs_msg(c, "background thread \"%s\" stops", c->bgt_name);
- return 0;
-}
-
-/**
* ubifs_commit_required - set commit state to "required".
* @c: UBIFS file-system description object
*
@@ -364,17 +271,8 @@ void ubifs_commit_required(struct ubifs_info *c)
* This function is called if the journal is full enough to make a commit
* worthwhile, so background thread is kicked to start it.
*/
-void ubifs_request_bg_commit(struct ubifs_info *c)
+void ubifs_request_bg_commit(__unused struct ubifs_info *c)
{
- spin_lock(&c->cs_lock);
- if (c->cmt_state == COMMIT_RESTING) {
- dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
- dbg_cstate(COMMIT_BACKGROUND));
- c->cmt_state = COMMIT_BACKGROUND;
- spin_unlock(&c->cs_lock);
- ubifs_wake_up_bgt(c);
- } else
- spin_unlock(&c->cs_lock);
}
/**
@@ -385,18 +283,11 @@ void ubifs_request_bg_commit(struct ubifs_info *c)
*/
static int wait_for_commit(struct ubifs_info *c)
{
- dbg_cmt("pid %d goes sleep", current->pid);
-
/*
- * The following sleeps if the condition is false, and will be woken
- * when the commit ends. It is possible, although very unlikely, that we
- * will wake up and see the subsequent commit running, rather than the
- * one we were waiting for, and go back to sleep. However, we will be
- * woken again, so there is no danger of sleeping forever.
+ * All commit operations are executed in synchronization context,
+ * so it is impossible that more than one threads doing commit.
*/
- wait_event(c->cmt_wq, c->cmt_state != COMMIT_RUNNING_BACKGROUND &&
- c->cmt_state != COMMIT_RUNNING_REQUIRED);
- dbg_cmt("commit finished, pid %d woke up", current->pid);
+ ubifs_assert(c, 0);
return 0;
}
@@ -490,244 +381,3 @@ int ubifs_gc_should_commit(struct ubifs_info *c)
spin_unlock(&c->cs_lock);
return ret;
}
-
-/*
- * Everything below is related to debugging.
- */
-
-/**
- * struct idx_node - hold index nodes during index tree traversal.
- * @list: list
- * @iip: index in parent (slot number of this indexing node in the parent
- * indexing node)
- * @upper_key: all keys in this indexing node have to be less or equivalent to
- * this key
- * @idx: index node (8-byte aligned because all node structures must be 8-byte
- * aligned)
- */
-struct idx_node {
- struct list_head list;
- int iip;
- union ubifs_key upper_key;
- struct ubifs_idx_node idx __aligned(8);
-};
-
-/**
- * dbg_old_index_check_init - get information for the next old index check.
- * @c: UBIFS file-system description object
- * @zroot: root of the index
- *
- * This function records information about the index that will be needed for the
- * next old index check i.e. 'dbg_check_old_index()'.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot)
-{
- struct ubifs_idx_node *idx;
- int lnum, offs, len, err = 0;
- struct ubifs_debug_info *d = c->dbg;
-
- d->old_zroot = *zroot;
- lnum = d->old_zroot.lnum;
- offs = d->old_zroot.offs;
- len = d->old_zroot.len;
-
- idx = kmalloc(c->max_idx_node_sz, GFP_NOFS);
- if (!idx)
- return -ENOMEM;
-
- err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
- if (err)
- goto out;
-
- d->old_zroot_level = le16_to_cpu(idx->level);
- d->old_zroot_sqnum = le64_to_cpu(idx->ch.sqnum);
-out:
- kfree(idx);
- return err;
-}
-
-/**
- * dbg_check_old_index - check the old copy of the index.
- * @c: UBIFS file-system description object
- * @zroot: root of the new index
- *
- * In order to be able to recover from an unclean unmount, a complete copy of
- * the index must exist on flash. This is the "old" index. The commit process
- * must write the "new" index to flash without overwriting or destroying any
- * part of the old index. This function is run at commit end in order to check
- * that the old index does indeed exist completely intact.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot)
-{
- int lnum, offs, len, err = 0, last_level, child_cnt;
- int first = 1, iip;
- struct ubifs_debug_info *d = c->dbg;
- union ubifs_key lower_key, upper_key, l_key, u_key;
- unsigned long long last_sqnum;
- struct ubifs_idx_node *idx;
- struct list_head list;
- struct idx_node *i;
- size_t sz;
-
- if (!dbg_is_chk_index(c))
- return 0;
-
- INIT_LIST_HEAD(&list);
-
- sz = sizeof(struct idx_node) + ubifs_idx_node_sz(c, c->fanout) -
- UBIFS_IDX_NODE_SZ;
-
- /* Start at the old zroot */
- lnum = d->old_zroot.lnum;
- offs = d->old_zroot.offs;
- len = d->old_zroot.len;
- iip = 0;
-
- /*
- * Traverse the index tree preorder depth-first i.e. do a node and then
- * its subtrees from left to right.
- */
- while (1) {
- struct ubifs_branch *br;
-
- /* Get the next index node */
- i = kmalloc(sz, GFP_NOFS);
- if (!i) {
- err = -ENOMEM;
- goto out_free;
- }
- i->iip = iip;
- /* Keep the index nodes on our path in a linked list */
- list_add_tail(&i->list, &list);
- /* Read the index node */
- idx = &i->idx;
- err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
- if (err)
- goto out_free;
- /* Validate index node */
- child_cnt = le16_to_cpu(idx->child_cnt);
- if (child_cnt < 1 || child_cnt > c->fanout) {
- err = 1;
- goto out_dump;
- }
- if (first) {
- first = 0;
- /* Check root level and sqnum */
- if (le16_to_cpu(idx->level) != d->old_zroot_level) {
- err = 2;
- goto out_dump;
- }
- if (le64_to_cpu(idx->ch.sqnum) != d->old_zroot_sqnum) {
- err = 3;
- goto out_dump;
- }
- /* Set last values as though root had a parent */
- last_level = le16_to_cpu(idx->level) + 1;
- last_sqnum = le64_to_cpu(idx->ch.sqnum) + 1;
- key_read(c, ubifs_idx_key(c, idx), &lower_key);
- highest_ino_key(c, &upper_key, INUM_WATERMARK);
- }
- key_copy(c, &upper_key, &i->upper_key);
- if (le16_to_cpu(idx->level) != last_level - 1) {
- err = 3;
- goto out_dump;
- }
- /*
- * The index is always written bottom up hence a child's sqnum
- * is always less than the parents.
- */
- if (le64_to_cpu(idx->ch.sqnum) >= last_sqnum) {
- err = 4;
- goto out_dump;
- }
- /* Check key range */
- key_read(c, ubifs_idx_key(c, idx), &l_key);
- br = ubifs_idx_branch(c, idx, child_cnt - 1);
- key_read(c, &br->key, &u_key);
- if (keys_cmp(c, &lower_key, &l_key) > 0) {
- err = 5;
- goto out_dump;
- }
- if (keys_cmp(c, &upper_key, &u_key) < 0) {
- err = 6;
- goto out_dump;
- }
- if (keys_cmp(c, &upper_key, &u_key) == 0)
- if (!is_hash_key(c, &u_key)) {
- err = 7;
- goto out_dump;
- }
- /* Go to next index node */
- if (le16_to_cpu(idx->level) == 0) {
- /* At the bottom, so go up until can go right */
- while (1) {
- /* Drop the bottom of the list */
- list_del(&i->list);
- kfree(i);
- /* No more list means we are done */
- if (list_empty(&list))
- goto out;
- /* Look at the new bottom */
- i = list_entry(list.prev, struct idx_node,
- list);
- idx = &i->idx;
- /* Can we go right */
- if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
- iip = iip + 1;
- break;
- } else
- /* Nope, so go up again */
- iip = i->iip;
- }
- } else
- /* Go down left */
- iip = 0;
- /*
- * We have the parent in 'idx' and now we set up for reading the
- * child pointed to by slot 'iip'.
- */
- last_level = le16_to_cpu(idx->level);
- last_sqnum = le64_to_cpu(idx->ch.sqnum);
- br = ubifs_idx_branch(c, idx, iip);
- lnum = le32_to_cpu(br->lnum);
- offs = le32_to_cpu(br->offs);
- len = le32_to_cpu(br->len);
- key_read(c, &br->key, &lower_key);
- if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
- br = ubifs_idx_branch(c, idx, iip + 1);
- key_read(c, &br->key, &upper_key);
- } else
- key_copy(c, &i->upper_key, &upper_key);
- }
-out:
- err = dbg_old_index_check_init(c, zroot);
- if (err)
- goto out_free;
-
- return 0;
-
-out_dump:
- ubifs_err(c, "dumping index node (iip=%d)", i->iip);
- ubifs_dump_node(c, idx, ubifs_idx_node_sz(c, c->fanout));
- list_del(&i->list);
- kfree(i);
- if (!list_empty(&list)) {
- i = list_entry(list.prev, struct idx_node, list);
- ubifs_err(c, "dumping parent index node");
- ubifs_dump_node(c, &i->idx, ubifs_idx_node_sz(c, c->fanout));
- }
-out_free:
- while (!list_empty(&list)) {
- i = list_entry(list.next, struct idx_node, list);
- list_del(&i->list);
- kfree(i);
- }
- ubifs_err(c, "failed, error %d", err);
- if (err > 0)
- err = -EINVAL;
- return err;
-}
--
2.13.6
Adapt recovery subsystem(replay.c, recovery.c) in libubifs, compared with
linux kernel implementations:
1. Remove authentication related implementations
(eg. authenticate_sleb_hash), authentication is not supported in fsck
for now.
2. Add explicit type conversions(const char *) to avoid compiling
warnings.
3. Replace implementations of inode_fix_size() with ubifs_assert(0),
authentication is not supported in fsck, so this function won't
be invoked.
4. Remove unused ubifs_clean_lebs() and ubifs_write_rcvrd_mst_node().
5. Adapt fix_unclean_leb/recover_head/fix_size_in_place to ignore
%-EBADMSG, subsequent steps will check nodes in lpt/main area
carefully.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/recovery.c | 236 ++++------------------------------------
ubifs-utils/libubifs/replay.c | 90 +++------------
2 files changed, 35 insertions(+), 291 deletions(-)
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
index f0d51dd2..910414cb 100644
--- a/ubifs-utils/libubifs/recovery.c
+++ b/ubifs-utils/libubifs/recovery.c
@@ -35,9 +35,17 @@
* refuses to mount.
*/
-#include <linux/crc32.h>
-#include <linux/slab.h>
+#include <sys/types.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "crc32.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/**
* is_empty - determine whether a buffer is empty (contains all 0xff).
@@ -364,31 +372,6 @@ out_free:
}
/**
- * ubifs_write_rcvrd_mst_node - write the recovered master node.
- * @c: UBIFS file-system description object
- *
- * This function writes the master node that was recovered during mounting in
- * read-only mode and must now be written because we are remounting rw.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int ubifs_write_rcvrd_mst_node(struct ubifs_info *c)
-{
- int err;
-
- if (!c->rcvrd_mst_node)
- return 0;
- c->rcvrd_mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
- c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
- err = write_rcvrd_mst_node(c, c->rcvrd_mst_node);
- if (err)
- return err;
- kfree(c->rcvrd_mst_node);
- c->rcvrd_mst_node = NULL;
- return 0;
-}
-
-/**
* is_last_write - determine if an offset was in the last write to a LEB.
* @c: UBIFS file-system description object
* @buf: buffer to check
@@ -530,7 +513,7 @@ static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
if (start) {
err = ubifs_leb_read(c, lnum, sleb->buf, 0,
start, 1);
- if (err)
+ if (err && err != -EBADMSG)
return err;
}
/* Pad to min_io_size */
@@ -926,7 +909,7 @@ static int recover_head(struct ubifs_info *c, int lnum, int offs, void *sbuf)
if (offs == 0)
return ubifs_leb_unmap(c, lnum);
err = ubifs_leb_read(c, lnum, sbuf, 0, offs, 1);
- if (err)
+ if (err && err != -EBADMSG)
return err;
return ubifs_leb_change(c, lnum, sbuf, offs);
}
@@ -968,129 +951,6 @@ int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf)
}
/**
- * clean_an_unclean_leb - read and write a LEB to remove corruption.
- * @c: UBIFS file-system description object
- * @ucleb: unclean LEB information
- * @sbuf: LEB-sized buffer to use
- *
- * This function reads a LEB up to a point pre-determined by the mount recovery,
- * checks the nodes, and writes the result back to the flash, thereby cleaning
- * off any following corruption, or non-fatal ECC errors.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-static int clean_an_unclean_leb(struct ubifs_info *c,
- struct ubifs_unclean_leb *ucleb, void *sbuf)
-{
- int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1;
- void *buf = sbuf;
-
- dbg_rcvry("LEB %d len %d", lnum, len);
-
- if (len == 0) {
- /* Nothing to read, just unmap it */
- return ubifs_leb_unmap(c, lnum);
- }
-
- err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
- if (err && err != -EBADMSG)
- return err;
-
- while (len >= 8) {
- int ret;
-
- cond_resched();
-
- /* Scan quietly until there is an error */
- ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
-
- if (ret == SCANNED_A_NODE) {
- /* A valid node, and not a padding node */
- struct ubifs_ch *ch = buf;
- int node_len;
-
- node_len = ALIGN(le32_to_cpu(ch->len), 8);
- offs += node_len;
- buf += node_len;
- len -= node_len;
- continue;
- }
-
- if (ret > 0) {
- /* Padding bytes or a valid padding node */
- offs += ret;
- buf += ret;
- len -= ret;
- continue;
- }
-
- if (ret == SCANNED_EMPTY_SPACE) {
- ubifs_err(c, "unexpected empty space at %d:%d",
- lnum, offs);
- return -EUCLEAN;
- }
-
- if (quiet) {
- /* Redo the last scan but noisily */
- quiet = 0;
- continue;
- }
-
- ubifs_scanned_corruption(c, lnum, offs, buf);
- return -EUCLEAN;
- }
-
- /* Pad to min_io_size */
- len = ALIGN(ucleb->endpt, c->min_io_size);
- if (len > ucleb->endpt) {
- int pad_len = len - ALIGN(ucleb->endpt, 8);
-
- if (pad_len > 0) {
- buf = c->sbuf + len - pad_len;
- ubifs_pad(c, buf, pad_len);
- }
- }
-
- /* Write back the LEB atomically */
- err = ubifs_leb_change(c, lnum, sbuf, len);
- if (err)
- return err;
-
- dbg_rcvry("cleaned LEB %d", lnum);
-
- return 0;
-}
-
-/**
- * ubifs_clean_lebs - clean LEBs recovered during read-only mount.
- * @c: UBIFS file-system description object
- * @sbuf: LEB-sized buffer to use
- *
- * This function cleans a LEB identified during recovery that needs to be
- * written but was not because UBIFS was mounted read-only. This happens when
- * remounting to read-write mode.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf)
-{
- dbg_rcvry("recovery");
- while (!list_empty(&c->unclean_leb_list)) {
- struct ubifs_unclean_leb *ucleb;
- int err;
-
- ucleb = list_entry(c->unclean_leb_list.next,
- struct ubifs_unclean_leb, list);
- err = clean_an_unclean_leb(c, ucleb, sbuf);
- if (err)
- return err;
- list_del(&ucleb->list);
- kfree(ucleb);
- }
- return 0;
-}
-
-/**
* grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
* @c: UBIFS file-system description object
*
@@ -1224,7 +1084,6 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
* @i_size: size on inode
* @d_size: maximum size based on data nodes
* @exists: indicates whether the inode exists
- * @inode: inode if pinned in memory awaiting rw mode to fix it
*/
struct size_entry {
struct rb_node rb;
@@ -1232,7 +1091,6 @@ struct size_entry {
loff_t i_size;
loff_t d_size;
int exists;
- struct inode *inode;
};
/**
@@ -1319,7 +1177,6 @@ void ubifs_destroy_size_tree(struct ubifs_info *c)
struct size_entry *e, *n;
rbtree_postorder_for_each_entry_safe(e, n, &c->size_tree, rb) {
- iput(e->inode);
kfree(e);
}
@@ -1422,7 +1279,7 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
return 0;
/* Read the LEB */
err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 1);
- if (err)
+ if (err && err != -EBADMSG)
goto out;
/* Change the size field and recalculate the CRC */
ino = c->sbuf + offs;
@@ -1441,12 +1298,14 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
if (err)
goto out;
dbg_rcvry("inode %lu at %d:%d size %lld -> %lld",
- (unsigned long)e->inum, lnum, offs, i_size, e->d_size);
+ (unsigned long)e->inum, lnum, offs, (long long)i_size,
+ (long long)e->d_size);
return 0;
out:
ubifs_warn(c, "inode %lu failed to fix size %lld -> %lld error %d",
- (unsigned long)e->inum, e->i_size, e->d_size, err);
+ (unsigned long)e->inum, (long long)e->i_size,
+ (long long)e->d_size, err);
return err;
}
@@ -1455,64 +1314,12 @@ out:
* @c: UBIFS file-system description object
* @e: inode size information for recovery
*/
-static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
+static int inode_fix_size(struct ubifs_info *c, __unused struct size_entry *e)
{
- struct inode *inode;
- struct ubifs_inode *ui;
- int err;
-
- if (c->ro_mount)
- ubifs_assert(c, !e->inode);
-
- if (e->inode) {
- /* Remounting rw, pick up inode we stored earlier */
- inode = e->inode;
- } else {
- inode = ubifs_iget(c->vfs_sb, e->inum);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- if (inode->i_size >= e->d_size) {
- /*
- * The original inode in the index already has a size
- * big enough, nothing to do
- */
- iput(inode);
- return 0;
- }
-
- dbg_rcvry("ino %lu size %lld -> %lld",
- (unsigned long)e->inum,
- inode->i_size, e->d_size);
-
- ui = ubifs_inode(inode);
-
- inode->i_size = e->d_size;
- ui->ui_size = e->d_size;
- ui->synced_i_size = e->d_size;
+ ubifs_assert(c, 0);
- e->inode = inode;
- }
-
- /*
- * In readonly mode just keep the inode pinned in memory until we go
- * readwrite. In readwrite mode write the inode to the journal with the
- * fixed size.
- */
- if (c->ro_mount)
- return 0;
-
- err = ubifs_jnl_write_inode(c, inode);
-
- iput(inode);
-
- if (err)
- return err;
-
- rb_erase(&e->rb, &c->size_tree);
- kfree(e);
-
- return 0;
+ // To be implemented
+ return -EINVAL;
}
/**
@@ -1571,7 +1378,6 @@ int ubifs_recover_size(struct ubifs_info *c, bool in_place)
err = fix_size_in_place(c, e);
if (err)
return err;
- iput(e->inode);
} else {
err = inode_fix_size(c, e);
if (err)
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index c59d47fe..ff9efaac 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -20,9 +20,14 @@
* larger is the journal, the more memory its index may consume.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
-#include <linux/list_sort.h>
-#include <crypto/hash.h>
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/**
* struct replay_entry - replay list entry.
@@ -485,7 +490,8 @@ int ubifs_validate_entry(struct ubifs_info *c,
if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 ||
dent->type >= UBIFS_ITYPES_CNT ||
nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
- (key_type == UBIFS_XENT_KEY && strnlen(dent->name, nlen) != nlen) ||
+ (key_type == UBIFS_XENT_KEY &&
+ strnlen((const char*)dent->name, nlen) != nlen) ||
le64_to_cpu(dent->inum) > MAX_INUM) {
ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ?
"directory entry" : "extended attribute entry");
@@ -558,19 +564,6 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
return data == 0xFFFFFFFF;
}
-/* authenticate_sleb_hash is split out for stack usage */
-static int noinline_for_stack
-authenticate_sleb_hash(struct ubifs_info *c,
- struct shash_desc *log_hash, u8 *hash)
-{
- SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
-
- hash_desc->tfm = c->hash_tfm;
-
- ubifs_shash_copy_state(c, log_hash, hash_desc);
- return crypto_shash_final(hash_desc, hash);
-}
-
/**
* authenticate_sleb - authenticate one scan LEB
* @c: UBIFS file-system description object
@@ -588,69 +581,14 @@ authenticate_sleb_hash(struct ubifs_info *c,
* that could be authenticated or a negative error code.
*/
static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
- struct shash_desc *log_hash, int is_last)
+ __unused struct shash_desc *log_hash,
+ __unused int is_last)
{
- int n_not_auth = 0;
- struct ubifs_scan_node *snod;
- int n_nodes = 0;
- int err;
- u8 hash[UBIFS_HASH_ARR_SZ];
- u8 hmac[UBIFS_HMAC_ARR_SZ];
-
if (!ubifs_authenticated(c))
return sleb->nodes_cnt;
- list_for_each_entry(snod, &sleb->nodes, list) {
-
- n_nodes++;
-
- if (snod->type == UBIFS_AUTH_NODE) {
- struct ubifs_auth_node *auth = snod->node;
-
- err = authenticate_sleb_hash(c, log_hash, hash);
- if (err)
- goto out;
-
- err = crypto_shash_tfm_digest(c->hmac_tfm, hash,
- c->hash_len, hmac);
- if (err)
- goto out;
-
- err = ubifs_check_hmac(c, auth->hmac, hmac);
- if (err) {
- err = -EPERM;
- goto out;
- }
- n_not_auth = 0;
- } else {
- err = crypto_shash_update(log_hash, snod->node,
- snod->len);
- if (err)
- goto out;
- n_not_auth++;
- }
- }
-
- /*
- * A powercut can happen when some nodes were written, but not yet
- * the corresponding authentication node. This may only happen on
- * the last bud though.
- */
- if (n_not_auth) {
- if (is_last) {
- dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them",
- n_not_auth, sleb->lnum);
- err = 0;
- } else {
- dbg_mnt("%d unauthenticated nodes found on non-last LEB %d",
- n_not_auth, sleb->lnum);
- err = -EPERM;
- }
- } else {
- err = 0;
- }
-out:
- return err ? err : n_nodes - n_not_auth;
+ // To be implemented
+ return -EINVAL;
}
/**
@@ -768,7 +706,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
goto out_dump;
err = insert_dent(c, lnum, snod->offs, snod->len, hash,
- &snod->key, dent->name,
+ &snod->key, (const char *)dent->name,
le16_to_cpu(dent->nlen), snod->sqnum,
!le64_to_cpu(dent->inum), &used);
break;
--
2.13.6
Add bit operations implementations, because there are some bit operations
(eg. __set_bit, test_bit) used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 2 +
ubifs-utils/common/bitops.c | 37 +++++++++++
ubifs-utils/common/bitops.h | 152 ++++++++++++++++++++++++++++++++++++++++++++
ubifs-utils/common/defs.h | 36 -----------
ubifs-utils/common/lpt.c | 1 +
5 files changed, 192 insertions(+), 36 deletions(-)
create mode 100644 ubifs-utils/common/bitops.c
create mode 100644 ubifs-utils/common/bitops.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 58162579..9e075071 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -3,6 +3,8 @@ common_SOURCES = \
ubifs-utils/common/linux_types.h \
ubifs-utils/common/linux_err.h \
ubifs-utils/common/atomic.h \
+ ubifs-utils/common/bitops.h \
+ ubifs-utils/common/bitops.c \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
diff --git a/ubifs-utils/common/bitops.c b/ubifs-utils/common/bitops.c
new file mode 100644
index 00000000..c82f1fae
--- /dev/null
+++ b/ubifs-utils/common/bitops.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Realizations of bit operations.
+ */
+
+#include "bitops.h"
+#include "defs.h"
+
+/*
+ * This is a common helper function for find_next_bit and
+ * find_next_zero_bit. The difference is the "invert" argument, which
+ * is XORed with each fetched word before searching it for one bits.
+ */
+unsigned long _find_next_bit(const unsigned long *addr,
+ unsigned long nbits, unsigned long start, unsigned long invert)
+{
+ unsigned long tmp;
+
+ if (!nbits || start >= nbits)
+ return nbits;
+
+ tmp = addr[start / BITS_PER_LONG] ^ invert;
+
+ /* Handle 1st word. */
+ tmp &= BITMAP_FIRST_WORD_MASK(start);
+ start = round_down(start, BITS_PER_LONG);
+
+ while (!tmp) {
+ start += BITS_PER_LONG;
+ if (start >= nbits)
+ return nbits;
+
+ tmp = addr[start / BITS_PER_LONG] ^ invert;
+ }
+
+ return min(start + __ffs(tmp), nbits);
+}
diff --git a/ubifs-utils/common/bitops.h b/ubifs-utils/common/bitops.h
new file mode 100644
index 00000000..3a2d3f87
--- /dev/null
+++ b/ubifs-utils/common/bitops.h
@@ -0,0 +1,152 @@
+#ifndef __BITOPS_H__
+#define __BITOPS_H__
+
+/*
+ * Non-atomic bitops.
+ */
+
+#include <stdbool.h>
+
+#define BITS_PER_LONG __LONG_WIDTH__
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG)
+
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
+
+static inline void __set_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p |= mask;
+}
+
+static inline void set_bit(int nr, volatile unsigned long *addr)
+{
+ __set_bit(nr, addr);
+}
+
+static inline void __clear_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p &= ~mask;
+}
+
+static inline void clear_bit(int nr, volatile unsigned long *addr)
+{
+ __clear_bit(nr, addr);
+}
+
+static inline bool test_bit(int nr, const volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ return (*p & mask) != 0;
+}
+
+/* Sets and returns original value of the bit */
+static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
+{
+ if (test_bit(nr, addr))
+ return 1;
+ set_bit(nr, addr);
+ return 0;
+}
+
+/**
+ * fls - find last (most-significant) bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as ffs.
+ * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
+ */
+static inline int fls(int x)
+{
+ int r = 32;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r -= 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r -= 1;
+ }
+ return r;
+}
+
+/**
+ * __ffs - find first bit in word.
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static inline unsigned long __ffs(unsigned long word)
+{
+ int num = 0;
+
+#if BITS_PER_LONG == 64
+ if ((word & 0xffffffff) == 0) {
+ num += 32;
+ word >>= 32;
+ }
+#endif
+ if ((word & 0xffff) == 0) {
+ num += 16;
+ word >>= 16;
+ }
+ if ((word & 0xff) == 0) {
+ num += 8;
+ word >>= 8;
+ }
+ if ((word & 0xf) == 0) {
+ num += 4;
+ word >>= 4;
+ }
+ if ((word & 0x3) == 0) {
+ num += 2;
+ word >>= 2;
+ }
+ if ((word & 0x1) == 0)
+ num += 1;
+ return num;
+}
+
+unsigned long _find_next_bit(const unsigned long *addr,
+ unsigned long nbits, unsigned long start, unsigned long invert);
+
+/*
+ * Find the next set bit in a memory region.
+ */
+static inline unsigned long find_next_bit(const unsigned long *addr,
+ unsigned long size, unsigned long offset)
+{
+ return _find_next_bit(addr, size, offset, 0UL);
+}
+
+static inline unsigned long find_next_zero_bit(const unsigned long *addr,
+ unsigned long size, unsigned long offset)
+{
+ return _find_next_bit(addr, size, offset, ~0UL);
+}
+
+#endif
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index dd3b806e..485c50c0 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -28,42 +28,6 @@ enum { MKFS_PROGRAM_TYPE = 0 };
#define unlikely(x) (x)
-/**
- * fls - find last (most-significant) bit set
- * @x: the word to search
- *
- * This is defined the same way as ffs.
- * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
- */
-static inline int fls(int x)
-{
- int r = 32;
-
- if (!x)
- return 0;
- if (!(x & 0xffff0000u)) {
- x <<= 16;
- r -= 16;
- }
- if (!(x & 0xff000000u)) {
- x <<= 8;
- r -= 8;
- }
- if (!(x & 0xf0000000u)) {
- x <<= 4;
- r -= 4;
- }
- if (!(x & 0xc0000000u)) {
- x <<= 2;
- r -= 2;
- }
- if (!(x & 0x80000000u)) {
- x <<= 1;
- r -= 1;
- }
- return r;
-}
-
#define do_div(n,base) ({ \
int __res; \
__res = ((unsigned long) n) % (unsigned) base; \
diff --git a/ubifs-utils/common/lpt.c b/ubifs-utils/common/lpt.c
index d07f569f..3c55f91b 100644
--- a/ubifs-utils/common/lpt.c
+++ b/ubifs-utils/common/lpt.c
@@ -25,6 +25,7 @@
#endif
#include "lpt.h"
+#include "bitops.h"
#include "defs.h"
#include "ubifs.h"
#include "crc16.h"
--
2.13.6
Adapt key.h in libubifs, compared with linux kernel implementations:
1. Add '__unused' modifier for unused parameters to avoid compiling
warnings.
2. Remove some functions(eg. lowest_dent_key, dent_key_init_flash)
which won't be used in fsck/mkfs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/key.h | 109 ++++++++++++---------------------------------
1 file changed, 29 insertions(+), 80 deletions(-)
diff --git a/ubifs-utils/libubifs/key.h b/ubifs-utils/libubifs/key.h
index 8142d9d6..0a35c6bf 100644
--- a/ubifs-utils/libubifs/key.h
+++ b/ubifs-utils/libubifs/key.h
@@ -87,7 +87,7 @@ static inline uint32_t key_test_hash(const char *str, int len)
* @key: key to initialize
* @inum: inode number
*/
-static inline void ino_key_init(const struct ubifs_info *c,
+static inline void ino_key_init(__unused const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
@@ -100,8 +100,8 @@ static inline void ino_key_init(const struct ubifs_info *c,
* @k: key to initialize
* @inum: inode number
*/
-static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
- ino_t inum)
+static inline void ino_key_init_flash(__unused const struct ubifs_info *c,
+ void *k, ino_t inum)
{
union ubifs_key *key = k;
@@ -116,8 +116,8 @@ static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
* @key: key to initialize
* @inum: inode number
*/
-static inline void lowest_ino_key(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum)
+static inline void lowest_ino_key(__unused const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = 0;
@@ -129,8 +129,8 @@ static inline void lowest_ino_key(const struct ubifs_info *c,
* @key: key to initialize
* @inum: inode number
*/
-static inline void highest_ino_key(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum)
+static inline void highest_ino_key(__unused const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = 0xffffffff;
@@ -172,40 +172,6 @@ static inline void dent_key_init_hash(const struct ubifs_info *c,
}
/**
- * dent_key_init_flash - initialize on-flash directory entry key.
- * @c: UBIFS file-system description object
- * @k: key to initialize
- * @inum: parent inode number
- * @nm: direntry name and length
- */
-static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
- ino_t inum,
- const struct fscrypt_name *nm)
-{
- union ubifs_key *key = k;
- uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
-
- ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
- key->j32[0] = cpu_to_le32(inum);
- key->j32[1] = cpu_to_le32(hash |
- (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
- memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
-}
-
-/**
- * lowest_dent_key - get the lowest possible directory entry key.
- * @c: UBIFS file-system description object
- * @key: where to store the lowest key
- * @inum: parent inode number
- */
-static inline void lowest_dent_key(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum)
-{
- key->u32[0] = inum;
- key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
-}
-
-/**
* xent_key_init - initialize extended attribute entry key.
* @c: UBIFS file-system description object
* @key: key to initialize
@@ -224,32 +190,12 @@ static inline void xent_key_init(const struct ubifs_info *c,
}
/**
- * xent_key_init_flash - initialize on-flash extended attribute entry key.
- * @c: UBIFS file-system description object
- * @k: key to initialize
- * @inum: host inode number
- * @nm: extended attribute entry name and length
- */
-static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
- ino_t inum, const struct fscrypt_name *nm)
-{
- union ubifs_key *key = k;
- uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
-
- ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
- key->j32[0] = cpu_to_le32(inum);
- key->j32[1] = cpu_to_le32(hash |
- (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
- memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
-}
-
-/**
* lowest_xent_key - get the lowest possible extended attribute entry key.
* @c: UBIFS file-system description object
* @key: where to store the lowest key
* @inum: host inode number
*/
-static inline void lowest_xent_key(const struct ubifs_info *c,
+static inline void lowest_xent_key(__unused const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
@@ -279,7 +225,7 @@ static inline void data_key_init(const struct ubifs_info *c,
* @inum: inode number
*/
static inline void highest_data_key(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum)
+ union ubifs_key *key, ino_t inum)
{
data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
}
@@ -293,7 +239,7 @@ static inline void highest_data_key(const struct ubifs_info *c,
* Note, UBIFS does not have truncation keys on the media and this function is
* only used for purposes of replay.
*/
-static inline void trun_key_init(const struct ubifs_info *c,
+static inline void trun_key_init(__unused const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
@@ -307,7 +253,7 @@ static inline void trun_key_init(const struct ubifs_info *c,
*
* This is a helper function which marks a @key object as invalid.
*/
-static inline void invalid_key_init(const struct ubifs_info *c,
+static inline void invalid_key_init(__unused const struct ubifs_info *c,
union ubifs_key *key)
{
key->u32[0] = 0xDEADBEAF;
@@ -319,7 +265,7 @@ static inline void invalid_key_init(const struct ubifs_info *c,
* @c: UBIFS file-system description object
* @key: key to get type of
*/
-static inline int key_type(const struct ubifs_info *c,
+static inline int key_type(__unused const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
@@ -330,7 +276,8 @@ static inline int key_type(const struct ubifs_info *c,
* @c: UBIFS file-system description object
* @k: key to get type of
*/
-static inline int key_type_flash(const struct ubifs_info *c, const void *k)
+static inline int key_type_flash(__unused const struct ubifs_info *c,
+ const void *k)
{
const union ubifs_key *key = k;
@@ -342,7 +289,7 @@ static inline int key_type_flash(const struct ubifs_info *c, const void *k)
* @c: UBIFS file-system description object
* @k: key to fetch inode number from
*/
-static inline ino_t key_inum(const struct ubifs_info *c, const void *k)
+static inline ino_t key_inum(__unused const struct ubifs_info *c, const void *k)
{
const union ubifs_key *key = k;
@@ -354,7 +301,8 @@ static inline ino_t key_inum(const struct ubifs_info *c, const void *k)
* @c: UBIFS file-system description object
* @k: key to fetch inode number from
*/
-static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
+static inline ino_t key_inum_flash(__unused const struct ubifs_info *c,
+ const void *k)
{
const union ubifs_key *key = k;
@@ -366,7 +314,7 @@ static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
* @c: UBIFS file-system description object
* @key: the key to get hash from
*/
-static inline uint32_t key_hash(const struct ubifs_info *c,
+static inline uint32_t key_hash(__unused const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
@@ -377,7 +325,8 @@ static inline uint32_t key_hash(const struct ubifs_info *c,
* @c: UBIFS file-system description object
* @k: the key to get hash from
*/
-static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
+static inline uint32_t key_hash_flash(__unused const struct ubifs_info *c,
+ const void *k)
{
const union ubifs_key *key = k;
@@ -389,7 +338,7 @@ static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
* @c: UBIFS file-system description object
* @key: the key to get the block number from
*/
-static inline unsigned int key_block(const struct ubifs_info *c,
+static inline unsigned int key_block(__unused const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
@@ -400,7 +349,7 @@ static inline unsigned int key_block(const struct ubifs_info *c,
* @c: UBIFS file-system description object
* @k: the key to get the block number from
*/
-static inline unsigned int key_block_flash(const struct ubifs_info *c,
+static inline unsigned int key_block_flash(__unused const struct ubifs_info *c,
const void *k)
{
const union ubifs_key *key = k;
@@ -414,8 +363,8 @@ static inline unsigned int key_block_flash(const struct ubifs_info *c,
* @from: the key to transform
* @to: the key to store the result
*/
-static inline void key_read(const struct ubifs_info *c, const void *from,
- union ubifs_key *to)
+static inline void key_read(__unused const struct ubifs_info *c,
+ const void *from, union ubifs_key *to)
{
const union ubifs_key *f = from;
@@ -429,7 +378,7 @@ static inline void key_read(const struct ubifs_info *c, const void *from,
* @from: the key to transform
* @to: the key to store the result
*/
-static inline void key_write(const struct ubifs_info *c,
+static inline void key_write(__unused const struct ubifs_info *c,
const union ubifs_key *from, void *to)
{
union ubifs_key *t = to;
@@ -445,7 +394,7 @@ static inline void key_write(const struct ubifs_info *c,
* @from: the key to transform
* @to: the key to store the result
*/
-static inline void key_write_idx(const struct ubifs_info *c,
+static inline void key_write_idx(__unused const struct ubifs_info *c,
const union ubifs_key *from, void *to)
{
union ubifs_key *t = to;
@@ -460,7 +409,7 @@ static inline void key_write_idx(const struct ubifs_info *c,
* @from: the key to copy from
* @to: the key to copy to
*/
-static inline void key_copy(const struct ubifs_info *c,
+static inline void key_copy(__unused const struct ubifs_info *c,
const union ubifs_key *from, union ubifs_key *to)
{
to->u64[0] = from->u64[0];
@@ -475,7 +424,7 @@ static inline void key_copy(const struct ubifs_info *c,
* This function compares 2 keys and returns %-1 if @key1 is less than
* @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2.
*/
-static inline int keys_cmp(const struct ubifs_info *c,
+static inline int keys_cmp(__unused const struct ubifs_info *c,
const union ubifs_key *key1,
const union ubifs_key *key2)
{
@@ -500,7 +449,7 @@ static inline int keys_cmp(const struct ubifs_info *c,
* This function compares 2 keys and returns %1 if @key1 is equal to @key2 and
* %0 if not.
*/
-static inline int keys_eq(const struct ubifs_info *c,
+static inline int keys_eq(__unused const struct ubifs_info *c,
const union ubifs_key *key1,
const union ubifs_key *key2)
{
--
2.13.6
Adapt auth.c in libubifs, compared with linux kernel implementations:
1. Only keep implementations used by mkfs, other implementations
are removed.
2. Adapt functions based on implementations in common/sign.c.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/auth.c | 534 +++++++-------------------------------------
1 file changed, 82 insertions(+), 452 deletions(-)
diff --git a/ubifs-utils/libubifs/auth.c b/ubifs-utils/libubifs/auth.c
index 0d561ecb..fab1dba4 100644
--- a/ubifs-utils/libubifs/auth.c
+++ b/ubifs-utils/libubifs/auth.c
@@ -9,13 +9,51 @@
* This file implements various helper functions for UBIFS authentication support
*/
-#include <linux/verification.h>
-#include <crypto/hash.h>
-#include <crypto/utils.h>
-#include <keys/user-type.h>
-#include <keys/asymmetric-type.h>
-
+#include "linux_err.h"
#include "ubifs.h"
+#include "sign.h"
+#include "defs.h"
+
+int ubifs_shash_init(const struct ubifs_info *c,
+ __unused struct shash_desc *desc)
+{
+ if (ubifs_authenticated(c))
+ return hash_digest_init();
+ else
+ return 0;
+}
+
+int ubifs_shash_update(const struct ubifs_info *c,
+ __unused struct shash_desc *desc,
+ const void *buf, unsigned int len)
+{
+ int err = 0;
+
+ if (ubifs_authenticated(c)) {
+ err = hash_digest_update(buf, len);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int ubifs_shash_final(const struct ubifs_info *c,
+ __unused struct shash_desc *desc, u8 *out)
+{
+ return ubifs_authenticated(c) ? hash_digest_final(out) : 0;
+}
+
+struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
+{
+ int err;
+
+ err = ubifs_shash_init(c, NULL);
+ if (err)
+ return ERR_PTR(err);
+
+ return NULL;
+}
/**
* ubifs_node_calc_hash - calculate the hash of a UBIFS node
@@ -25,101 +63,47 @@
*
* Returns 0 for success or a negative error code otherwise.
*/
-int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
- u8 *hash)
+int __ubifs_node_calc_hash(__unused const struct ubifs_info *c,
+ const void *node, u8 *hash)
{
const struct ubifs_ch *ch = node;
- return crypto_shash_tfm_digest(c->hash_tfm, node, le32_to_cpu(ch->len),
- hash);
+ return hash_digest(node, le32_to_cpu(ch->len), hash);
}
/**
- * ubifs_hash_calc_hmac - calculate a HMAC from a hash
- * @c: UBIFS file-system description object
- * @hash: the node to calculate a HMAC for
- * @hmac: the returned HMAC
- *
- * Returns 0 for success or a negative error code otherwise.
- */
-static int ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
- u8 *hmac)
-{
- return crypto_shash_tfm_digest(c->hmac_tfm, hash, c->hash_len, hmac);
-}
-
-/**
- * ubifs_prepare_auth_node - Prepare an authentication node
- * @c: UBIFS file-system description object
+ * ubifs_master_node_calc_hash - calculate the hash of a UBIFS master node
* @node: the node to calculate a hash for
- * @inhash: input hash of previous nodes
- *
- * This function prepares an authentication node for writing onto flash.
- * It creates a HMAC from the given input hash and writes it to the node.
- *
- * Returns 0 for success or a negative error code otherwise.
+ * @hash: the returned hash
*/
-int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
- struct shash_desc *inhash)
+int ubifs_master_node_calc_hash(const struct ubifs_info *c, const void *node,
+ uint8_t *hash)
{
- struct ubifs_auth_node *auth = node;
- u8 hash[UBIFS_HASH_ARR_SZ];
- int err;
-
- {
- SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
-
- hash_desc->tfm = c->hash_tfm;
- ubifs_shash_copy_state(c, inhash, hash_desc);
-
- err = crypto_shash_final(hash_desc, hash);
- if (err)
- return err;
- }
-
- err = ubifs_hash_calc_hmac(c, hash, auth->hmac);
- if (err)
- return err;
+ if (!ubifs_authenticated(c))
+ return 0;
- auth->ch.node_type = UBIFS_AUTH_NODE;
- ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
- return 0;
+ return hash_digest(node + sizeof(struct ubifs_ch),
+ UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch), hash);
}
-static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
- struct crypto_shash *tfm)
+int ubifs_sign_superblock_node(struct ubifs_info *c, void *node)
{
- struct shash_desc *desc;
- int err;
+ int err, len;
+ struct ubifs_sig_node *sig = node + UBIFS_SB_NODE_SZ;
if (!ubifs_authenticated(c))
- return NULL;
-
- desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
-
- desc->tfm = tfm;
+ return 0;
- err = crypto_shash_init(desc);
- if (err) {
- kfree(desc);
- return ERR_PTR(err);
- }
+ err = hash_sign_node(c->auth_key_filename, c->auth_cert_filename, node,
+ &len, sig + 1);
+ if (err)
+ return err;
- return desc;
-}
+ sig->type = UBIFS_SIGNATURE_TYPE_PKCS7;
+ sig->len = cpu_to_le32(len);
+ sig->ch.node_type = UBIFS_SIG_NODE;
-/**
- * __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
- * @c: UBIFS file-system description object
- *
- * This function returns a descriptor suitable for hashing a node. Free after use
- * with kfree.
- */
-struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
-{
- return ubifs_get_desc(c, c->hash_tfm);
+ return 0;
}
/**
@@ -150,103 +134,6 @@ void ubifs_bad_hash(const struct ubifs_info *c, const void *node, const u8 *hash
}
/**
- * __ubifs_node_check_hash - check the hash of a node against given hash
- * @c: UBIFS file-system description object
- * @node: the node
- * @expected: the expected hash
- *
- * This function calculates a hash over a node and compares it to the given hash.
- * Returns 0 if both hashes are equal or authentication is disabled, otherwise a
- * negative error code is returned.
- */
-int __ubifs_node_check_hash(const struct ubifs_info *c, const void *node,
- const u8 *expected)
-{
- u8 calc[UBIFS_HASH_ARR_SZ];
- int err;
-
- err = __ubifs_node_calc_hash(c, node, calc);
- if (err)
- return err;
-
- if (ubifs_check_hash(c, expected, calc))
- return -EPERM;
-
- return 0;
-}
-
-/**
- * ubifs_sb_verify_signature - verify the signature of a superblock
- * @c: UBIFS file-system description object
- * @sup: The superblock node
- *
- * To support offline signed images the superblock can be signed with a
- * PKCS#7 signature. The signature is placed directly behind the superblock
- * node in an ubifs_sig_node.
- *
- * Returns 0 when the signature can be successfully verified or a negative
- * error code if not.
- */
-int ubifs_sb_verify_signature(struct ubifs_info *c,
- const struct ubifs_sb_node *sup)
-{
- int err;
- struct ubifs_scan_leb *sleb;
- struct ubifs_scan_node *snod;
- const struct ubifs_sig_node *signode;
-
- sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0);
- if (IS_ERR(sleb)) {
- err = PTR_ERR(sleb);
- return err;
- }
-
- if (sleb->nodes_cnt == 0) {
- ubifs_err(c, "Unable to find signature node");
- err = -EINVAL;
- goto out_destroy;
- }
-
- snod = list_first_entry(&sleb->nodes, struct ubifs_scan_node, list);
-
- if (snod->type != UBIFS_SIG_NODE) {
- ubifs_err(c, "Signature node is of wrong type");
- err = -EINVAL;
- goto out_destroy;
- }
-
- signode = snod->node;
-
- if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) {
- ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len));
- err = -EINVAL;
- goto out_destroy;
- }
-
- if (le32_to_cpu(signode->type) != UBIFS_SIGNATURE_TYPE_PKCS7) {
- ubifs_err(c, "Signature type %d is not supported\n",
- le32_to_cpu(signode->type));
- err = -EINVAL;
- goto out_destroy;
- }
-
- err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node),
- signode->sig, le32_to_cpu(signode->len),
- NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
- NULL, NULL);
-
- if (err)
- ubifs_err(c, "Failed to verify signature");
- else
- ubifs_msg(c, "Successfully verified super block signature");
-
-out_destroy:
- ubifs_scan_destroy(sleb);
-
- return err;
-}
-
-/**
* ubifs_init_authentication - initialize UBIFS authentication support
* @c: UBIFS file-system description object
*
@@ -254,292 +141,35 @@ out_destroy:
*/
int ubifs_init_authentication(struct ubifs_info *c)
{
- struct key *keyring_key;
- const struct user_key_payload *ukp;
- int err;
- char hmac_name[CRYPTO_MAX_ALG_NAME];
+ int err, hash_len, hash_algo;
- if (!c->auth_hash_name) {
- ubifs_err(c, "authentication hash name needed with authentication");
- return -EINVAL;
- }
+ if (!c->auth_key_filename && !c->auth_cert_filename && !c->hash_algo_name)
+ return 0;
- c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
- c->auth_hash_name);
- if ((int)c->auth_hash_algo < 0) {
- ubifs_err(c, "Unknown hash algo %s specified",
- c->auth_hash_name);
+ if (!c->auth_key_filename) {
+ ubifs_err(c, "authentication key not given (--auth-key)");
return -EINVAL;
}
- snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
- c->auth_hash_name);
-
- keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
-
- if (IS_ERR(keyring_key)) {
- ubifs_err(c, "Failed to request key: %ld",
- PTR_ERR(keyring_key));
- return PTR_ERR(keyring_key);
- }
-
- down_read(&keyring_key->sem);
-
- if (keyring_key->type != &key_type_logon) {
- ubifs_err(c, "key type must be logon");
- err = -ENOKEY;
- goto out;
- }
-
- ukp = user_key_payload_locked(keyring_key);
- if (!ukp) {
- /* key was revoked before we acquired its semaphore */
- err = -EKEYREVOKED;
- goto out;
- }
-
- c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0, 0);
- if (IS_ERR(c->hash_tfm)) {
- err = PTR_ERR(c->hash_tfm);
- ubifs_err(c, "Can not allocate %s: %d",
- c->auth_hash_name, err);
- goto out;
- }
-
- c->hash_len = crypto_shash_digestsize(c->hash_tfm);
- if (c->hash_len > UBIFS_HASH_ARR_SZ) {
- ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
- c->auth_hash_name, c->hash_len, UBIFS_HASH_ARR_SZ);
- err = -EINVAL;
- goto out_free_hash;
- }
-
- c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
- if (IS_ERR(c->hmac_tfm)) {
- err = PTR_ERR(c->hmac_tfm);
- ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
- goto out_free_hash;
- }
-
- c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
- if (c->hmac_desc_len > UBIFS_HMAC_ARR_SZ) {
- ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
- hmac_name, c->hmac_desc_len, UBIFS_HMAC_ARR_SZ);
- err = -EINVAL;
- goto out_free_hmac;
- }
-
- err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
- if (err)
- goto out_free_hmac;
-
- c->authenticated = true;
-
- c->log_hash = ubifs_hash_get_desc(c);
- if (IS_ERR(c->log_hash)) {
- err = PTR_ERR(c->log_hash);
- goto out_free_hmac;
- }
-
- err = 0;
-
-out_free_hmac:
- if (err)
- crypto_free_shash(c->hmac_tfm);
-out_free_hash:
- if (err)
- crypto_free_shash(c->hash_tfm);
-out:
- up_read(&keyring_key->sem);
- key_put(keyring_key);
-
- return err;
-}
-
-/**
- * __ubifs_exit_authentication - release resource
- * @c: UBIFS file-system description object
- *
- * This function releases the authentication related resources.
- */
-void __ubifs_exit_authentication(struct ubifs_info *c)
-{
- if (!ubifs_authenticated(c))
- return;
-
- crypto_free_shash(c->hmac_tfm);
- crypto_free_shash(c->hash_tfm);
- kfree(c->log_hash);
-}
-
-/**
- * ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
- * @c: UBIFS file-system description object
- * @node: the node to insert a HMAC into.
- * @len: the length of the node
- * @ofs_hmac: the offset in the node where the HMAC is inserted
- * @hmac: returned HMAC
- *
- * This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
- * embedded into the node, so this area is not covered by the HMAC. Also not
- * covered is the UBIFS_NODE_MAGIC and the CRC of the node.
- */
-static int ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
- int len, int ofs_hmac, void *hmac)
-{
- SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
- int hmac_len = c->hmac_desc_len;
- int err;
-
- ubifs_assert(c, ofs_hmac > 8);
- ubifs_assert(c, ofs_hmac + hmac_len < len);
-
- shash->tfm = c->hmac_tfm;
-
- err = crypto_shash_init(shash);
- if (err)
- return err;
-
- /* behind common node header CRC up to HMAC begin */
- err = crypto_shash_update(shash, node + 8, ofs_hmac - 8);
- if (err < 0)
- return err;
-
- /* behind HMAC, if any */
- if (len - ofs_hmac - hmac_len > 0) {
- err = crypto_shash_update(shash, node + ofs_hmac + hmac_len,
- len - ofs_hmac - hmac_len);
- if (err < 0)
- return err;
+ if (!c->hash_algo_name) {
+ ubifs_err(c, "Hash algorithm not given (--hash-algo)");
+ return -EINVAL;
}
- return crypto_shash_final(shash, hmac);
-}
-
-/**
- * __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
- * @c: UBIFS file-system description object
- * @node: the node to insert a HMAC into.
- * @len: the length of the node
- * @ofs_hmac: the offset in the node where the HMAC is inserted
- *
- * This function inserts a HMAC at offset @ofs_hmac into the node given in
- * @node.
- *
- * This function returns 0 for success or a negative error code otherwise.
- */
-int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
- int ofs_hmac)
-{
- return ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
-}
-
-/**
- * __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
- * @c: UBIFS file-system description object
- * @node: the node to insert a HMAC into.
- * @len: the length of the node
- * @ofs_hmac: the offset in the node where the HMAC is inserted
- *
- * This function verifies the HMAC at offset @ofs_hmac of the node given in
- * @node. Returns 0 if successful or a negative error code otherwise.
- */
-int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
- int len, int ofs_hmac)
-{
- int hmac_len = c->hmac_desc_len;
- u8 *hmac;
- int err;
-
- hmac = kmalloc(hmac_len, GFP_NOFS);
- if (!hmac)
- return -ENOMEM;
-
- err = ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
+ err = init_authentication(c->hash_algo_name, &hash_len, &hash_algo);
if (err) {
- kfree(hmac);
+ ubifs_err(c, "Init authentication failed");
return err;
}
- err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
-
- kfree(hmac);
-
- if (!err)
- return 0;
-
- return -EPERM;
-}
-
-int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
- struct shash_desc *target)
-{
- u8 *state;
- int err;
-
- state = kmalloc(crypto_shash_descsize(src->tfm), GFP_NOFS);
- if (!state)
- return -ENOMEM;
-
- err = crypto_shash_export(src, state);
- if (err)
- goto out;
-
- err = crypto_shash_import(target, state);
-
-out:
- kfree(state);
-
- return err;
-}
-
-/**
- * ubifs_hmac_wkm - Create a HMAC of the well known message
- * @c: UBIFS file-system description object
- * @hmac: The HMAC of the well known message
- *
- * This function creates a HMAC of a well known message. This is used
- * to check if the provided key is suitable to authenticate a UBIFS
- * image. This is only a convenience to the user to provide a better
- * error message when the wrong key is provided.
- *
- * This function returns 0 for success or a negative error code otherwise.
- */
-int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
-{
- SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
- int err;
- const char well_known_message[] = "UBIFS";
-
- if (!ubifs_authenticated(c))
- return 0;
-
- shash->tfm = c->hmac_tfm;
-
- err = crypto_shash_init(shash);
- if (err)
- return err;
-
- err = crypto_shash_update(shash, well_known_message,
- sizeof(well_known_message) - 1);
- if (err < 0)
- return err;
+ c->hash_len = hash_len;
+ c->hash_algo = hash_algo;
+ c->authenticated = 1;
- err = crypto_shash_final(shash, hmac);
- if (err)
- return err;
return 0;
}
-/*
- * ubifs_hmac_zero - test if a HMAC is zero
- * @c: UBIFS file-system description object
- * @hmac: the HMAC to test
- *
- * This function tests if a HMAC is zero and returns true if it is
- * and false otherwise.
- */
-bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac)
+void __ubifs_exit_authentication(__unused struct ubifs_info *c)
{
- return !memchr_inv(hmac, 0, c->hmac_desc_len);
+ exit_authentication();
}
--
2.13.6
Adapt tnc subsystem(tnc.c,tnc_misc.c,tnc_commit.c) in libubifs, compared
with linux kernel implementations:
1. Remove debug related functions(eg. dbg_check_inode_size), debug
functions are not needed by fsck, because fsck will check fs in
another way.
2. Remove some functions(eg. ubifs_tnc_bulk_read) which won't be used
in fsck/mkfs.
3. Adapt tnc_delete and ubifs_search_zbranch to handle empty TNC case,
which could happen in fsck.
4. Don't skip checking the length of non-leaf index node's branch in
read_znode.
5. Adapt try_read_node to ignore %-EBADMSG, subsequent steps will check
nodes carefully.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/tnc.c | 590 +-------------------------------------
ubifs-utils/libubifs/tnc_commit.c | 18 +-
ubifs-utils/libubifs/tnc_misc.c | 108 ++-----
3 files changed, 43 insertions(+), 673 deletions(-)
diff --git a/ubifs-utils/libubifs/tnc.c b/ubifs-utils/libubifs/tnc.c
index 0fabecd9..12c56e0a 100644
--- a/ubifs-utils/libubifs/tnc.c
+++ b/ubifs-utils/libubifs/tnc.c
@@ -18,9 +18,15 @@
* the mutex locked.
*/
-#include <linux/crc32.h>
-#include <linux/slab.h>
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "crc32.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
static int try_read_node(const struct ubifs_info *c, void *buf, int type,
struct ubifs_zbranch *zbr);
@@ -488,7 +494,7 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type,
dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
err = ubifs_leb_read(c, lnum, buf, offs, len, 1);
- if (err) {
+ if (err && err != -EBADMSG) {
ubifs_err(c, "cannot read node type %d from LEB %d:%d, error %d",
type, lnum, offs, err);
return err;
@@ -929,7 +935,7 @@ static int fallible_resolve_collision(struct ubifs_info *c,
int adding)
{
struct ubifs_znode *o_znode = NULL, *znode = *zn;
- int o_n, err, cmp, unsure = 0, nn = *n;
+ int err, cmp, o_n = 0, unsure = 0, nn = *n;
cmp = fallible_matches_name(c, &znode->zbranch[nn], nm);
if (unlikely(cmp < 0))
@@ -1427,42 +1433,6 @@ static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key,
}
/**
- * maybe_leb_gced - determine if a LEB may have been garbage collected.
- * @c: UBIFS file-system description object
- * @lnum: LEB number
- * @gc_seq1: garbage collection sequence number
- *
- * This function determines if @lnum may have been garbage collected since
- * sequence number @gc_seq1. If it may have been then %1 is returned, otherwise
- * %0 is returned.
- */
-static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1)
-{
- int gc_seq2, gced_lnum;
-
- gced_lnum = c->gced_lnum;
- smp_rmb();
- gc_seq2 = c->gc_seq;
- /* Same seq means no GC */
- if (gc_seq1 == gc_seq2)
- return 0;
- /* Different by more than 1 means we don't know */
- if (gc_seq1 + 1 != gc_seq2)
- return 1;
- /*
- * We have seen the sequence number has increased by 1. Now we need to
- * be sure we read the right LEB number, so read it again.
- */
- smp_rmb();
- if (gced_lnum != c->gced_lnum)
- return 1;
- /* Finally we can check lnum */
- if (gced_lnum == lnum)
- return 1;
- return 0;
-}
-
-/**
* ubifs_tnc_locate - look up a file-system node and return it and its location.
* @c: UBIFS file-system description object
* @key: node key to lookup
@@ -1512,300 +1482,6 @@ out:
}
/**
- * ubifs_tnc_get_bu_keys - lookup keys for bulk-read.
- * @c: UBIFS file-system description object
- * @bu: bulk-read parameters and results
- *
- * Lookup consecutive data node keys for the same inode that reside
- * consecutively in the same LEB. This function returns zero in case of success
- * and a negative error code in case of failure.
- *
- * Note, if the bulk-read buffer length (@bu->buf_len) is known, this function
- * makes sure bulk-read nodes fit the buffer. Otherwise, this function prepares
- * maximum possible amount of nodes for bulk-read.
- */
-int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu)
-{
- int n, err = 0, lnum = -1, offs;
- int len;
- unsigned int block = key_block(c, &bu->key);
- struct ubifs_znode *znode;
-
- bu->cnt = 0;
- bu->blk_cnt = 0;
- bu->eof = 0;
-
- mutex_lock(&c->tnc_mutex);
- /* Find first key */
- err = ubifs_lookup_level0(c, &bu->key, &znode, &n);
- if (err < 0)
- goto out;
- if (err) {
- /* Key found */
- len = znode->zbranch[n].len;
- /* The buffer must be big enough for at least 1 node */
- if (len > bu->buf_len) {
- err = -EINVAL;
- goto out;
- }
- /* Add this key */
- bu->zbranch[bu->cnt++] = znode->zbranch[n];
- bu->blk_cnt += 1;
- lnum = znode->zbranch[n].lnum;
- offs = ALIGN(znode->zbranch[n].offs + len, 8);
- }
- while (1) {
- struct ubifs_zbranch *zbr;
- union ubifs_key *key;
- unsigned int next_block;
-
- /* Find next key */
- err = tnc_next(c, &znode, &n);
- if (err)
- goto out;
- zbr = &znode->zbranch[n];
- key = &zbr->key;
- /* See if there is another data key for this file */
- if (key_inum(c, key) != key_inum(c, &bu->key) ||
- key_type(c, key) != UBIFS_DATA_KEY) {
- err = -ENOENT;
- goto out;
- }
- if (lnum < 0) {
- /* First key found */
- lnum = zbr->lnum;
- offs = ALIGN(zbr->offs + zbr->len, 8);
- len = zbr->len;
- if (len > bu->buf_len) {
- err = -EINVAL;
- goto out;
- }
- } else {
- /*
- * The data nodes must be in consecutive positions in
- * the same LEB.
- */
- if (zbr->lnum != lnum || zbr->offs != offs)
- goto out;
- offs += ALIGN(zbr->len, 8);
- len = ALIGN(len, 8) + zbr->len;
- /* Must not exceed buffer length */
- if (len > bu->buf_len)
- goto out;
- }
- /* Allow for holes */
- next_block = key_block(c, key);
- bu->blk_cnt += (next_block - block - 1);
- if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
- goto out;
- block = next_block;
- /* Add this key */
- bu->zbranch[bu->cnt++] = *zbr;
- bu->blk_cnt += 1;
- /* See if we have room for more */
- if (bu->cnt >= UBIFS_MAX_BULK_READ)
- goto out;
- if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
- goto out;
- }
-out:
- if (err == -ENOENT) {
- bu->eof = 1;
- err = 0;
- }
- bu->gc_seq = c->gc_seq;
- mutex_unlock(&c->tnc_mutex);
- if (err)
- return err;
- /*
- * An enormous hole could cause bulk-read to encompass too many
- * page cache pages, so limit the number here.
- */
- if (bu->blk_cnt > UBIFS_MAX_BULK_READ)
- bu->blk_cnt = UBIFS_MAX_BULK_READ;
- /*
- * Ensure that bulk-read covers a whole number of page cache
- * pages.
- */
- if (UBIFS_BLOCKS_PER_PAGE == 1 ||
- !(bu->blk_cnt & (UBIFS_BLOCKS_PER_PAGE - 1)))
- return 0;
- if (bu->eof) {
- /* At the end of file we can round up */
- bu->blk_cnt += UBIFS_BLOCKS_PER_PAGE - 1;
- return 0;
- }
- /* Exclude data nodes that do not make up a whole page cache page */
- block = key_block(c, &bu->key) + bu->blk_cnt;
- block &= ~(UBIFS_BLOCKS_PER_PAGE - 1);
- while (bu->cnt) {
- if (key_block(c, &bu->zbranch[bu->cnt - 1].key) < block)
- break;
- bu->cnt -= 1;
- }
- return 0;
-}
-
-/**
- * read_wbuf - bulk-read from a LEB with a wbuf.
- * @wbuf: wbuf that may overlap the read
- * @buf: buffer into which to read
- * @len: read length
- * @lnum: LEB number from which to read
- * @offs: offset from which to read
- *
- * This functions returns %0 on success or a negative error code on failure.
- */
-static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum,
- int offs)
-{
- const struct ubifs_info *c = wbuf->c;
- int rlen, overlap;
-
- dbg_io("LEB %d:%d, length %d", lnum, offs, len);
- ubifs_assert(c, wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
- ubifs_assert(c, !(offs & 7) && offs < c->leb_size);
- ubifs_assert(c, offs + len <= c->leb_size);
-
- spin_lock(&wbuf->lock);
- overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
- if (!overlap) {
- /* We may safely unlock the write-buffer and read the data */
- spin_unlock(&wbuf->lock);
- return ubifs_leb_read(c, lnum, buf, offs, len, 0);
- }
-
- /* Don't read under wbuf */
- rlen = wbuf->offs - offs;
- if (rlen < 0)
- rlen = 0;
-
- /* Copy the rest from the write-buffer */
- memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
- spin_unlock(&wbuf->lock);
-
- if (rlen > 0)
- /* Read everything that goes before write-buffer */
- return ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
-
- return 0;
-}
-
-/**
- * validate_data_node - validate data nodes for bulk-read.
- * @c: UBIFS file-system description object
- * @buf: buffer containing data node to validate
- * @zbr: zbranch of data node to validate
- *
- * This functions returns %0 on success or a negative error code on failure.
- */
-static int validate_data_node(struct ubifs_info *c, void *buf,
- struct ubifs_zbranch *zbr)
-{
- union ubifs_key key1;
- struct ubifs_ch *ch = buf;
- int err, len;
-
- if (ch->node_type != UBIFS_DATA_NODE) {
- ubifs_err(c, "bad node type (%d but expected %d)",
- ch->node_type, UBIFS_DATA_NODE);
- goto out_err;
- }
-
- err = ubifs_check_node(c, buf, zbr->len, zbr->lnum, zbr->offs, 0, 0);
- if (err) {
- ubifs_err(c, "expected node type %d", UBIFS_DATA_NODE);
- goto out;
- }
-
- err = ubifs_node_check_hash(c, buf, zbr->hash);
- if (err) {
- ubifs_bad_hash(c, buf, zbr->hash, zbr->lnum, zbr->offs);
- return err;
- }
-
- len = le32_to_cpu(ch->len);
- if (len != zbr->len) {
- ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
- goto out_err;
- }
-
- /* Make sure the key of the read node is correct */
- key_read(c, buf + UBIFS_KEY_OFFSET, &key1);
- if (!keys_eq(c, &zbr->key, &key1)) {
- ubifs_err(c, "bad key in node at LEB %d:%d",
- zbr->lnum, zbr->offs);
- dbg_tnck(&zbr->key, "looked for key ");
- dbg_tnck(&key1, "found node's key ");
- goto out_err;
- }
-
- return 0;
-
-out_err:
- err = -EINVAL;
-out:
- ubifs_err(c, "bad node at LEB %d:%d", zbr->lnum, zbr->offs);
- ubifs_dump_node(c, buf, zbr->len);
- dump_stack();
- return err;
-}
-
-/**
- * ubifs_tnc_bulk_read - read a number of data nodes in one go.
- * @c: UBIFS file-system description object
- * @bu: bulk-read parameters and results
- *
- * This functions reads and validates the data nodes that were identified by the
- * 'ubifs_tnc_get_bu_keys()' function. This functions returns %0 on success,
- * -EAGAIN to indicate a race with GC, or another negative error code on
- * failure.
- */
-int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
-{
- int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i;
- struct ubifs_wbuf *wbuf;
- void *buf;
-
- len = bu->zbranch[bu->cnt - 1].offs;
- len += bu->zbranch[bu->cnt - 1].len - offs;
- if (len > bu->buf_len) {
- ubifs_err(c, "buffer too small %d vs %d", bu->buf_len, len);
- return -EINVAL;
- }
-
- /* Do the read */
- wbuf = ubifs_get_wbuf(c, lnum);
- if (wbuf)
- err = read_wbuf(wbuf, bu->buf, len, lnum, offs);
- else
- err = ubifs_leb_read(c, lnum, bu->buf, offs, len, 0);
-
- /* Check for a race with GC */
- if (maybe_leb_gced(c, lnum, bu->gc_seq))
- return -EAGAIN;
-
- if (err && err != -EBADMSG) {
- ubifs_err(c, "failed to read from LEB %d:%d, error %d",
- lnum, offs, err);
- dump_stack();
- dbg_tnck(&bu->key, "key ");
- return err;
- }
-
- /* Validate the nodes read */
- buf = bu->buf;
- for (i = 0; i < bu->cnt; i++) {
- err = validate_data_node(c, buf, &bu->zbranch[i]);
- if (err)
- return err;
- buf = buf + ALIGN(bu->zbranch[i].len, 8);
- }
-
- return 0;
-}
-
-/**
* do_lookup_nm- look up a "hashed" node.
* @c: UBIFS file-system description object
* @key: node key to lookup
@@ -1892,110 +1568,6 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
return do_lookup_nm(c, key, node, nm);
}
-static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key,
- struct ubifs_dent_node *dent, uint32_t cookie,
- struct ubifs_znode **zn, int *n, int exact)
-{
- int err;
- struct ubifs_znode *znode = *zn;
- struct ubifs_zbranch *zbr;
- union ubifs_key *dkey;
-
- if (!exact) {
- err = tnc_next(c, &znode, n);
- if (err)
- return err;
- }
-
- for (;;) {
- zbr = &znode->zbranch[*n];
- dkey = &zbr->key;
-
- if (key_inum(c, dkey) != key_inum(c, key) ||
- key_type(c, dkey) != key_type(c, key)) {
- return -ENOENT;
- }
-
- err = tnc_read_hashed_node(c, zbr, dent);
- if (err)
- return err;
-
- if (key_hash(c, key) == key_hash(c, dkey) &&
- le32_to_cpu(dent->cookie) == cookie) {
- *zn = znode;
- return 0;
- }
-
- err = tnc_next(c, &znode, n);
- if (err)
- return err;
- }
-}
-
-static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
- struct ubifs_dent_node *dent, uint32_t cookie)
-{
- int n, err;
- struct ubifs_znode *znode;
- union ubifs_key start_key;
-
- ubifs_assert(c, is_hash_key(c, key));
-
- lowest_dent_key(c, &start_key, key_inum(c, key));
-
- mutex_lock(&c->tnc_mutex);
- err = ubifs_lookup_level0(c, &start_key, &znode, &n);
- if (unlikely(err < 0))
- goto out_unlock;
-
- err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
-
-out_unlock:
- mutex_unlock(&c->tnc_mutex);
- return err;
-}
-
-/**
- * ubifs_tnc_lookup_dh - look up a "double hashed" node.
- * @c: UBIFS file-system description object
- * @key: node key to lookup
- * @node: the node is returned here
- * @cookie: node cookie for collision resolution
- *
- * This function looks up and reads a node which contains name hash in the key.
- * Since the hash may have collisions, there may be many nodes with the same
- * key, so we have to sequentially look to all of them until the needed one
- * with the same cookie value is found.
- * This function returns zero in case of success, %-ENOENT if the node
- * was not found, and a negative error code in case of failure.
- */
-int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
- void *node, uint32_t cookie)
-{
- int err;
- const struct ubifs_dent_node *dent = node;
-
- if (!c->double_hash)
- return -EOPNOTSUPP;
-
- /*
- * We assume that in most of the cases there are no name collisions and
- * 'ubifs_tnc_lookup()' returns us the right direntry.
- */
- err = ubifs_tnc_lookup(c, key, node);
- if (err)
- return err;
-
- if (le32_to_cpu(dent->cookie) == cookie)
- return 0;
-
- /*
- * Unluckily, there are hash collisions and we have to iterate over
- * them look at each direntry with colliding name hash sequentially.
- */
- return do_lookup_dh(c, key, node, cookie);
-}
-
/**
* correct_parent_keys - correct parent znodes' keys.
* @c: UBIFS file-system description object
@@ -2540,6 +2112,10 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n)
if (znode->child_cnt > 0)
return 0;
+ /* Different with linux kernel, TNC could become empty. */
+ if (!znode->parent)
+ return 0;
+
/*
* This was the last zbranch, we have to delete this znode from the
* parent.
@@ -2697,74 +2273,6 @@ out_unlock:
}
/**
- * ubifs_tnc_remove_dh - remove an index entry for a "double hashed" node.
- * @c: UBIFS file-system description object
- * @key: key of node
- * @cookie: node cookie for collision resolution
- *
- * Returns %0 on success or negative error code on failure.
- */
-int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
- uint32_t cookie)
-{
- int n, err;
- struct ubifs_znode *znode;
- struct ubifs_dent_node *dent;
- struct ubifs_zbranch *zbr;
-
- if (!c->double_hash)
- return -EOPNOTSUPP;
-
- mutex_lock(&c->tnc_mutex);
- err = lookup_level0_dirty(c, key, &znode, &n);
- if (err <= 0)
- goto out_unlock;
-
- zbr = &znode->zbranch[n];
- dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
- if (!dent) {
- err = -ENOMEM;
- goto out_unlock;
- }
-
- err = tnc_read_hashed_node(c, zbr, dent);
- if (err)
- goto out_free;
-
- /* If the cookie does not match, we're facing a hash collision. */
- if (le32_to_cpu(dent->cookie) != cookie) {
- union ubifs_key start_key;
-
- lowest_dent_key(c, &start_key, key_inum(c, key));
-
- err = ubifs_lookup_level0(c, &start_key, &znode, &n);
- if (unlikely(err < 0))
- goto out_free;
-
- err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
- if (err)
- goto out_free;
- }
-
- if (znode->cnext || !ubifs_zn_dirty(znode)) {
- znode = dirty_cow_bottom_up(c, znode);
- if (IS_ERR(znode)) {
- err = PTR_ERR(znode);
- goto out_free;
- }
- }
- err = tnc_delete(c, znode, n);
-
-out_free:
- kfree(dent);
-out_unlock:
- if (!err)
- err = dbg_check_tnc(c, 0);
- mutex_unlock(&c->tnc_mutex);
- return err;
-}
-
-/**
* key_in_range - determine if a key falls within a range of keys.
* @c: UBIFS file-system description object
* @key: key to check
@@ -2905,9 +2413,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
dbg_tnc("xent '%s', ino %lu", xent->name,
(unsigned long)xattr_inum);
- ubifs_evict_xattr_inode(c, xattr_inum);
-
- fname_name(&nm) = xent->name;
+ fname_name(&nm) = (const char *)xent->name;
fname_len(&nm) = le16_to_cpu(xent->nlen);
err = ubifs_tnc_remove_nm(c, &key1, &nm);
if (err) {
@@ -3485,69 +2991,3 @@ out_unlock:
mutex_unlock(&c->tnc_mutex);
return err;
}
-
-/**
- * dbg_check_inode_size - check if inode size is correct.
- * @c: UBIFS file-system description object
- * @inode: inode to check
- * @size: inode size
- *
- * This function makes sure that the inode size (@size) is correct and it does
- * not have any pages beyond @size. Returns zero if the inode is OK, %-EINVAL
- * if it has a data page beyond @size, and other negative error code in case of
- * other errors.
- */
-int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
- loff_t size)
-{
- int err, n;
- union ubifs_key from_key, to_key, *key;
- struct ubifs_znode *znode;
- unsigned int block;
-
- if (!S_ISREG(inode->i_mode))
- return 0;
- if (!dbg_is_chk_gen(c))
- return 0;
-
- block = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
- data_key_init(c, &from_key, inode->i_ino, block);
- highest_data_key(c, &to_key, inode->i_ino);
-
- mutex_lock(&c->tnc_mutex);
- err = ubifs_lookup_level0(c, &from_key, &znode, &n);
- if (err < 0)
- goto out_unlock;
-
- if (err) {
- key = &from_key;
- goto out_dump;
- }
-
- err = tnc_next(c, &znode, &n);
- if (err == -ENOENT) {
- err = 0;
- goto out_unlock;
- }
- if (err < 0)
- goto out_unlock;
-
- ubifs_assert(c, err == 0);
- key = &znode->zbranch[n].key;
- if (!key_in_range(c, key, &from_key, &to_key))
- goto out_unlock;
-
-out_dump:
- block = key_block(c, key);
- ubifs_err(c, "inode %lu has size %lld, but there are data at offset %lld",
- (unsigned long)inode->i_ino, size,
- ((loff_t)block) << UBIFS_BLOCK_SHIFT);
- mutex_unlock(&c->tnc_mutex);
- ubifs_dump_inode(c, inode);
- dump_stack();
- return -EINVAL;
-
-out_unlock:
- mutex_unlock(&c->tnc_mutex);
- return err;
-}
diff --git a/ubifs-utils/libubifs/tnc_commit.c b/ubifs-utils/libubifs/tnc_commit.c
index a55e0482..d797006e 100644
--- a/ubifs-utils/libubifs/tnc_commit.c
+++ b/ubifs-utils/libubifs/tnc_commit.c
@@ -10,8 +10,14 @@
/* This file implements TNC functions for committing */
-#include <linux/random.h>
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/**
* make_idx_node - make an index node for fill-the-gaps method of TNC commit.
@@ -546,8 +552,8 @@ static int layout_in_empty_space(struct ubifs_info *c)
break;
}
- c->dbg->new_ihead_lnum = lnum;
- c->dbg->new_ihead_offs = buf_offs;
+ c->new_ihead_lnum = lnum;
+ c->new_ihead_offs = buf_offs;
return 0;
}
@@ -700,7 +706,7 @@ static int alloc_idx_lebs(struct ubifs_info *c, int cnt)
c->ilebs[c->ileb_cnt++] = lnum;
dbg_cmt("LEB %d", lnum);
}
- if (dbg_is_chk_index(c) && !get_random_u32_below(8))
+ if (dbg_is_chk_index(c))
return -ENOSPC;
return 0;
}
@@ -1011,8 +1017,8 @@ static int write_index(struct ubifs_info *c)
break;
}
- if (lnum != c->dbg->new_ihead_lnum ||
- buf_offs != c->dbg->new_ihead_offs) {
+ if (lnum != c->new_ihead_lnum ||
+ buf_offs != c->new_ihead_offs) {
ubifs_err(c, "inconsistent ihead");
return -EINVAL;
}
diff --git a/ubifs-utils/libubifs/tnc_misc.c b/ubifs-utils/libubifs/tnc_misc.c
index d3f8a6aa..8c38f153 100644
--- a/ubifs-utils/libubifs/tnc_misc.c
+++ b/ubifs-utils/libubifs/tnc_misc.c
@@ -15,97 +15,14 @@
* putting it all in one file would make that file too big and unreadable.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
-
-/**
- * ubifs_tnc_levelorder_next - next TNC tree element in levelorder traversal.
- * @c: UBIFS file-system description object
- * @zr: root of the subtree to traverse
- * @znode: previous znode
- *
- * This function implements levelorder TNC traversal. The LNC is ignored.
- * Returns the next element or %NULL if @znode is already the last one.
- */
-struct ubifs_znode *ubifs_tnc_levelorder_next(const struct ubifs_info *c,
- struct ubifs_znode *zr,
- struct ubifs_znode *znode)
-{
- int level, iip, level_search = 0;
- struct ubifs_znode *zn;
-
- ubifs_assert(c, zr);
-
- if (unlikely(!znode))
- return zr;
-
- if (unlikely(znode == zr)) {
- if (znode->level == 0)
- return NULL;
- return ubifs_tnc_find_child(zr, 0);
- }
-
- level = znode->level;
-
- iip = znode->iip;
- while (1) {
- ubifs_assert(c, znode->level <= zr->level);
-
- /*
- * First walk up until there is a znode with next branch to
- * look at.
- */
- while (znode->parent != zr && iip >= znode->parent->child_cnt) {
- znode = znode->parent;
- iip = znode->iip;
- }
-
- if (unlikely(znode->parent == zr &&
- iip >= znode->parent->child_cnt)) {
- /* This level is done, switch to the lower one */
- level -= 1;
- if (level_search || level < 0)
- /*
- * We were already looking for znode at lower
- * level ('level_search'). As we are here
- * again, it just does not exist. Or all levels
- * were finished ('level < 0').
- */
- return NULL;
-
- level_search = 1;
- iip = -1;
- znode = ubifs_tnc_find_child(zr, 0);
- ubifs_assert(c, znode);
- }
-
- /* Switch to the next index */
- zn = ubifs_tnc_find_child(znode->parent, iip + 1);
- if (!zn) {
- /* No more children to look at, we have walk up */
- iip = znode->parent->child_cnt;
- continue;
- }
-
- /* Walk back down to the level we came from ('level') */
- while (zn->level != level) {
- znode = zn;
- zn = ubifs_tnc_find_child(zn, 0);
- if (!zn) {
- /*
- * This path is not too deep so it does not
- * reach 'level'. Try next path.
- */
- iip = znode->iip;
- break;
- }
- }
-
- if (zn) {
- ubifs_assert(c, zn->level >= 0);
- return zn;
- }
- }
-}
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/**
* ubifs_search_zbranch - search znode branch.
@@ -130,6 +47,12 @@ int ubifs_search_zbranch(const struct ubifs_info *c,
int cmp;
const struct ubifs_zbranch *zbr = &znode->zbranch[0];
+ if (!end) {
+ /* Different with linux kernel, TNC could become empty. */
+ *n = -1;
+ return 0;
+ }
+
ubifs_assert(c, end > beg);
while (end > beg) {
@@ -360,9 +283,10 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
}
if (znode->level)
- continue;
+ type = UBIFS_IDX_NODE;
+ else
+ type = key_type(c, &zbr->key);
- type = key_type(c, &zbr->key);
if (c->ranges[type].max_len == 0) {
if (zbr->len != c->ranges[type].len) {
ubifs_err(c, "bad target node (type %d) length (%d)",
--
2.13.6
Split common source files into common dir from mkfs.ubifs, this is a
preparation for importing libubifs(from linux kernel) to replace
current UBIFS libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 57 ++++++++++++----------
ubifs-utils/{mkfs.ubifs => common}/README | 2 +-
ubifs-utils/{mkfs.ubifs => common}/compr.c | 0
ubifs-utils/{mkfs.ubifs => common}/compr.h | 0
ubifs-utils/{mkfs.ubifs => common}/crc16.c | 0
ubifs-utils/{mkfs.ubifs => common}/crc16.h | 0
ubifs-utils/{mkfs.ubifs => common}/crypto.c | 0
ubifs-utils/{mkfs.ubifs => common}/crypto.h | 0
ubifs-utils/{mkfs.ubifs => common}/defs.h | 0
ubifs-utils/{mkfs.ubifs => common}/devtable.c | 0
ubifs-utils/{mkfs.ubifs => common}/fscrypt.c | 0
ubifs-utils/{mkfs.ubifs => common}/fscrypt.h | 0
.../{mkfs.ubifs => common}/hashtable/hashtable.c | 0
.../{mkfs.ubifs => common}/hashtable/hashtable.h | 0
.../hashtable/hashtable_itr.c | 0
.../hashtable/hashtable_itr.h | 0
.../hashtable/hashtable_private.h | 0
ubifs-utils/{mkfs.ubifs => common}/key.h | 0
ubifs-utils/{mkfs.ubifs => common}/lpt.c | 0
ubifs-utils/{mkfs.ubifs => common}/lpt.h | 0
ubifs-utils/{mkfs.ubifs => common}/sign.c | 0
ubifs-utils/{mkfs.ubifs => common}/sign.h | 0
ubifs-utils/{mkfs.ubifs => common}/ubifs.h | 0
23 files changed, 31 insertions(+), 28 deletions(-)
rename ubifs-utils/{mkfs.ubifs => common}/README (91%)
rename ubifs-utils/{mkfs.ubifs => common}/compr.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/compr.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crc16.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crc16.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crypto.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/crypto.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/defs.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/devtable.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/fscrypt.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/fscrypt.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_itr.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_itr.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/hashtable/hashtable_private.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/key.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/lpt.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/lpt.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/sign.c (100%)
rename ubifs-utils/{mkfs.ubifs => common}/sign.h (100%)
rename ubifs-utils/{mkfs.ubifs => common}/ubifs.h (100%)
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 6814d47b..4a617c19 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -1,37 +1,40 @@
-mkfs_ubifs_SOURCES = \
- ubifs-utils/mkfs.ubifs/mkfs.ubifs.c \
- ubifs-utils/mkfs.ubifs/defs.h \
- ubifs-utils/mkfs.ubifs/lpt.h \
- ubifs-utils/mkfs.ubifs/mkfs.ubifs.h \
- ubifs-utils/mkfs.ubifs/crc16.h \
- ubifs-utils/mkfs.ubifs/key.h \
- ubifs-utils/mkfs.ubifs/compr.h \
- ubifs-utils/mkfs.ubifs/ubifs.h \
- ubifs-utils/mkfs.ubifs/sign.h \
- ubifs-utils/mkfs.ubifs/crc16.c \
- ubifs-utils/mkfs.ubifs/lpt.c \
- ubifs-utils/mkfs.ubifs/compr.c \
- ubifs-utils/mkfs.ubifs/hashtable/hashtable.h \
- ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h \
- ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h \
- ubifs-utils/mkfs.ubifs/hashtable/hashtable.c \
- ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c \
- ubifs-utils/mkfs.ubifs/devtable.c \
+common_SOURCES = \
+ ubifs-utils/common/defs.h \
+ ubifs-utils/common/crc16.h \
+ ubifs-utils/common/crc16.c \
+ ubifs-utils/common/compr.h \
+ ubifs-utils/common/compr.c \
+ ubifs-utils/common/hashtable/hashtable.h \
+ ubifs-utils/common/hashtable/hashtable_itr.h \
+ ubifs-utils/common/hashtable/hashtable_private.h \
+ ubifs-utils/common/hashtable/hashtable.c \
+ ubifs-utils/common/hashtable/hashtable_itr.c \
+ ubifs-utils/common/devtable.c \
+ ubifs-utils/common/ubifs.h \
+ ubifs-utils/common/key.h \
+ ubifs-utils/common/lpt.h \
+ ubifs-utils/common/lpt.c \
+ ubifs-utils/common/sign.h \
include/mtd/ubifs-media.h
if WITH_CRYPTO
-mkfs_ubifs_SOURCES += ubifs-utils/mkfs.ubifs/crypto.c \
- ubifs-utils/mkfs.ubifs/crypto.h \
- ubifs-utils/mkfs.ubifs/fscrypt.c \
- ubifs-utils/mkfs.ubifs/fscrypt.h \
- ubifs-utils/mkfs.ubifs/sign.c
+common_SOURCES += ubifs-utils/common/crypto.c \
+ ubifs-utils/common/crypto.h \
+ ubifs-utils/common/fscrypt.c \
+ ubifs-utils/common/fscrypt.h \
+ ubifs-utils/common/sign.c
endif
+mkfs_ubifs_SOURCES = \
+ $(common_SOURCES) \
+ ubifs-utils/mkfs.ubifs/mkfs.ubifs.h \
+ ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+
mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm
-mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS)\
- -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/mkfs.ubifs/
+mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
+ -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/mkfs.ubifs/ -I$(top_srcdir)/ubifs-utils/common
-EXTRA_DIST += ubifs-utils/mkfs.ubifs/README
+EXTRA_DIST += ubifs-utils/common/README
dist_sbin_SCRIPTS = ubifs-utils/mount.ubifs
diff --git a/ubifs-utils/mkfs.ubifs/README b/ubifs-utils/common/README
similarity index 91%
rename from ubifs-utils/mkfs.ubifs/README
rename to ubifs-utils/common/README
index 7e199390..8c10fd4b 100644
--- a/ubifs-utils/mkfs.ubifs/README
+++ b/ubifs-utils/common/README
@@ -1,4 +1,4 @@
-UBIFS File System - Make File System program
+Common Library
* crc16.h and crc16.c were copied from the linux kernel.
* crc32.h and crc32.c were copied from mtd-utils and amended.
diff --git a/ubifs-utils/mkfs.ubifs/compr.c b/ubifs-utils/common/compr.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/compr.c
rename to ubifs-utils/common/compr.c
diff --git a/ubifs-utils/mkfs.ubifs/compr.h b/ubifs-utils/common/compr.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/compr.h
rename to ubifs-utils/common/compr.h
diff --git a/ubifs-utils/mkfs.ubifs/crc16.c b/ubifs-utils/common/crc16.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/crc16.c
rename to ubifs-utils/common/crc16.c
diff --git a/ubifs-utils/mkfs.ubifs/crc16.h b/ubifs-utils/common/crc16.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/crc16.h
rename to ubifs-utils/common/crc16.h
diff --git a/ubifs-utils/mkfs.ubifs/crypto.c b/ubifs-utils/common/crypto.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/crypto.c
rename to ubifs-utils/common/crypto.c
diff --git a/ubifs-utils/mkfs.ubifs/crypto.h b/ubifs-utils/common/crypto.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/crypto.h
rename to ubifs-utils/common/crypto.h
diff --git a/ubifs-utils/mkfs.ubifs/defs.h b/ubifs-utils/common/defs.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/defs.h
rename to ubifs-utils/common/defs.h
diff --git a/ubifs-utils/mkfs.ubifs/devtable.c b/ubifs-utils/common/devtable.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/devtable.c
rename to ubifs-utils/common/devtable.c
diff --git a/ubifs-utils/mkfs.ubifs/fscrypt.c b/ubifs-utils/common/fscrypt.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/fscrypt.c
rename to ubifs-utils/common/fscrypt.c
diff --git a/ubifs-utils/mkfs.ubifs/fscrypt.h b/ubifs-utils/common/fscrypt.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/fscrypt.h
rename to ubifs-utils/common/fscrypt.h
diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c b/ubifs-utils/common/hashtable/hashtable.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/hashtable/hashtable.c
rename to ubifs-utils/common/hashtable/hashtable.c
diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h b/ubifs-utils/common/hashtable/hashtable.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/hashtable/hashtable.h
rename to ubifs-utils/common/hashtable/hashtable.h
diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c b/ubifs-utils/common/hashtable/hashtable_itr.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c
rename to ubifs-utils/common/hashtable/hashtable_itr.c
diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h b/ubifs-utils/common/hashtable/hashtable_itr.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h
rename to ubifs-utils/common/hashtable/hashtable_itr.h
diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h b/ubifs-utils/common/hashtable/hashtable_private.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h
rename to ubifs-utils/common/hashtable/hashtable_private.h
diff --git a/ubifs-utils/mkfs.ubifs/key.h b/ubifs-utils/common/key.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/key.h
rename to ubifs-utils/common/key.h
diff --git a/ubifs-utils/mkfs.ubifs/lpt.c b/ubifs-utils/common/lpt.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/lpt.c
rename to ubifs-utils/common/lpt.c
diff --git a/ubifs-utils/mkfs.ubifs/lpt.h b/ubifs-utils/common/lpt.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/lpt.h
rename to ubifs-utils/common/lpt.h
diff --git a/ubifs-utils/mkfs.ubifs/sign.c b/ubifs-utils/common/sign.c
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/sign.c
rename to ubifs-utils/common/sign.c
diff --git a/ubifs-utils/mkfs.ubifs/sign.h b/ubifs-utils/common/sign.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/sign.h
rename to ubifs-utils/common/sign.h
diff --git a/ubifs-utils/mkfs.ubifs/ubifs.h b/ubifs-utils/common/ubifs.h
similarity index 100%
rename from ubifs-utils/mkfs.ubifs/ubifs.h
rename to ubifs-utils/common/ubifs.h
--
2.13.6
Move UBI opening/closing/volume_check_empty functions into common/super.c.
These functions will be used in fsck.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/super.c | 123 ++++++++++++++++++++++++++++++++++++
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 114 ---------------------------------
3 files changed, 124 insertions(+), 114 deletions(-)
create mode 100644 ubifs-utils/common/super.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 8af65be7..90cc7005 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -15,6 +15,7 @@ common_SOURCES = \
ubifs-utils/common/key.h \
ubifs-utils/common/lpt.h \
ubifs-utils/common/lpt.c \
+ ubifs-utils/common/super.c \
ubifs-utils/common/sign.h \
include/mtd/ubifs-media.h
diff --git a/ubifs-utils/common/super.c b/ubifs-utils/common/super.c
new file mode 100644
index 00000000..eee0197d
--- /dev/null
+++ b/ubifs-utils/common/super.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "defs.h"
+
+/**
+ * open_ubi - open the libubi.
+ * @c: the UBIFS file-system description object
+ * @node: name of the UBI volume character device to fetch information about
+ *
+ * This function opens libubi, and initialize device & volume information
+ * according to @node. Returns %0 in case of success and %-1 in case of failure.
+ */
+int open_ubi(struct ubifs_info *c, const char *node)
+{
+ struct stat st;
+
+ if (stat(node, &st) || !S_ISCHR(st.st_mode))
+ return -1;
+
+ c->libubi = libubi_open();
+ if (!c->libubi)
+ return -1;
+ if (ubi_get_vol_info(c->libubi, node, &c->vi))
+ goto out_err;
+ if (ubi_get_dev_info1(c->libubi, c->vi.dev_num, &c->di))
+ goto out_err;
+
+ return 0;
+
+out_err:
+ close_ubi(c);
+ return -1;
+}
+
+void close_ubi(struct ubifs_info *c)
+{
+ if (c->libubi) {
+ libubi_close(c->libubi);
+ c->libubi = NULL;
+ }
+}
+
+/**
+ * open_target - open the output target.
+ * @c: the UBIFS file-system description object
+ *
+ * Open the output target. The target can be an UBI volume
+ * or a file.
+ *
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+int open_target(struct ubifs_info *c)
+{
+ if (c->libubi) {
+ c->dev_fd = open(c->dev_name, O_RDWR | O_EXCL);
+
+ if (c->dev_fd == -1)
+ return sys_errmsg("cannot open the UBI volume '%s'",
+ c->dev_name);
+ if (ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
+ close(c->dev_fd);
+ return sys_errmsg("ubi_set_property(set direct_write) failed");
+ }
+ } else {
+ c->dev_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ if (c->dev_fd == -1)
+ return sys_errmsg("cannot create output file '%s'",
+ c->dev_name);
+ }
+ return 0;
+}
+
+
+/**
+ * close_target - close the output target.
+ * @c: the UBIFS file-system description object
+ *
+ * Close the output target. If the target was an UBI
+ * volume, also close libubi.
+ *
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+int close_target(struct ubifs_info *c)
+{
+ if (c->dev_fd >= 0) {
+ if (c->libubi && ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
+ return sys_errmsg("ubi_set_property(clear direct_write) failed");
+ if (close(c->dev_fd) == -1)
+ return sys_errmsg("cannot close the target '%s'", c->dev_name);
+ }
+ return 0;
+}
+
+/**
+ * check_volume_empty - check if the UBI volume is empty.
+ * @c: the UBIFS file-system description object
+ *
+ * This function checks if the UBI volume is empty by looking if its LEBs are
+ * mapped or not.
+ *
+ * Returns %0 in case of success, %1 is the volume is not empty,
+ * and a negative error code in case of failure.
+ */
+int check_volume_empty(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) {
+ err = ubi_is_mapped(c->dev_fd, lnum);
+ if (err < 0)
+ return err;
+ if (err == 1)
+ return 1;
+ }
+ return 0;
+}
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 8bff44b2..2181595e 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -518,44 +518,6 @@ static long long get_bytes(const char *str)
return bytes;
}
-void close_ubi(struct ubifs_info *c)
-{
- if (c->libubi) {
- libubi_close(c->libubi);
- c->libubi = NULL;
- }
-}
-
-/**
- * open_ubi - open the libubi.
- * @c: the UBIFS file-system description object
- * @node: name of the UBI volume character device to fetch information about
- *
- * This function opens libubi, and initialize device & volume information
- * according to @node. Returns %0 in case of success and %-1 in case of failure.
- */
-int open_ubi(struct ubifs_info *c, const char *node)
-{
- struct stat st;
-
- if (stat(node, &st) || !S_ISCHR(st.st_mode))
- return -1;
-
- c->libubi = libubi_open();
- if (!c->libubi)
- return -1;
- if (ubi_get_vol_info(c->libubi, node, &c->vi))
- goto out_err;
- if (ubi_get_dev_info1(c->libubi, c->vi.dev_num, &c->di))
- goto out_err;
-
- return 0;
-
-out_err:
- close_ubi(c);
- return -1;
-}
-
static void select_default_compr(void)
{
if (c->encrypted) {
@@ -2821,82 +2783,6 @@ static int write_orphan_area(void)
}
/**
- * check_volume_empty - check if the UBI volume is empty.
- * @c: the UBIFS file-system description object
- *
- * This function checks if the UBI volume is empty by looking if its LEBs are
- * mapped or not.
- *
- * Returns %0 in case of success, %1 is the volume is not empty,
- * and a negative error code in case of failure.
- */
-int check_volume_empty(struct ubifs_info *c)
-{
- int lnum, err;
-
- for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) {
- err = ubi_is_mapped(c->dev_fd, lnum);
- if (err < 0)
- return err;
- if (err == 1)
- return 1;
- }
- return 0;
-}
-
-/**
- * open_target - open the output target.
- * @c: the UBIFS file-system description object
- *
- * Open the output target. The target can be an UBI volume
- * or a file.
- *
- * Returns %0 in case of success and %-1 in case of failure.
- */
-int open_target(struct ubifs_info *c)
-{
- if (c->libubi) {
- c->dev_fd = open(c->dev_name, O_RDWR | O_EXCL);
-
- if (c->dev_fd == -1)
- return sys_errmsg("cannot open the UBI volume '%s'",
- c->dev_name);
- if (ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
- close(c->dev_fd);
- return sys_errmsg("ubi_set_property(set direct_write) failed");
- }
- } else {
- c->dev_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
- if (c->dev_fd == -1)
- return sys_errmsg("cannot create output file '%s'",
- c->dev_name);
- }
- return 0;
-}
-
-
-/**
- * close_target - close the output target.
- * @c: the UBIFS file-system description object
- *
- * Close the output target. If the target was an UBI
- * volume, also close libubi.
- *
- * Returns %0 in case of success and %-1 in case of failure.
- */
-int close_target(struct ubifs_info *c)
-{
- if (c->dev_fd >= 0) {
- if (c->libubi && ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
- return sys_errmsg("ubi_set_property(clear direct_write) failed");
- if (close(c->dev_fd) == -1)
- return sys_errmsg("cannot close the target '%s'", c->dev_name);
- }
- return 0;
-}
-
-/**
* init - initialize things.
*/
static int init(void)
--
2.13.6
Add mutexlock implementations, because there are some mutexlocks
(eg. c->tnc_mutex, c->log_mutex) used in UBIFS linux kernel libs.
The mutexlock is implementated based on pthread mutex.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/mutex.h | 18 ++++++++++++++++++
2 files changed, 19 insertions(+)
create mode 100644 ubifs-utils/common/mutex.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 7849114e..9f33f0db 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -6,6 +6,7 @@ common_SOURCES = \
ubifs-utils/common/bitops.h \
ubifs-utils/common/bitops.c \
ubifs-utils/common/spinlock.h \
+ ubifs-utils/common/mutex.h \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
diff --git a/ubifs-utils/common/mutex.h b/ubifs-utils/common/mutex.h
new file mode 100644
index 00000000..4bf018b0
--- /dev/null
+++ b/ubifs-utils/common/mutex.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_MUTEX_H_
+#define __LINUX_MUTEX_H_
+
+#include <pthread.h>
+
+struct mutex {
+ pthread_mutex_t lock;
+};
+
+#define mutex_init(x) pthread_mutex_init(&(x)->lock, NULL)
+
+#define mutex_lock(x) pthread_mutex_lock(&(x)->lock)
+#define mutex_lock_nested(x, c) pthread_mutex_lock(&(x)->lock)
+#define mutex_unlock(x) pthread_mutex_unlock(&(x)->lock)
+#define mutex_is_locked(x) (pthread_mutex_trylock(&(x)->lock) == EBUSY)
+
+#endif
--
2.13.6
Add linux kernel memory allocation implementations, because there are many
memory allocations(eg. kmalloc, kzalloc) used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 2 ++
ubifs-utils/common/kmem.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++
ubifs-utils/common/kmem.h | 56 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 122 insertions(+)
create mode 100644 ubifs-utils/common/kmem.c
create mode 100644 ubifs-utils/common/kmem.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 69192ffd..13171b74 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -2,6 +2,8 @@ common_SOURCES = \
ubifs-utils/common/compiler_attributes.h \
ubifs-utils/common/linux_types.h \
ubifs-utils/common/linux_err.h \
+ ubifs-utils/common/kmem.h \
+ ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
ubifs-utils/common/crc16.h \
ubifs-utils/common/crc16.c \
diff --git a/ubifs-utils/common/kmem.c b/ubifs-utils/common/kmem.c
new file mode 100644
index 00000000..e926a137
--- /dev/null
+++ b/ubifs-utils/common/kmem.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Simple memory interface
+ */
+
+#include "compiler_attributes.h"
+#include "linux_types.h"
+#include "kmem.h"
+#include "defs.h"
+
+static void *kmem_alloc(size_t size)
+{
+ void *ptr = malloc(size);
+
+ if (ptr == NULL)
+ sys_errmsg("malloc failed (%d bytes)", (int)size);
+ return ptr;
+}
+
+static void *kmem_zalloc(size_t size)
+{
+ void *ptr = kmem_alloc(size);
+
+ if (!ptr)
+ return ptr;
+
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void *kmalloc(size_t size, gfp_t flags)
+{
+ if (flags & __GFP_ZERO)
+ return kmem_zalloc(size);
+ return kmem_alloc(size);
+}
+
+void *krealloc(void *ptr, size_t new_size, __unused gfp_t flags)
+{
+ ptr = realloc(ptr, new_size);
+ if (ptr == NULL)
+ sys_errmsg("realloc failed (%d bytes)", (int)new_size);
+ return ptr;
+}
+
+void *kmalloc_array(size_t n, size_t size, gfp_t flags)
+{
+ size_t bytes;
+
+ if (unlikely(check_mul_overflow(n, size, &bytes)))
+ return NULL;
+ return kmalloc(bytes, flags);
+}
+
+void *kmemdup(const void *src, size_t len, gfp_t gfp)
+{
+ void *p;
+
+ p = kmalloc(len, gfp);
+ if (p)
+ memcpy(p, src, len);
+
+ return p;
+}
diff --git a/ubifs-utils/common/kmem.h b/ubifs-utils/common/kmem.h
new file mode 100644
index 00000000..9fe2a364
--- /dev/null
+++ b/ubifs-utils/common/kmem.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2008 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#ifndef __KMEM_H__
+#define __KMEM_H__
+
+#include <stdlib.h>
+
+typedef unsigned int gfp_t;
+
+#define GFP_KERNEL 0
+#define GFP_NOFS 0
+#define __GFP_NOWARN 0
+#define __GFP_ZERO 1
+
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+extern void *kmalloc(size_t, gfp_t);
+extern void *krealloc(void *, size_t, __attribute__((unused)) gfp_t);
+extern void *kmalloc_array(size_t, size_t, gfp_t);
+extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
+
+static inline void kfree(const void *ptr)
+{
+ free((void *)ptr);
+}
+
+static inline void kvfree(const void *ptr)
+{
+ kfree(ptr);
+}
+
+static inline void *kvmalloc(size_t size, gfp_t flags)
+{
+ return kmalloc(size, flags);
+}
+
+static inline void *kzalloc(size_t size, gfp_t flags)
+{
+ return kmalloc(size, flags | __GFP_ZERO);
+}
+
+static inline void *__vmalloc(unsigned long size, gfp_t gfp_mask)
+{
+ return kmalloc(size, gfp_mask);
+}
+
+static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
+{
+ return kmalloc_array(n, size, flags | __GFP_ZERO);
+}
+
+#endif
--
2.13.6
Adapt ubifs header file in libubifs, compared with linux kernel
implementations:
1. New header file contains all definitions in common/ubifs.h
2. Remove some structures(eg. ubifs_mount_opts, bu_info) and
functions(eg. ubifs_sync_wbufs_by_inode, ubifs_jnl_XXX) which
won't be used in fsck/mkfs.
3. Modify some authentication related functions, keep functions
that mkfs may need, other functions are defined as empty.
4. Move ubifs message printing functions(ubifs_err/warn/msg) which
are originally defined in misc.c into ubifs.h.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/ubifs.h | 832 +++++++++++--------------------------------
1 file changed, 201 insertions(+), 631 deletions(-)
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 4226b21e..f32818dc 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -11,38 +11,18 @@
#ifndef __UBIFS_H__
#define __UBIFS_H__
-#include <asm/div64.h>
-#include <linux/statfs.h>
-#include <linux/fs.h>
-#include <linux/err.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/rwsem.h>
-#include <linux/mtd/ubi.h>
-#include <linux/pagemap.h>
-#include <linux/backing-dev.h>
-#include <linux/security.h>
-#include <linux/xattr.h>
-#include <linux/random.h>
-#include <linux/sysfs.h>
-#include <linux/completion.h>
-#include <crypto/hash_info.h>
-#include <crypto/hash.h>
-#include <crypto/utils.h>
-
-#include <linux/fscrypt.h>
-
+#include <string.h>
+
+#include "linux_types.h"
+#include "list.h"
+#include "rbtree.h"
+#include "spinlock.h"
+#include "mutex.h"
+#include "rwsem.h"
+#include "atomic.h"
+#include "libubi.h"
#include "ubifs-media.h"
-/* Version of this UBIFS implementation */
-#define UBIFS_VERSION 1
-
-/* UBIFS file system VFS magic number */
-#define UBIFS_SUPER_MAGIC 0x24051905
-
/* Number of UBIFS blocks per VFS page */
#define UBIFS_BLOCKS_PER_PAGE (PAGE_SIZE / UBIFS_BLOCK_SIZE)
#define UBIFS_BLOCKS_PER_PAGE_SHIFT (PAGE_SHIFT - UBIFS_BLOCK_SHIFT)
@@ -59,6 +39,9 @@
*/
#define MIN_INDEX_LEBS 2
+/* Maximum logical eraseblock size in bytes */
+#define UBIFS_MAX_LEB_SZ (2*1024*1024)
+
/* Minimum amount of data UBIFS writes to the flash */
#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8)
@@ -74,12 +57,6 @@
/* Maximum number of entries in each LPT (LEB category) heap */
#define LPT_HEAP_SZ 256
-/*
- * Background thread name pattern. The numbers are UBI device and volume
- * numbers.
- */
-#define BGT_NAME_PATTERN "ubifs_bgt%d_%d"
-
/* Maximum possible inode number (only 32-bit inodes are supported now) */
#define MAX_INUM 0xFFFFFFFF
@@ -115,64 +92,11 @@
/* How much an extended attribute adds to the host inode */
#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8)
-/*
- * Znodes which were not touched for 'OLD_ZNODE_AGE' seconds are considered
- * "old", and znode which were touched last 'YOUNG_ZNODE_AGE' seconds ago are
- * considered "young". This is used by shrinker when selecting znode to trim
- * off.
- */
-#define OLD_ZNODE_AGE 20
-#define YOUNG_ZNODE_AGE 5
-
-/*
- * Some compressors, like LZO, may end up with more data then the input buffer.
- * So UBIFS always allocates larger output buffer, to be sure the compressor
- * will not corrupt memory in case of worst case compression.
- */
-#define WORST_COMPR_FACTOR 2
-
-#ifdef CONFIG_FS_ENCRYPTION
-#define UBIFS_CIPHER_BLOCK_SIZE FSCRYPT_CONTENTS_ALIGNMENT
-#else
-#define UBIFS_CIPHER_BLOCK_SIZE 0
-#endif
-
-/*
- * How much memory is needed for a buffer where we compress a data node.
- */
-#define COMPRESSED_DATA_NODE_BUF_SZ \
- (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR)
-
/* Maximum expected tree height for use by bottom_up_buf */
#define BOTTOM_UP_HEIGHT 64
-/* Maximum number of data nodes to bulk-read */
-#define UBIFS_MAX_BULK_READ 32
-
-#ifdef CONFIG_UBIFS_FS_AUTHENTICATION
#define UBIFS_HASH_ARR_SZ UBIFS_MAX_HASH_LEN
#define UBIFS_HMAC_ARR_SZ UBIFS_MAX_HMAC_LEN
-#else
-#define UBIFS_HASH_ARR_SZ 0
-#define UBIFS_HMAC_ARR_SZ 0
-#endif
-
-/*
- * The UBIFS sysfs directory name pattern and maximum name length (3 for "ubi"
- * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
- */
-#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
-#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
-
-/*
- * Lockdep classes for UBIFS inode @ui_mutex.
- */
-enum {
- WB_MUTEX_1 = 0,
- WB_MUTEX_2 = 1,
- WB_MUTEX_3 = 2,
- WB_MUTEX_4 = 3,
-};
/*
* Znode flags (actually, bit numbers which store the flags).
@@ -265,18 +189,6 @@ enum {
LEB_RETAINED,
};
-/*
- * Action taken upon a failed ubifs_assert().
- * @ASSACT_REPORT: just report the failed assertion
- * @ASSACT_RO: switch to read-only mode
- * @ASSACT_PANIC: call BUG() and possible panic the kernel
- */
-enum {
- ASSACT_REPORT = 0,
- ASSACT_RO,
- ASSACT_PANIC,
-};
-
/**
* struct ubifs_old_idx - index node obsoleted since last commit start.
* @rb: rb-tree node
@@ -351,81 +263,25 @@ struct ubifs_gced_idx_leb {
/**
* struct ubifs_inode - UBIFS in-memory inode description.
- * @vfs_inode: VFS inode description object
* @creat_sqnum: sequence number at time of creation
- * @del_cmtno: commit number corresponding to the time the inode was deleted,
- * protected by @c->commit_sem;
* @xattr_size: summarized size of all extended attributes in bytes
* @xattr_cnt: count of extended attributes this inode has
* @xattr_names: sum of lengths of all extended attribute names belonging to
* this inode
- * @dirty: non-zero if the inode is dirty
- * @xattr: non-zero if this is an extended attribute inode
- * @bulk_read: non-zero if bulk-read should be used
- * @ui_mutex: serializes inode write-back with the rest of VFS operations,
- * serializes "clean <-> dirty" state changes, serializes bulk-read,
- * protects @dirty, @bulk_read, @ui_size, and @xattr_size
- * @xattr_sem: serilizes write operations (remove|set|create) on xattr
- * @ui_lock: protects @synced_i_size
- * @synced_i_size: synchronized size of inode, i.e. the value of inode size
- * currently stored on the flash; used only for regular file
- * inodes
* @ui_size: inode size used by UBIFS when writing to flash
* @flags: inode flags (@UBIFS_COMPR_FL, etc)
* @compr_type: default compression type used for this inode
- * @last_page_read: page number of last page read (for bulk read)
- * @read_in_a_row: number of consecutive pages read in a row (for bulk read)
* @data_len: length of the data attached to the inode
* @data: inode's data
- *
- * @ui_mutex exists for two main reasons. At first it prevents inodes from
- * being written back while UBIFS changing them, being in the middle of an VFS
- * operation. This way UBIFS makes sure the inode fields are consistent. For
- * example, in 'ubifs_rename()' we change 4 inodes simultaneously, and
- * write-back must not write any of them before we have finished.
- *
- * The second reason is budgeting - UBIFS has to budget all operations. If an
- * operation is going to mark an inode dirty, it has to allocate budget for
- * this. It cannot just mark it dirty because there is no guarantee there will
- * be enough flash space to write the inode back later. This means UBIFS has
- * to have full control over inode "clean <-> dirty" transitions (and pages
- * actually). But unfortunately, VFS marks inodes dirty in many places, and it
- * does not ask the file-system if it is allowed to do so (there is a notifier,
- * but it is not enough), i.e., there is no mechanism to synchronize with this.
- * So UBIFS has its own inode dirty flag and its own mutex to serialize
- * "clean <-> dirty" transitions.
- *
- * The @synced_i_size field is used to make sure we never write pages which are
- * beyond last synchronized inode size. See 'ubifs_writepage()' for more
- * information.
- *
- * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
- * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
- * make sure @inode->i_size is always changed under @ui_mutex, because it
- * cannot call 'truncate_setsize()' with @ui_mutex locked, because it would
- * deadlock with 'ubifs_writepage()' (see file.c). All the other inode fields
- * are changed under @ui_mutex, so they do not need "shadow" fields. Note, one
- * could consider to rework locking and base it on "shadow" fields.
*/
struct ubifs_inode {
- struct inode vfs_inode;
unsigned long long creat_sqnum;
- unsigned long long del_cmtno;
unsigned int xattr_size;
unsigned int xattr_cnt;
unsigned int xattr_names;
- unsigned int dirty:1;
- unsigned int xattr:1;
- unsigned int bulk_read:1;
unsigned int compr_type:2;
- struct mutex ui_mutex;
- struct rw_semaphore xattr_sem;
- spinlock_t ui_lock;
- loff_t synced_i_size;
loff_t ui_size;
int flags;
- pgoff_t last_page_read;
- pgoff_t read_in_a_row;
int data_len;
void *data;
};
@@ -673,9 +529,6 @@ typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c,
* @io_mutex: serializes write-buffer I/O
* @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes
* fields
- * @timer: write-buffer timer
- * @no_timer: non-zero if this write-buffer does not have a timer
- * @need_sync: non-zero if the timer expired and the wbuf needs sync'ing
* @next_ino: points to the next position of the following inode number
* @inodes: stores the inode numbers of the nodes which are in wbuf
*
@@ -701,9 +554,6 @@ struct ubifs_wbuf {
int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad);
struct mutex io_mutex;
spinlock_t lock;
- struct hrtimer timer;
- unsigned int no_timer:1;
- unsigned int need_sync:1;
int next_ino;
ino_t *inodes;
};
@@ -801,28 +651,6 @@ struct ubifs_znode {
};
/**
- * struct bu_info - bulk-read information.
- * @key: first data node key
- * @zbranch: zbranches of data nodes to bulk read
- * @buf: buffer to read into
- * @buf_len: buffer length
- * @gc_seq: GC sequence number to detect races with GC
- * @cnt: number of data nodes for bulk read
- * @blk_cnt: number of data blocks including holes
- * @oef: end of file reached
- */
-struct bu_info {
- union ubifs_key key;
- struct ubifs_zbranch zbranch[UBIFS_MAX_BULK_READ];
- void *buf;
- int buf_len;
- int gc_seq;
- int cnt;
- int blk_cnt;
- int eof;
-};
-
-/**
* struct ubifs_node_range - node length range description data structure.
* @len: fixed node length
* @min_len: minimum possible node length
@@ -839,24 +667,6 @@ struct ubifs_node_range {
};
/**
- * struct ubifs_compressor - UBIFS compressor description structure.
- * @compr_type: compressor type (%UBIFS_COMPR_LZO, etc)
- * @cc: cryptoapi compressor handle
- * @comp_mutex: mutex used during compression
- * @decomp_mutex: mutex used during decompression
- * @name: compressor name
- * @capi_name: cryptoapi compressor name
- */
-struct ubifs_compressor {
- int compr_type;
- struct crypto_comp *cc;
- struct mutex *comp_mutex;
- struct mutex *decomp_mutex;
- const char *name;
- const char *capi_name;
-};
-
-/**
* struct ubifs_budget_req - budget requirements of an operation.
*
* @fast: non-zero if the budgeting should try to acquire budget quickly and
@@ -943,26 +753,6 @@ struct ubifs_orphan {
};
/**
- * struct ubifs_mount_opts - UBIFS-specific mount options information.
- * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast)
- * @bulk_read: enable/disable bulk-reads (%0 default, %1 disable, %2 enable)
- * @chk_data_crc: enable/disable CRC data checking when reading data nodes
- * (%0 default, %1 disable, %2 enable)
- * @override_compr: override default compressor (%0 - do not override and use
- * superblock compressor, %1 - override and use compressor
- * specified in @compr_type)
- * @compr_type: compressor type to override the superblock compressor with
- * (%UBIFS_COMPR_NONE, etc)
- */
-struct ubifs_mount_opts {
- unsigned int unmount_mode:2;
- unsigned int bulk_read:2;
- unsigned int chk_data_crc:2;
- unsigned int override_compr:1;
- unsigned int compr_type:2;
-};
-
-/**
* struct ubifs_budg_info - UBIFS budgeting information.
* @idx_growth: amount of bytes budgeted for index growth
* @data_growth: amount of bytes budgeted for cached data
@@ -996,24 +786,12 @@ struct ubifs_budg_info {
int dent_budget;
};
-/**
- * ubifs_stats_info - per-FS statistics information.
- * @magic_errors: number of bad magic numbers (will be reset with a new mount).
- * @node_errors: number of bad nodes (will be reset with a new mount).
- * @crc_errors: number of bad crcs (will be reset with a new mount).
- */
-struct ubifs_stats_info {
- unsigned int magic_errors;
- unsigned int node_errors;
- unsigned int crc_errors;
-};
-
struct ubifs_debug_info;
/**
* struct ubifs_info - UBIFS file-system description data structure
* (per-superblock).
- * @vfs_sb: VFS @struct super_block object
+ *
* @sup_node: The super block node as read from the device
*
* @highest_inum: highest used inode number
@@ -1023,7 +801,14 @@ struct ubifs_debug_info;
* @cnt_lock: protects @highest_inum and @max_sqnum counters
* @fmt_version: UBIFS on-flash format version
* @ro_compat_version: R/O compatibility version
- * @uuid: UUID from super block
+ *
+ * @debug_level: level of debug messages, 0 - none, 1 - error message,
+ * 2 - warning message, 3 - notice message, 4 - debug message
+ * @program_type: used to identify the type of current program
+ * @program_name: program name
+ * @dev_name: device name
+ * @dev_fd: opening handler for an UBI volume or an image file
+ * @libubi: opening handler for libubi
*
* @lhead_lnum: log head logical eraseblock number
* @lhead_offs: log head offset
@@ -1048,7 +833,6 @@ struct ubifs_debug_info;
* @commit_sem: synchronizes committer with other processes
* @cmt_state: commit state
* @cs_lock: commit state lock
- * @cmt_wq: wait queue to sleep on if the log is full and a commit is running
*
* @big_lpt: flag that LPT is too big to write whole during commit
* @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
@@ -1056,11 +840,8 @@ struct ubifs_debug_info;
* @encrypted: flag indicating that this file system contains encrypted files
* @no_chk_data_crc: do not check CRCs when reading data nodes (except during
* recovery)
- * @bulk_read: enable bulk-reads
- * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
- * @rw_incompat: the media is not R/W compatible
- * @assert_action: action to take when a ubifs_assert() fails
* @authenticated: flag indigating the FS is mounted in authenticated mode
+ * @superblock_need_write: flag indicating that we need to write superblock node
*
* @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
* @calc_idx_sz
@@ -1082,15 +863,6 @@ struct ubifs_debug_info;
* @mst_node: master node
* @mst_offs: offset of valid master node
*
- * @max_bu_buf_len: maximum bulk-read buffer length
- * @bu_mutex: protects the pre-allocated bulk-read buffer and @c->bu
- * @bu: pre-allocated bulk-read information
- *
- * @write_reserve_mutex: protects @write_reserve_buf
- * @write_reserve_buf: on the write path we allocate memory, which might
- * sometimes be unavailable, in which case we use this
- * write reserve buffer
- *
* @log_lebs: number of logical eraseblocks in the log
* @log_bytes: log size in bytes
* @log_last: last LEB of the log
@@ -1103,12 +875,14 @@ struct ubifs_debug_info;
* @main_lebs: count of LEBs in the main area
* @main_first: first LEB of the main area
* @main_bytes: main area size in bytes
+ * @default_compr: default compression type
+ * @favor_lzo: favor LZO compression method
+ * @favor_percent: lzo vs. zlib threshold used in case favor LZO
*
* @key_hash_type: type of the key hash
* @key_hash: direntry key hash function
* @key_fmt: key format
* @key_len: key length
- * @hash_len: The length of the index node hashes
* @fanout: fanout of the index tree (number of links per indexing node)
*
* @min_io_size: minimal input/output unit size
@@ -1117,8 +891,6 @@ struct ubifs_debug_info;
* time (MTD write buffer size)
* @max_write_shift: number of bits in @max_write_size minus one
* @leb_size: logical eraseblock size in bytes
- * @leb_start: starting offset of logical eraseblocks within physical
- * eraseblocks
* @half_leb_size: half LEB size
* @idx_leb_size: how many bytes of an LEB are effectively available when it is
* used to store indexing nodes (@leb_size - @max_idx_node_sz)
@@ -1150,10 +922,8 @@ struct ubifs_debug_info;
* data nodes of maximum size - used in free space reporting
* @dead_wm: LEB dead space watermark
* @dark_wm: LEB dark space watermark
- * @block_cnt: count of 4KiB blocks on the FS
*
* @ranges: UBIFS node length ranges
- * @ubi: UBI volume descriptor
* @di: UBI device information
* @vi: UBI volume information
*
@@ -1172,11 +942,6 @@ struct ubifs_debug_info;
* @ohead_offs: orphan head offset
* @no_orphs: non-zero if there are no orphans
*
- * @bgt: UBIFS background thread
- * @bgt_name: background thread name
- * @need_bgt: if background thread should run
- * @need_wbuf_sync: if write-buffers have to be synchronized
- *
* @gc_lnum: LEB number used for garbage collection
* @sbuf: a buffer of LEB size used by GC and replay for scanning
* @idx_gc: list of index LEBs that have been garbage collected
@@ -1184,10 +949,6 @@ struct ubifs_debug_info;
* @gc_seq: incremented for every non-index LEB garbage collected
* @gced_lnum: last non-index LEB that was garbage collected
*
- * @infos_list: links all 'ubifs_info' objects
- * @umount_mutex: serializes shrinker and un-mount
- * @shrinker_run_no: shrinker run number
- *
* @space_bits: number of bits needed to record free or dirty space
* @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT
* @lpt_offs_bits: number of bits needed to record an offset in the LPT
@@ -1230,6 +991,7 @@ struct ubifs_debug_info;
*
* @ltab_lnum: LEB number of LPT's own lprops table
* @ltab_offs: offset of LPT's own lprops table
+ * @lpt: lprops table
* @ltab: LPT's own lprops table
* @ltab_cmt: LPT's own lprops table (commit copy)
* @lsave_cnt: number of LEB numbers in LPT's save table
@@ -1238,25 +1000,22 @@ struct ubifs_debug_info;
* @lsave: LPT's save table
* @lscan_lnum: LEB number of last LPT scan
*
- * @rp_size: size of the reserved pool in bytes
- * @report_rp_size: size of the reserved pool reported to user-space
- * @rp_uid: reserved pool user ID
- * @rp_gid: reserved pool group ID
+ * @rp_size: reserved pool size
*
- * @hash_tfm: the hash transformation used for hashing nodes
- * @hmac_tfm: the HMAC transformation for this filesystem
- * @hmac_desc_len: length of the HMAC used for authentication
- * @auth_key_name: the authentication key name
- * @auth_hash_name: the name of the hash algorithm used for authentication
- * @auth_hash_algo: the authentication hash used for this fs
+ * @hash_algo_name: the name of the hashing algorithm to use
+ * @hash_algo: The hash algo number (from include/linux/hash_info.h)
+ * @auth_key_filename: authentication key file name
+ * @x509_filename: x509 certificate file name for authentication
+ * @hash_len: the length of the hash
+ * @root_idx_hash: The hash of the root index node
+ * @lpt_hash: The hash of the LPT
+ * @mst_hash: The hash of the master node
* @log_hash: the log hash from the commit start node up to the latest reference
* node.
*
- * @empty: %1 if the UBI device is empty
* @need_recovery: %1 if the file-system needs recovery
* @replaying: %1 during journal replay
* @mounting: %1 while mounting
- * @probing: %1 while attempting to mount if SB_SILENT mount flag is set
* @remounting_rw: %1 while re-mounting from R/O mode to R/W mode
* @replay_list: temporary list used during journal replay
* @replay_buds: list of buds to replay
@@ -1266,16 +1025,11 @@ struct ubifs_debug_info;
* @rcvrd_mst_node: recovered master node to write when re-mounting R/O mounted
* FS to R/W mode
* @size_tree: inode size information for recovery
- * @mount_opts: UBIFS-specific mount options
- *
- * @dbg: debugging-related information
- * @stats: statistics exported over sysfs
*
- * @kobj: kobject for /sys/fs/ubifs/
- * @kobj_unregister: completion to unregister sysfs kobject
+ * @new_ihead_lnum: used by debugging to check @c->ihead_lnum
+ * @new_ihead_offs: used by debugging to check @c->ihead_offs
*/
struct ubifs_info {
- struct super_block *vfs_sb;
struct ubifs_sb_node *sup_node;
ino_t highest_inum;
@@ -1284,7 +1038,13 @@ struct ubifs_info {
spinlock_t cnt_lock;
int fmt_version;
int ro_compat_version;
- unsigned char uuid[16];
+
+ int debug_level;
+ int program_type;
+ const char *program_name;
+ char *dev_name;
+ int dev_fd;
+ libubi_t libubi;
int lhead_lnum;
int lhead_offs;
@@ -1306,20 +1066,12 @@ struct ubifs_info {
struct rw_semaphore commit_sem;
int cmt_state;
spinlock_t cs_lock;
- wait_queue_head_t cmt_wq;
-
- struct kobject kobj;
- struct completion kobj_unregister;
unsigned int big_lpt:1;
unsigned int space_fixup:1;
unsigned int double_hash:1;
unsigned int encrypted:1;
unsigned int no_chk_data_crc:1;
- unsigned int bulk_read:1;
- unsigned int default_compr:2;
- unsigned int rw_incompat:1;
- unsigned int assert_action:2;
unsigned int authenticated:1;
unsigned int superblock_need_write:1;
@@ -1342,13 +1094,6 @@ struct ubifs_info {
struct ubifs_mst_node *mst_node;
int mst_offs;
- int max_bu_buf_len;
- struct mutex bu_mutex;
- struct bu_info bu;
-
- struct mutex write_reserve_mutex;
- void *write_reserve_buf;
-
int log_lebs;
long long log_bytes;
int log_last;
@@ -1361,12 +1106,14 @@ struct ubifs_info {
int main_lebs;
int main_first;
long long main_bytes;
+ int default_compr;
+ int favor_lzo;
+ int favor_percent;
uint8_t key_hash_type;
uint32_t (*key_hash)(const char *str, int len);
int key_fmt;
int key_len;
- int hash_len;
int fanout;
int min_io_size;
@@ -1374,7 +1121,6 @@ struct ubifs_info {
int max_write_size;
int max_write_shift;
int leb_size;
- int leb_start;
int half_leb_size;
int idx_leb_size;
int leb_cnt;
@@ -1402,12 +1148,10 @@ struct ubifs_info {
int leb_overhead;
int dead_wm;
int dark_wm;
- int block_cnt;
struct ubifs_node_range ranges[UBIFS_NODE_TYPES_CNT];
- struct ubi_volume_desc *ubi;
- struct ubi_device_info di;
- struct ubi_volume_info vi;
+ struct ubi_dev_info di;
+ struct ubi_vol_info vi;
struct rb_root orph_tree;
struct list_head orph_list;
@@ -1424,11 +1168,6 @@ struct ubifs_info {
int ohead_offs;
int no_orphs;
- struct task_struct *bgt;
- char bgt_name[sizeof(BGT_NAME_PATTERN) + 9];
- int need_bgt;
- int need_wbuf_sync;
-
int gc_lnum;
void *sbuf;
struct list_head idx_gc;
@@ -1436,10 +1175,6 @@ struct ubifs_info {
int gc_seq;
int gced_lnum;
- struct list_head infos_list;
- struct mutex umount_mutex;
- unsigned int shrinker_run_no;
-
int space_bits;
int lpt_lnum_bits;
int lpt_offs_bits;
@@ -1480,6 +1215,7 @@ struct ubifs_info {
int ltab_lnum;
int ltab_offs;
+ struct ubifs_lprops *lpt;
struct ubifs_lpt_lprops *ltab;
struct ubifs_lpt_lprops *ltab_cmt;
int lsave_cnt;
@@ -1489,97 +1225,99 @@ struct ubifs_info {
int lscan_lnum;
long long rp_size;
- long long report_rp_size;
- kuid_t rp_uid;
- kgid_t rp_gid;
- struct crypto_shash *hash_tfm;
- struct crypto_shash *hmac_tfm;
- int hmac_desc_len;
- char *auth_key_name;
- char *auth_hash_name;
- enum hash_algo auth_hash_algo;
+ char *hash_algo_name;
+ int hash_algo;
+ char *auth_key_filename;
+ char *auth_cert_filename;
+ int hash_len;
+ uint8_t root_idx_hash[UBIFS_MAX_HASH_LEN];
+ uint8_t lpt_hash[UBIFS_MAX_HASH_LEN];
+ uint8_t mst_hash[UBIFS_MAX_HASH_LEN];
struct shash_desc *log_hash;
- /* The below fields are used only during mounting and re-mounting */
- unsigned int empty:1;
unsigned int need_recovery:1;
unsigned int replaying:1;
unsigned int mounting:1;
unsigned int remounting_rw:1;
- unsigned int probing:1;
struct list_head replay_list;
struct list_head replay_buds;
unsigned long long cs_sqnum;
struct list_head unclean_leb_list;
struct ubifs_mst_node *rcvrd_mst_node;
struct rb_root size_tree;
- struct ubifs_mount_opts mount_opts;
- struct ubifs_debug_info *dbg;
- struct ubifs_stats_info *stats;
+ int new_ihead_lnum;
+ int new_ihead_offs;
};
-extern struct list_head ubifs_infos;
-extern spinlock_t ubifs_infos_lock;
extern atomic_long_t ubifs_clean_zn_cnt;
-extern const struct super_operations ubifs_super_operations;
-extern const struct address_space_operations ubifs_file_address_operations;
-extern const struct file_operations ubifs_file_operations;
-extern const struct inode_operations ubifs_file_inode_operations;
-extern const struct file_operations ubifs_dir_operations;
-extern const struct inode_operations ubifs_dir_inode_operations;
-extern const struct inode_operations ubifs_symlink_inode_operations;
-extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
-extern int ubifs_default_version;
/* auth.c */
static inline int ubifs_authenticated(const struct ubifs_info *c)
{
- return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated;
+ return c->authenticated;
}
-struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c);
-static inline struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
-{
- return ubifs_authenticated(c) ? __ubifs_hash_get_desc(c) : NULL;
-}
-
-static inline int ubifs_shash_init(const struct ubifs_info *c,
- struct shash_desc *desc)
-{
- if (ubifs_authenticated(c))
- return crypto_shash_init(desc);
- else
- return 0;
-}
+#ifdef WITH_CRYPTO
+int ubifs_init_authentication(struct ubifs_info *c);
+int ubifs_shash_init(const struct ubifs_info *c, struct shash_desc *desc);
+int ubifs_shash_update(const struct ubifs_info *c, struct shash_desc *desc,
+ const void *buf, unsigned int len);
+int ubifs_shash_final(const struct ubifs_info *c, struct shash_desc *desc,
+ u8 *out);
+struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c);
+int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
+ u8 *hash);
+int ubifs_master_node_calc_hash(const struct ubifs_info *c, const void *node,
+ uint8_t *hash);
+int ubifs_sign_superblock_node(struct ubifs_info *c, void *node);
+void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
+ const u8 *hash, int lnum, int offs);
+void __ubifs_exit_authentication(struct ubifs_info *c);
+#else
+static inline int ubifs_init_authentication(__unused struct ubifs_info *c)
+{ return 0; }
+static inline int ubifs_shash_init(__unused const struct ubifs_info *c,
+ __unused struct shash_desc *desc)
+{ return 0; }
+static inline int ubifs_shash_update(__unused const struct ubifs_info *c,
+ __unused struct shash_desc *desc,
+ __unused const void *buf,
+ __unused unsigned int len) { return 0; }
+static inline int ubifs_shash_final(__unused const struct ubifs_info *c,
+ __unused struct shash_desc *desc,
+ __unused u8 *out) { return 0; }
+static inline struct shash_desc *
+ubifs_hash_get_desc(__unused const struct ubifs_info *c) { return NULL; }
+static inline int __ubifs_node_calc_hash(__unused const struct ubifs_info *c,
+ __unused const void *buf,
+ __unused u8 *hash) { return 0; }
+static inline int
+ubifs_master_node_calc_hash(__unused const struct ubifs_info *c,
+ __unused const void *node, __unused uint8_t *hash)
+{ return 0; }
+static inline int ubifs_sign_superblock_node(__unused struct ubifs_info *c,
+ __unused void *node)
+{ return 0; }
+static inline void ubifs_bad_hash(__unused const struct ubifs_info *c,
+ __unused const void *node,
+ __unused const u8 *hash, __unused int lnum,
+ __unused int offs) {}
+static inline void __ubifs_exit_authentication(__unused struct ubifs_info *c) {}
+#endif
-static inline int ubifs_shash_update(const struct ubifs_info *c,
- struct shash_desc *desc, const void *buf,
- unsigned int len)
+static inline int ubifs_prepare_auth_node(__unused struct ubifs_info *c,
+ __unused void *node,
+ __unused struct shash_desc *inhash)
{
- int err = 0;
-
- if (ubifs_authenticated(c)) {
- err = crypto_shash_update(desc, buf, len);
- if (err < 0)
- return err;
- }
-
+ // To be implemented
return 0;
}
-static inline int ubifs_shash_final(const struct ubifs_info *c,
- struct shash_desc *desc, u8 *out)
-{
- return ubifs_authenticated(c) ? crypto_shash_final(desc, out) : 0;
-}
-
-int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
- u8 *hash);
-static inline int ubifs_node_calc_hash(const struct ubifs_info *c,
- const void *buf, u8 *hash)
+static inline int
+ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf, u8 *hash)
{
if (ubifs_authenticated(c))
return __ubifs_node_calc_hash(c, buf, hash);
@@ -1587,8 +1325,13 @@ static inline int ubifs_node_calc_hash(const struct ubifs_info *c,
return 0;
}
-int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
- struct shash_desc *inhash);
+static inline int
+ubifs_node_check_hash(__unused const struct ubifs_info *c,
+ __unused const void *buf, __unused const u8 *expected)
+{
+ // To be implemented
+ return 0;
+}
/**
* ubifs_check_hash - compare two hashes
@@ -1599,10 +1342,12 @@ int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
* Compare two hashes @expected and @got. Returns 0 when they are equal, a
* negative error code otherwise.
*/
-static inline int ubifs_check_hash(const struct ubifs_info *c,
- const u8 *expected, const u8 *got)
+static inline int
+ubifs_check_hash(__unused const struct ubifs_info *c,
+ __unused const u8 *expected, __unused const u8 *got)
{
- return crypto_memneq(expected, got, c->hash_len);
+ // To be implemented
+ return 0;
}
/**
@@ -1614,37 +1359,12 @@ static inline int ubifs_check_hash(const struct ubifs_info *c,
* Compare two hashes @expected and @got. Returns 0 when they are equal, a
* negative error code otherwise.
*/
-static inline int ubifs_check_hmac(const struct ubifs_info *c,
- const u8 *expected, const u8 *got)
-{
- return crypto_memneq(expected, got, c->hmac_desc_len);
-}
-
-#ifdef CONFIG_UBIFS_FS_AUTHENTICATION
-void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
- const u8 *hash, int lnum, int offs);
-#else
-static inline void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
- const u8 *hash, int lnum, int offs) {};
-#endif
-
-int __ubifs_node_check_hash(const struct ubifs_info *c, const void *buf,
- const u8 *expected);
-static inline int ubifs_node_check_hash(const struct ubifs_info *c,
- const void *buf, const u8 *expected)
+static inline int
+ubifs_check_hmac(__unused const struct ubifs_info *c,
+ __unused const u8 *expected, __unused const u8 *got)
{
- if (ubifs_authenticated(c))
- return __ubifs_node_check_hash(c, buf, expected);
- else
- return 0;
-}
-
-int ubifs_init_authentication(struct ubifs_info *c);
-void __ubifs_exit_authentication(struct ubifs_info *c);
-static inline void ubifs_exit_authentication(struct ubifs_info *c)
-{
- if (ubifs_authenticated(c))
- __ubifs_exit_authentication(c);
+ // To be implemented
+ return 0;
}
/**
@@ -1655,8 +1375,8 @@ static inline void ubifs_exit_authentication(struct ubifs_info *c)
* This returns a pointer to the hash of a branch. Since the key already is a
* dynamically sized object we cannot use a struct member here.
*/
-static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
- struct ubifs_branch *br)
+static inline u8 *
+ubifs_branch_hash(struct ubifs_info *c, struct ubifs_branch *br)
{
return (void *)br + sizeof(*br) + c->key_len;
}
@@ -1669,33 +1389,28 @@ static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
*
* With authentication this copies a hash, otherwise does nothing.
*/
-static inline void ubifs_copy_hash(const struct ubifs_info *c, const u8 *from,
- u8 *to)
+static inline void
+ubifs_copy_hash(const struct ubifs_info *c, const u8 *from, u8 *to)
{
if (ubifs_authenticated(c))
memcpy(to, from, c->hash_len);
}
-int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
- int len, int ofs_hmac);
-static inline int ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
- int len, int ofs_hmac)
+static inline int
+ubifs_node_insert_hmac(__unused const struct ubifs_info *c, __unused void *buf,
+ __unused int len, __unused int ofs_hmac)
{
- if (ubifs_authenticated(c))
- return __ubifs_node_insert_hmac(c, buf, len, ofs_hmac);
- else
- return 0;
+ // To be implemented
+ return 0;
}
-int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *buf,
- int len, int ofs_hmac);
-static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
- const void *buf, int len, int ofs_hmac)
+static inline int
+ubifs_node_verify_hmac(__unused const struct ubifs_info *c,
+ __unused const void *buf, __unused int len,
+ __unused int ofs_hmac)
{
- if (ubifs_authenticated(c))
- return __ubifs_node_verify_hmac(c, buf, len, ofs_hmac);
- else
- return 0;
+ // To be implemented
+ return 0;
}
/**
@@ -1706,29 +1421,33 @@ static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
* be 0 for unauthenticated filesystems or the real size of an auth node
* authentication is enabled.
*/
-static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
+static inline int
+ubifs_auth_node_sz(__unused const struct ubifs_info *c)
{
- if (ubifs_authenticated(c))
- return sizeof(struct ubifs_auth_node) + c->hmac_desc_len;
- else
- return 0;
+ // To be implemented
+ return 0;
}
-int ubifs_sb_verify_signature(struct ubifs_info *c,
- const struct ubifs_sb_node *sup);
-bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac);
-int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
+static inline bool
+ubifs_hmac_zero(__unused struct ubifs_info *c, __unused const u8 *hmac)
+{
+ // To be implemented
+ return true;
+}
-int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
- struct shash_desc *target);
-static inline int ubifs_shash_copy_state(const struct ubifs_info *c,
- struct shash_desc *src,
- struct shash_desc *target)
+static inline int
+ubifs_shash_copy_state(__unused const struct ubifs_info *c,
+ __unused struct shash_desc *src,
+ __unused struct shash_desc *target)
+{
+ // To be implemented
+ return 0;
+}
+
+static inline void ubifs_exit_authentication(struct ubifs_info *c)
{
if (ubifs_authenticated(c))
- return __ubifs_shash_copy_state(c, src, target);
- else
- return 0;
+ __ubifs_exit_authentication(c);
}
/* io.c */
@@ -1763,9 +1482,6 @@ void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
int ubifs_io_init(struct ubifs_info *c);
void ubifs_pad(const struct ubifs_info *c, void *buf, int pad);
int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf);
-int ubifs_bg_wbufs_sync(struct ubifs_info *c);
-void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum);
-int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode);
/* scan.c */
struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
@@ -1793,49 +1509,11 @@ int ubifs_log_end_commit(struct ubifs_info *c, int new_ltail_lnum);
int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum);
int ubifs_consolidate_log(struct ubifs_info *c);
-/* journal.c */
-int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
- const struct fscrypt_name *nm, const struct inode *inode,
- int deletion, int xent);
-int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
- const union ubifs_key *key, const void *buf, int len);
-int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
-int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
-int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
- const struct inode *fst_inode,
- const struct fscrypt_name *fst_nm,
- const struct inode *snd_dir,
- const struct inode *snd_inode,
- const struct fscrypt_name *snd_nm, int sync);
-int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
- const struct inode *old_inode,
- const struct fscrypt_name *old_nm,
- const struct inode *new_dir,
- const struct inode *new_inode,
- const struct fscrypt_name *new_nm,
- const struct inode *whiteout, int sync);
-int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
- loff_t old_size, loff_t new_size);
-int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
- const struct inode *inode, const struct fscrypt_name *nm);
-int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode1,
- const struct inode *inode2);
-
/* budget.c */
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req);
void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req);
-void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
- struct ubifs_inode *ui);
-int ubifs_budget_inode_op(struct ubifs_info *c, struct inode *inode,
- struct ubifs_budget_req *req);
-void ubifs_release_ino_dirty(struct ubifs_info *c, struct inode *inode,
- struct ubifs_budget_req *req);
-void ubifs_cancel_ino_op(struct ubifs_info *c, struct inode *inode,
- struct ubifs_budget_req *req);
-long long ubifs_get_free_space(struct ubifs_info *c);
long long ubifs_get_free_space_nolock(struct ubifs_info *c);
int ubifs_calc_min_idx_lebs(struct ubifs_info *c);
-void ubifs_convert_page_budget(struct ubifs_info *c);
long long ubifs_reported_space(const struct ubifs_info *c, long long free);
long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs);
@@ -1853,8 +1531,6 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_znode **zn, int *n);
int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
void *node, const struct fscrypt_name *nm);
-int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
- void *node, uint32_t secondary_hash);
int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
void *node, int *lnum, int *offs);
int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
@@ -1867,8 +1543,6 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
const struct fscrypt_name *nm);
-int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
- uint32_t cookie);
int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
union ubifs_key *to_key);
int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
@@ -1885,13 +1559,8 @@ void destroy_old_idx(struct ubifs_info *c);
int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
int lnum, int offs);
int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode);
-int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu);
-int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu);
/* tnc_misc.c */
-struct ubifs_znode *ubifs_tnc_levelorder_next(const struct ubifs_info *c,
- struct ubifs_znode *zr,
- struct ubifs_znode *znode);
int ubifs_search_zbranch(const struct ubifs_info *c,
const struct ubifs_znode *znode,
const union ubifs_key *key, int *n);
@@ -1911,14 +1580,7 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int ubifs_tnc_end_commit(struct ubifs_info *c);
-/* shrinker.c */
-unsigned long ubifs_shrink_scan(struct shrinker *shrink,
- struct shrink_control *sc);
-unsigned long ubifs_shrink_count(struct shrinker *shrink,
- struct shrink_control *sc);
-
/* commit.c */
-int ubifs_bg_thread(void *info);
void ubifs_commit_required(struct ubifs_info *c);
void ubifs_request_bg_commit(struct ubifs_info *c);
int ubifs_run_commit(struct ubifs_info *c);
@@ -1935,7 +1597,6 @@ int ubifs_write_master(struct ubifs_info *c);
int ubifs_read_superblock(struct ubifs_info *c);
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
int ubifs_fixup_free_space(struct ubifs_info *c);
-int ubifs_enable_encryption(struct ubifs_info *c);
/* replay.c */
int ubifs_validate_entry(struct ubifs_info *c,
@@ -1951,19 +1612,16 @@ int ubifs_get_idx_gc_leb(struct ubifs_info *c);
int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp);
/* orphan.c */
-int ubifs_add_orphan(struct ubifs_info *c, ino_t inum);
-void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum);
int ubifs_orphan_start_commit(struct ubifs_info *c);
int ubifs_orphan_end_commit(struct ubifs_info *c);
int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only);
int ubifs_clear_orphans(struct ubifs_info *c);
/* lpt.c */
+int ubifs_calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt);
int ubifs_calc_lpt_geom(struct ubifs_info *c);
int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
u8 *hash);
-int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
- int *lpt_lebs, int *big_lpt, u8 *hash);
int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
@@ -2023,142 +1681,54 @@ const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c);
const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);
int ubifs_calc_dark(const struct ubifs_info *c, int spc);
-/* file.c */
-int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
-int ubifs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
- struct iattr *attr);
-int ubifs_update_time(struct inode *inode, int flags);
-
-/* dir.c */
-struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
- umode_t mode, bool is_xattr);
-int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,
- struct kstat *stat, u32 request_mask, unsigned int flags);
-int ubifs_check_dir_empty(struct inode *dir);
-
-/* xattr.c */
-int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
- size_t size, int flags, bool check_lock);
-ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
- size_t size);
-
-#ifdef CONFIG_UBIFS_FS_XATTR
-extern const struct xattr_handler * const ubifs_xattr_handlers[];
-ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size);
-void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum);
-int ubifs_purge_xattrs(struct inode *host);
-#else
-#define ubifs_listxattr NULL
-#define ubifs_xattr_handlers NULL
-static inline void ubifs_evict_xattr_inode(struct ubifs_info *c,
- ino_t xattr_inum) { }
-static inline int ubifs_purge_xattrs(struct inode *host)
-{
- return 0;
-}
-#endif
-
-#ifdef CONFIG_UBIFS_FS_SECURITY
-extern int ubifs_init_security(struct inode *dentry, struct inode *inode,
- const struct qstr *qstr);
-#else
-static inline int ubifs_init_security(struct inode *dentry,
- struct inode *inode, const struct qstr *qstr)
-{
- return 0;
-}
-#endif
-
-
/* super.c */
-struct inode *ubifs_iget(struct super_block *sb, unsigned long inum);
+int open_ubi(struct ubifs_info *c, const char *node);
+void close_ubi(struct ubifs_info *c);
+int open_target(struct ubifs_info *c);
+int close_target(struct ubifs_info *c);
+int check_volume_empty(struct ubifs_info *c);
+void init_ubifs_info(struct ubifs_info *c, int program_type);
+int init_constants_early(struct ubifs_info *c);
+int init_constants_sb(struct ubifs_info *c);
+void init_constants_master(struct ubifs_info *c);
+int take_gc_lnum(struct ubifs_info *c);
+int alloc_wbufs(struct ubifs_info *c);
+void free_wbufs(struct ubifs_info *c);
+void free_orphans(struct ubifs_info *c);
+void destroy_journal(struct ubifs_info *c);
/* recovery.c */
int ubifs_recover_master_node(struct ubifs_info *c);
-int ubifs_write_rcvrd_mst_node(struct ubifs_info *c);
struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
int offs, void *sbuf, int jhead);
struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum,
int offs, void *sbuf);
int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf);
-int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
int ubifs_rcvry_gc_commit(struct ubifs_info *c);
int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
int deletion, loff_t new_size);
int ubifs_recover_size(struct ubifs_info *c, bool in_place);
void ubifs_destroy_size_tree(struct ubifs_info *c);
-/* ioctl.c */
-int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
-int ubifs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
-long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-void ubifs_set_inode_flags(struct inode *inode);
-#ifdef CONFIG_COMPAT
-long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-#endif
-
-/* compressor.c */
-int __init ubifs_compressors_init(void);
-void ubifs_compressors_exit(void);
-void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
- void *out_buf, int *out_len, int *compr_type);
-int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
- void *out, int *out_len, int compr_type);
-
-/* sysfs.c */
-int ubifs_sysfs_init(void);
-void ubifs_sysfs_exit(void);
-int ubifs_sysfs_register(struct ubifs_info *c);
-void ubifs_sysfs_unregister(struct ubifs_info *c);
-
-#include "debug.h"
-#include "misc.h"
-#include "key.h"
-
-#ifndef CONFIG_FS_ENCRYPTION
-static inline int ubifs_encrypt(const struct inode *inode,
- struct ubifs_data_node *dn,
- unsigned int in_len, unsigned int *out_len,
- int block)
-{
- struct ubifs_info *c = inode->i_sb->s_fs_info;
- ubifs_assert(c, 0);
- return -EOPNOTSUPP;
-}
-static inline int ubifs_decrypt(const struct inode *inode,
- struct ubifs_data_node *dn,
- unsigned int *out_len, int block)
-{
- struct ubifs_info *c = inode->i_sb->s_fs_info;
- ubifs_assert(c, 0);
- return -EOPNOTSUPP;
-}
-#else
-/* crypto.c */
-int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
- unsigned int in_len, unsigned int *out_len, int block);
-int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn,
- unsigned int *out_len, int block);
-#endif
-
-extern const struct fscrypt_operations ubifs_crypt_operations;
-
/* Normal UBIFS messages */
-__printf(2, 3)
-void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...);
-__printf(2, 3)
-void ubifs_err(const struct ubifs_info *c, const char *fmt, ...);
-__printf(2, 3)
-void ubifs_warn(const struct ubifs_info *c, const char *fmt, ...);
-/*
- * A conditional variant of 'ubifs_err()' which doesn't output anything
- * if probing (ie. SB_SILENT set).
- */
-#define ubifs_errc(c, fmt, ...) \
-do { \
- if (!(c)->probing) \
- ubifs_err(c, fmt, ##__VA_ARGS__); \
+enum { ERR_LEVEL = 1, WARN_LEVEL, INFO_LEVEL, DEBUG_LEVEL };
+#define ubifs_msg(c, fmt, ...) do { \
+ if (c->debug_level >= INFO_LEVEL) \
+ printf("<INFO> %s[%d] (%s): %s: " fmt "\n", \
+ c->program_name, getpid(), \
+ c->dev_name, __FUNCTION__, ##__VA_ARGS__); \
+} while (0)
+#define ubifs_warn(c, fmt, ...) do { \
+ if (c->debug_level >= WARN_LEVEL) \
+ printf("<WARN> %s[%d] (%s): %s: " fmt "\n", \
+ c->program_name, getpid(), \
+ c->dev_name, __FUNCTION__, ##__VA_ARGS__); \
+} while (0)
+#define ubifs_err(c, fmt, ...) do { \
+ if (c->debug_level >= ERR_LEVEL) \
+ printf("<ERROR> %s[%d] (%s): %s: " fmt "\n", \
+ c->program_name, getpid(), \
+ c->dev_name, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
#endif /* !__UBIFS_H__ */
--
2.13.6
Adapt sb.c in libubifs, compared with linux kernel implementations:
1. Remove authentication related implementations
(eg. authenticate_sb_node), authentication is not supported in fsck
for now.
2. Remove some functions(eg. create_default_filesystem) which won't be
used in fsck/mkfs.
3. Modify ubifs_read_superblock(), remove some assignments which won't
be used in mkfs/fsck.
4. Apapt fixup_leb to ignore %-EBADMSG, subsequent steps will check
all areas carefully.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/sb.c | 482 ++--------------------------------------------
1 file changed, 18 insertions(+), 464 deletions(-)
diff --git a/ubifs-utils/libubifs/sb.c b/ubifs-utils/libubifs/sb.c
index e7693b94..312661d6 100644
--- a/ubifs-utils/libubifs/sb.c
+++ b/ubifs-utils/libubifs/sb.c
@@ -14,358 +14,18 @@
* change it. The superblock node mostly contains geometry information.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
-#include <linux/slab.h>
-#include <linux/math64.h>
-#include <linux/uuid.h>
-
-/*
- * Default journal size in logical eraseblocks as a percent of total
- * flash size.
- */
-#define DEFAULT_JNL_PERCENT 5
-
-/* Default maximum journal size in bytes */
-#define DEFAULT_MAX_JNL (32*1024*1024)
-
-/* Default indexing tree fanout */
-#define DEFAULT_FANOUT 8
-
-/* Default number of data journal heads */
-#define DEFAULT_JHEADS_CNT 1
-
-/* Default positions of different LEBs in the main area */
-#define DEFAULT_IDX_LEB 0
-#define DEFAULT_DATA_LEB 1
-#define DEFAULT_GC_LEB 2
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/* Default number of LEB numbers in LPT's save table */
#define DEFAULT_LSAVE_CNT 256
-/* Default reserved pool size as a percent of maximum free space */
-#define DEFAULT_RP_PERCENT 5
-
-/* The default maximum size of reserved pool in bytes */
-#define DEFAULT_MAX_RP_SIZE (5*1024*1024)
-
-/* Default time granularity in nanoseconds */
-#define DEFAULT_TIME_GRAN 1000000000
-
-static int get_default_compressor(struct ubifs_info *c)
-{
- if (ubifs_compr_present(c, UBIFS_COMPR_ZSTD))
- return UBIFS_COMPR_ZSTD;
-
- if (ubifs_compr_present(c, UBIFS_COMPR_LZO))
- return UBIFS_COMPR_LZO;
-
- if (ubifs_compr_present(c, UBIFS_COMPR_ZLIB))
- return UBIFS_COMPR_ZLIB;
-
- return UBIFS_COMPR_NONE;
-}
-
-/**
- * create_default_filesystem - format empty UBI volume.
- * @c: UBIFS file-system description object
- *
- * This function creates default empty file-system. Returns zero in case of
- * success and a negative error code in case of failure.
- */
-static int create_default_filesystem(struct ubifs_info *c)
-{
- struct ubifs_sb_node *sup;
- struct ubifs_mst_node *mst;
- struct ubifs_idx_node *idx;
- struct ubifs_branch *br;
- struct ubifs_ino_node *ino;
- struct ubifs_cs_node *cs;
- union ubifs_key key;
- int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
- int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
- int min_leb_cnt = UBIFS_MIN_LEB_CNT;
- int idx_node_size;
- long long tmp64, main_bytes;
- __le64 tmp_le64;
- struct timespec64 ts;
- u8 hash[UBIFS_HASH_ARR_SZ];
- u8 hash_lpt[UBIFS_HASH_ARR_SZ];
-
- /* Some functions called from here depend on the @c->key_len filed */
- c->key_len = UBIFS_SK_LEN;
-
- /*
- * First of all, we have to calculate default file-system geometry -
- * log size, journal size, etc.
- */
- if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT)
- /* We can first multiply then divide and have no overflow */
- jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100;
- else
- jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT;
-
- if (jnl_lebs < UBIFS_MIN_JNL_LEBS)
- jnl_lebs = UBIFS_MIN_JNL_LEBS;
- if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL)
- jnl_lebs = DEFAULT_MAX_JNL / c->leb_size;
-
- /*
- * The log should be large enough to fit reference nodes for all bud
- * LEBs. Because buds do not have to start from the beginning of LEBs
- * (half of the LEB may contain committed data), the log should
- * generally be larger, make it twice as large.
- */
- tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1;
- log_lebs = tmp / c->leb_size;
- /* Plus one LEB reserved for commit */
- log_lebs += 1;
- if (c->leb_cnt - min_leb_cnt > 8) {
- /* And some extra space to allow writes while committing */
- log_lebs += 1;
- min_leb_cnt += 1;
- }
-
- max_buds = jnl_lebs - log_lebs;
- if (max_buds < UBIFS_MIN_BUD_LEBS)
- max_buds = UBIFS_MIN_BUD_LEBS;
-
- /*
- * Orphan nodes are stored in a separate area. One node can store a lot
- * of orphan inode numbers, but when new orphan comes we just add a new
- * orphan node. At some point the nodes are consolidated into one
- * orphan node.
- */
- orph_lebs = UBIFS_MIN_ORPH_LEBS;
- if (c->leb_cnt - min_leb_cnt > 1)
- /*
- * For debugging purposes it is better to have at least 2
- * orphan LEBs, because the orphan subsystem would need to do
- * consolidations and would be stressed more.
- */
- orph_lebs += 1;
-
- main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs;
- main_lebs -= orph_lebs;
-
- lpt_first = UBIFS_LOG_LNUM + log_lebs;
- c->lsave_cnt = DEFAULT_LSAVE_CNT;
- c->max_leb_cnt = c->leb_cnt;
- err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
- &big_lpt, hash_lpt);
- if (err)
- return err;
-
- dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first,
- lpt_first + lpt_lebs - 1);
-
- main_first = c->leb_cnt - main_lebs;
-
- sup = kzalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_KERNEL);
- mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
- idx_node_size = ubifs_idx_node_sz(c, 1);
- idx = kzalloc(ALIGN(idx_node_size, c->min_io_size), GFP_KERNEL);
- ino = kzalloc(ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size), GFP_KERNEL);
- cs = kzalloc(ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size), GFP_KERNEL);
-
- if (!sup || !mst || !idx || !ino || !cs) {
- err = -ENOMEM;
- goto out;
- }
-
- /* Create default superblock */
-
- tmp64 = (long long)max_buds * c->leb_size;
- if (big_lpt)
- sup_flags |= UBIFS_FLG_BIGLPT;
- if (ubifs_default_version > 4)
- sup_flags |= UBIFS_FLG_DOUBLE_HASH;
-
- if (ubifs_authenticated(c)) {
- sup_flags |= UBIFS_FLG_AUTHENTICATION;
- sup->hash_algo = cpu_to_le16(c->auth_hash_algo);
- err = ubifs_hmac_wkm(c, sup->hmac_wkm);
- if (err)
- goto out;
- } else {
- sup->hash_algo = cpu_to_le16(0xffff);
- }
-
- sup->ch.node_type = UBIFS_SB_NODE;
- sup->key_hash = UBIFS_KEY_HASH_R5;
- sup->flags = cpu_to_le32(sup_flags);
- sup->min_io_size = cpu_to_le32(c->min_io_size);
- sup->leb_size = cpu_to_le32(c->leb_size);
- sup->leb_cnt = cpu_to_le32(c->leb_cnt);
- sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt);
- sup->max_bud_bytes = cpu_to_le64(tmp64);
- sup->log_lebs = cpu_to_le32(log_lebs);
- sup->lpt_lebs = cpu_to_le32(lpt_lebs);
- sup->orph_lebs = cpu_to_le32(orph_lebs);
- sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT);
- sup->fanout = cpu_to_le32(DEFAULT_FANOUT);
- sup->lsave_cnt = cpu_to_le32(c->lsave_cnt);
- sup->fmt_version = cpu_to_le32(ubifs_default_version);
- sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN);
- if (c->mount_opts.override_compr)
- sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
- else
- sup->default_compr = cpu_to_le16(get_default_compressor(c));
-
- generate_random_uuid(sup->uuid);
-
- main_bytes = (long long)main_lebs * c->leb_size;
- tmp64 = div_u64(main_bytes * DEFAULT_RP_PERCENT, 100);
- if (tmp64 > DEFAULT_MAX_RP_SIZE)
- tmp64 = DEFAULT_MAX_RP_SIZE;
- sup->rp_size = cpu_to_le64(tmp64);
- sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
-
- dbg_gen("default superblock created at LEB 0:0");
-
- /* Create default master node */
-
- mst->ch.node_type = UBIFS_MST_NODE;
- mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
- mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO);
- mst->cmt_no = 0;
- mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
- mst->root_offs = 0;
- tmp = ubifs_idx_node_sz(c, 1);
- mst->root_len = cpu_to_le32(tmp);
- mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB);
- mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
- mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size));
- mst->index_size = cpu_to_le64(ALIGN(tmp, 8));
- mst->lpt_lnum = cpu_to_le32(c->lpt_lnum);
- mst->lpt_offs = cpu_to_le32(c->lpt_offs);
- mst->nhead_lnum = cpu_to_le32(c->nhead_lnum);
- mst->nhead_offs = cpu_to_le32(c->nhead_offs);
- mst->ltab_lnum = cpu_to_le32(c->ltab_lnum);
- mst->ltab_offs = cpu_to_le32(c->ltab_offs);
- mst->lsave_lnum = cpu_to_le32(c->lsave_lnum);
- mst->lsave_offs = cpu_to_le32(c->lsave_offs);
- mst->lscan_lnum = cpu_to_le32(main_first);
- mst->empty_lebs = cpu_to_le32(main_lebs - 2);
- mst->idx_lebs = cpu_to_le32(1);
- mst->leb_cnt = cpu_to_le32(c->leb_cnt);
- ubifs_copy_hash(c, hash_lpt, mst->hash_lpt);
-
- /* Calculate lprops statistics */
- tmp64 = main_bytes;
- tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
- tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
- mst->total_free = cpu_to_le64(tmp64);
-
- tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
- ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) -
- UBIFS_INO_NODE_SZ;
- tmp64 += ino_waste;
- tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8);
- mst->total_dirty = cpu_to_le64(tmp64);
-
- /* The indexing LEB does not contribute to dark space */
- tmp64 = ((long long)(c->main_lebs - 1) * c->dark_wm);
- mst->total_dark = cpu_to_le64(tmp64);
-
- mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
-
- dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
-
- /* Create the root indexing node */
-
- c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
- c->key_hash = key_r5_hash;
-
- idx->ch.node_type = UBIFS_IDX_NODE;
- idx->child_cnt = cpu_to_le16(1);
- ino_key_init(c, &key, UBIFS_ROOT_INO);
- br = ubifs_idx_branch(c, idx, 0);
- key_write_idx(c, &key, &br->key);
- br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
- br->len = cpu_to_le32(UBIFS_INO_NODE_SZ);
-
- dbg_gen("default root indexing node created LEB %d:0",
- main_first + DEFAULT_IDX_LEB);
-
- /* Create default root inode */
-
- ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
- ino->ch.node_type = UBIFS_INO_NODE;
- ino->creat_sqnum = cpu_to_le64(++c->max_sqnum);
- ino->nlink = cpu_to_le32(2);
-
- ktime_get_coarse_real_ts64(&ts);
- tmp_le64 = cpu_to_le64(ts.tv_sec);
- ino->atime_sec = tmp_le64;
- ino->ctime_sec = tmp_le64;
- ino->mtime_sec = tmp_le64;
- ino->atime_nsec = 0;
- ino->ctime_nsec = 0;
- ino->mtime_nsec = 0;
- ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
- ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ);
-
- /* Set compression enabled by default */
- ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
-
- dbg_gen("root inode created at LEB %d:0",
- main_first + DEFAULT_DATA_LEB);
-
- /*
- * The first node in the log has to be the commit start node. This is
- * always the case during normal file-system operation. Write a fake
- * commit start node to the log.
- */
-
- cs->ch.node_type = UBIFS_CS_NODE;
-
- err = ubifs_write_node_hmac(c, sup, UBIFS_SB_NODE_SZ, 0, 0,
- offsetof(struct ubifs_sb_node, hmac));
- if (err)
- goto out;
-
- err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
- main_first + DEFAULT_DATA_LEB, 0);
- if (err)
- goto out;
-
- ubifs_node_calc_hash(c, ino, hash);
- ubifs_copy_hash(c, hash, ubifs_branch_hash(c, br));
-
- err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
- if (err)
- goto out;
-
- ubifs_node_calc_hash(c, idx, hash);
- ubifs_copy_hash(c, hash, mst->hash_root_idx);
-
- err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0,
- offsetof(struct ubifs_mst_node, hmac));
- if (err)
- goto out;
-
- err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
- 0, offsetof(struct ubifs_mst_node, hmac));
- if (err)
- goto out;
-
- err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
- if (err)
- goto out;
-
- ubifs_msg(c, "default file-system created");
-
- err = 0;
-out:
- kfree(sup);
- kfree(mst);
- kfree(idx);
- kfree(ino);
- kfree(cs);
-
- return err;
-}
-
/**
* validate_sb - validate superblock node.
* @c: UBIFS file-system description object
@@ -419,9 +79,9 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
min_leb_cnt = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs;
min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;
- if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) {
+ if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.rsvd_lebs) {
ubifs_err(c, "bad LEB count: %d in superblock, %d on UBI volume, %d minimum required",
- c->leb_cnt, c->vi.size, min_leb_cnt);
+ c->leb_cnt, c->vi.rsvd_lebs, min_leb_cnt);
goto failed;
}
@@ -537,72 +197,19 @@ static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
return sup;
}
-static int authenticate_sb_node(struct ubifs_info *c,
+static int authenticate_sb_node(__unused struct ubifs_info *c,
const struct ubifs_sb_node *sup)
{
unsigned int sup_flags = le32_to_cpu(sup->flags);
- u8 hmac_wkm[UBIFS_HMAC_ARR_SZ];
int authenticated = !!(sup_flags & UBIFS_FLG_AUTHENTICATION);
- int hash_algo;
- int err;
-
- if (c->authenticated && !authenticated) {
- ubifs_err(c, "authenticated FS forced, but found FS without authentication");
- return -EINVAL;
- }
-
- if (!c->authenticated && authenticated) {
- ubifs_err(c, "authenticated FS found, but no key given");
- return -EINVAL;
- }
-
- ubifs_msg(c, "Mounting in %sauthenticated mode",
- c->authenticated ? "" : "un");
-
- if (!c->authenticated)
- return 0;
- if (!IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION))
+ if (authenticated) {
+ // To be implemented
+ ubifs_err(c, "not support authentication");
return -EOPNOTSUPP;
-
- hash_algo = le16_to_cpu(sup->hash_algo);
- if (hash_algo >= HASH_ALGO__LAST) {
- ubifs_err(c, "superblock uses unknown hash algo %d",
- hash_algo);
- return -EINVAL;
- }
-
- if (strcmp(hash_algo_name[hash_algo], c->auth_hash_name)) {
- ubifs_err(c, "This filesystem uses %s for hashing,"
- " but %s is specified", hash_algo_name[hash_algo],
- c->auth_hash_name);
- return -EINVAL;
}
- /*
- * The super block node can either be authenticated by a HMAC or
- * by a signature in a ubifs_sig_node directly following the
- * super block node to support offline image creation.
- */
- if (ubifs_hmac_zero(c, sup->hmac)) {
- err = ubifs_sb_verify_signature(c, sup);
- } else {
- err = ubifs_hmac_wkm(c, hmac_wkm);
- if (err)
- return err;
- if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
- ubifs_err(c, "provided key does not fit");
- return -ENOKEY;
- }
- err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
- offsetof(struct ubifs_sb_node,
- hmac));
- }
-
- if (err)
- ubifs_err(c, "Failed to authenticate superblock: %d", err);
-
- return err;
+ return 0;
}
/**
@@ -638,12 +245,6 @@ int ubifs_read_superblock(struct ubifs_info *c)
int err, sup_flags;
struct ubifs_sb_node *sup;
- if (c->empty) {
- err = create_default_filesystem(c);
- if (err)
- return err;
- }
-
sup = ubifs_read_sb_node(c);
if (IS_ERR(sup))
return PTR_ERR(sup);
@@ -672,13 +273,6 @@ int ubifs_read_superblock(struct ubifs_info *c)
err = -EINVAL;
goto out;
}
-
- /*
- * The FS is mounted R/O, and the media format is
- * R/O-compatible with the UBIFS implementation, so we can
- * mount.
- */
- c->rw_incompat = 1;
}
if (c->fmt_version < 3) {
@@ -722,14 +316,9 @@ int ubifs_read_superblock(struct ubifs_info *c)
c->fanout = le32_to_cpu(sup->fanout);
c->lsave_cnt = le32_to_cpu(sup->lsave_cnt);
c->rp_size = le64_to_cpu(sup->rp_size);
- c->rp_uid = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid));
- c->rp_gid = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid));
sup_flags = le32_to_cpu(sup->flags);
- if (!c->mount_opts.override_compr)
- c->default_compr = le16_to_cpu(sup->default_compr);
+ c->default_compr = le16_to_cpu(sup->default_compr);
- c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
- memcpy(&c->uuid, &sup->uuid, 16);
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
@@ -746,18 +335,11 @@ int ubifs_read_superblock(struct ubifs_info *c)
goto out;
}
- if (!IS_ENABLED(CONFIG_FS_ENCRYPTION) && c->encrypted) {
- ubifs_err(c, "file system contains encrypted files but UBIFS"
- " was built without crypto support.");
- err = -EINVAL;
- goto out;
- }
-
/* Automatically increase file system size to the maximum size */
- if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
+ if (c->leb_cnt < c->vi.rsvd_lebs && c->leb_cnt < c->max_leb_cnt) {
int old_leb_cnt = c->leb_cnt;
- c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
+ c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.rsvd_lebs);
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
c->superblock_need_write = 1;
@@ -807,7 +389,7 @@ static int fixup_leb(struct ubifs_info *c, int lnum, int len)
dbg_mnt("fixup LEB %d, data len %d", lnum, len);
err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 1);
- if (err)
+ if (err && err != -EBADMSG)
return err;
return ubifs_leb_change(c, lnum, c->sbuf, len);
@@ -926,31 +508,3 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
ubifs_msg(c, "free space fixup complete");
return err;
}
-
-int ubifs_enable_encryption(struct ubifs_info *c)
-{
- int err;
- struct ubifs_sb_node *sup = c->sup_node;
-
- if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
- return -EOPNOTSUPP;
-
- if (c->encrypted)
- return 0;
-
- if (c->ro_mount || c->ro_media)
- return -EROFS;
-
- if (c->fmt_version < 5) {
- ubifs_err(c, "on-flash format version 5 is needed for encryption");
- return -EINVAL;
- }
-
- sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
-
- err = ubifs_write_sb_node(c, sup);
- if (!err)
- c->encrypted = 1;
-
- return err;
-}
--
2.13.6
Add spinlock implementations, because there are some spinlocks
(eg. c->cnt_lock, c->buds_lock) used in UBIFS linux kernel libs.
The spinlock is implementated based on pthread mutex.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 ++-
ubifs-utils/common/spinlock.h | 14 ++++++++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 ubifs-utils/common/spinlock.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 9e075071..7849114e 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -5,6 +5,7 @@ common_SOURCES = \
ubifs-utils/common/atomic.h \
ubifs-utils/common/bitops.h \
ubifs-utils/common/bitops.c \
+ ubifs-utils/common/spinlock.h \
ubifs-utils/common/kmem.h \
ubifs-utils/common/kmem.c \
ubifs-utils/common/defs.h \
@@ -39,7 +40,7 @@ mkfs_ubifs_SOURCES = \
$(common_SOURCES) \
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
-mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm
+mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
-I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common
diff --git a/ubifs-utils/common/spinlock.h b/ubifs-utils/common/spinlock.h
new file mode 100644
index 00000000..b9ed3938
--- /dev/null
+++ b/ubifs-utils/common/spinlock.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_SPINLOCK_H_
+#define __LINUX_SPINLOCK_H_
+
+#include <pthread.h>
+
+#define spinlock_t pthread_mutex_t
+#define DEFINE_SPINLOCK(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER
+#define spin_lock_init(x) pthread_mutex_init(x, NULL)
+
+#define spin_lock(x) pthread_mutex_lock(x)
+#define spin_unlock(x) pthread_mutex_unlock(x)
+
+#endif
--
2.13.6
The type of xattr entry should be regular file, otherwise xattr entry
and xattr inode have inconsistent types.
Fixes: 50044efbd6e713 ("mkfs.ubifs: Add extended attribute support")
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 51852861..8bf073ce 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -1255,7 +1255,7 @@ static int add_xattr(struct ubifs_ino_node *host_ino, struct stat *st,
xent->ch.len = len;
xent->padding1 = 0;
- xent->type = UBIFS_ITYPE_DIR;
+ xent->type = UBIFS_ITYPE_REG;
xent->nlen = cpu_to_le16(nm.len);
memcpy(xent->name, nm.name, nm.len + 1);
--
2.13.6
Adapt lpt subsystem(lpt.c,lprops.c,lpt_commit.c) in libubifs, compared
with linux kernel implementations:
1. Remove debug related functions(eg. dbg_chk_lpt_sz, dbg_chk_pnode),
some of debug functions are not needed by fsck, because fsck will
check fs in another way.
2. Remove some functions(eg. ubifs_create_dflt_lpt) which won't be used
in fsck/mkfs.
3. Adapt do_calc_lpt_geom() to mkfs/fsck situations.
4. Adapt calc_dflt_lpt_geom to support the mkfs tool, and export it.
5. Adapt ubifs_create_lpt() according to create_lpt(mkfs), make sure that
the height of created lpt is right.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/lprops.c | 484 +-------------------------------------
ubifs-utils/libubifs/lpt.c | 381 +++++++++---------------------
ubifs-utils/libubifs/lpt_commit.c | 235 ++----------------
3 files changed, 136 insertions(+), 964 deletions(-)
diff --git a/ubifs-utils/libubifs/lprops.c b/ubifs-utils/libubifs/lprops.c
index 6d6cd85c..84cdb353 100644
--- a/ubifs-utils/libubifs/lprops.c
+++ b/ubifs-utils/libubifs/lprops.c
@@ -16,7 +16,13 @@
* an empty LEB for the journal, or a very dirty LEB for garbage collection.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
/**
* get_heap_comp_val - get the LEB properties value for heap comparisons.
@@ -47,7 +53,8 @@ static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
* is either the amount of free space or the amount of dirty space, depending
* on the category.
*/
-static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+static void move_up_lpt_heap(__unused struct ubifs_info *c,
+ struct ubifs_lpt_heap *heap,
struct ubifs_lprops *lprops, int cat)
{
int val1, val2, hpos;
@@ -84,7 +91,8 @@ static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
* greater. In the case of LPT's category heaps, the value is either the amount
* of free space or the amount of dirty space, depending on the category.
*/
-static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+static void adjust_lpt_heap(__unused struct ubifs_info *c,
+ struct ubifs_lpt_heap *heap,
struct ubifs_lprops *lprops, int hpos, int cat)
{
int val1, val2, val3, cpos;
@@ -191,16 +199,13 @@ static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
lprops->hpos = cpos;
heap->arr[cpos] = lprops;
move_up_lpt_heap(c, heap, lprops, cat);
- dbg_check_heap(c, heap, cat, lprops->hpos);
return 1; /* Added to heap */
}
- dbg_check_heap(c, heap, cat, -1);
return 0; /* Not added to heap */
} else {
lprops->hpos = heap->cnt++;
heap->arr[lprops->hpos] = lprops;
move_up_lpt_heap(c, heap, lprops, cat);
- dbg_check_heap(c, heap, cat, lprops->hpos);
return 1; /* Added to heap */
}
}
@@ -226,7 +231,6 @@ static void remove_from_lpt_heap(struct ubifs_info *c,
heap->arr[hpos]->hpos = hpos;
adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
}
- dbg_check_heap(c, heap, cat, -1);
}
/**
@@ -837,471 +841,3 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
return lprops;
}
-
-/*
- * Everything below is related to debugging.
- */
-
-/**
- * dbg_check_cats - check category heaps and lists.
- * @c: UBIFS file-system description object
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_check_cats(struct ubifs_info *c)
-{
- struct ubifs_lprops *lprops;
- struct list_head *pos;
- int i, cat;
-
- if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
- return 0;
-
- list_for_each_entry(lprops, &c->empty_list, list) {
- if (lprops->free != c->leb_size) {
- ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- if (lprops->flags & LPROPS_TAKEN) {
- ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- }
-
- i = 0;
- list_for_each_entry(lprops, &c->freeable_list, list) {
- if (lprops->free + lprops->dirty != c->leb_size) {
- ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- if (lprops->flags & LPROPS_TAKEN) {
- ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- i += 1;
- }
- if (i != c->freeable_cnt) {
- ubifs_err(c, "freeable list count %d expected %d", i,
- c->freeable_cnt);
- return -EINVAL;
- }
-
- i = 0;
- list_for_each(pos, &c->idx_gc)
- i += 1;
- if (i != c->idx_gc_cnt) {
- ubifs_err(c, "idx_gc list count %d expected %d", i,
- c->idx_gc_cnt);
- return -EINVAL;
- }
-
- list_for_each_entry(lprops, &c->frdi_idx_list, list) {
- if (lprops->free + lprops->dirty != c->leb_size) {
- ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- if (lprops->flags & LPROPS_TAKEN) {
- ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- if (!(lprops->flags & LPROPS_INDEX)) {
- ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
- lprops->lnum, lprops->free, lprops->dirty,
- lprops->flags);
- return -EINVAL;
- }
- }
-
- for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
- struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
-
- for (i = 0; i < heap->cnt; i++) {
- lprops = heap->arr[i];
- if (!lprops) {
- ubifs_err(c, "null ptr in LPT heap cat %d", cat);
- return -EINVAL;
- }
- if (lprops->hpos != i) {
- ubifs_err(c, "bad ptr in LPT heap cat %d", cat);
- return -EINVAL;
- }
- if (lprops->flags & LPROPS_TAKEN) {
- ubifs_err(c, "taken LEB in LPT heap cat %d", cat);
- return -EINVAL;
- }
- }
- }
-
- return 0;
-}
-
-void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
- int add_pos)
-{
- int i = 0, j, err = 0;
-
- if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
- return;
-
- for (i = 0; i < heap->cnt; i++) {
- struct ubifs_lprops *lprops = heap->arr[i];
- struct ubifs_lprops *lp;
-
- if (i != add_pos)
- if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
- err = 1;
- goto out;
- }
- if (lprops->hpos != i) {
- err = 2;
- goto out;
- }
- lp = ubifs_lpt_lookup(c, lprops->lnum);
- if (IS_ERR(lp)) {
- err = 3;
- goto out;
- }
- if (lprops != lp) {
- ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
- (size_t)lprops, (size_t)lp, lprops->lnum,
- lp->lnum);
- err = 4;
- goto out;
- }
- for (j = 0; j < i; j++) {
- lp = heap->arr[j];
- if (lp == lprops) {
- err = 5;
- goto out;
- }
- if (lp->lnum == lprops->lnum) {
- err = 6;
- goto out;
- }
- }
- }
-out:
- if (err) {
- ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err);
- dump_stack();
- ubifs_dump_heap(c, heap, cat);
- }
-}
-
-/**
- * scan_check_cb - scan callback.
- * @c: the UBIFS file-system description object
- * @lp: LEB properties to scan
- * @in_tree: whether the LEB properties are in main memory
- * @lst: lprops statistics to update
- *
- * This function returns a code that indicates whether the scan should continue
- * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
- * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
- * (%LPT_SCAN_STOP).
- */
-static int scan_check_cb(struct ubifs_info *c,
- const struct ubifs_lprops *lp, int in_tree,
- struct ubifs_lp_stats *lst)
-{
- struct ubifs_scan_leb *sleb;
- struct ubifs_scan_node *snod;
- int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
- void *buf = NULL;
-
- cat = lp->flags & LPROPS_CAT_MASK;
- if (cat != LPROPS_UNCAT) {
- cat = ubifs_categorize_lprops(c, lp);
- if (cat != (lp->flags & LPROPS_CAT_MASK)) {
- ubifs_err(c, "bad LEB category %d expected %d",
- (lp->flags & LPROPS_CAT_MASK), cat);
- return -EINVAL;
- }
- }
-
- /* Check lp is on its category list (if it has one) */
- if (in_tree) {
- struct list_head *list = NULL;
-
- switch (cat) {
- case LPROPS_EMPTY:
- list = &c->empty_list;
- break;
- case LPROPS_FREEABLE:
- list = &c->freeable_list;
- break;
- case LPROPS_FRDI_IDX:
- list = &c->frdi_idx_list;
- break;
- case LPROPS_UNCAT:
- list = &c->uncat_list;
- break;
- }
- if (list) {
- struct ubifs_lprops *lprops;
- int found = 0;
-
- list_for_each_entry(lprops, list, list) {
- if (lprops == lp) {
- found = 1;
- break;
- }
- }
- if (!found) {
- ubifs_err(c, "bad LPT list (category %d)", cat);
- return -EINVAL;
- }
- }
- }
-
- /* Check lp is on its category heap (if it has one) */
- if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
- struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
-
- if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
- lp != heap->arr[lp->hpos]) {
- ubifs_err(c, "bad LPT heap (category %d)", cat);
- return -EINVAL;
- }
- }
-
- /*
- * After an unclean unmount, empty and freeable LEBs
- * may contain garbage - do not scan them.
- */
- if (lp->free == c->leb_size) {
- lst->empty_lebs += 1;
- lst->total_free += c->leb_size;
- lst->total_dark += ubifs_calc_dark(c, c->leb_size);
- return LPT_SCAN_CONTINUE;
- }
- if (lp->free + lp->dirty == c->leb_size &&
- !(lp->flags & LPROPS_INDEX)) {
- lst->total_free += lp->free;
- lst->total_dirty += lp->dirty;
- lst->total_dark += ubifs_calc_dark(c, c->leb_size);
- return LPT_SCAN_CONTINUE;
- }
-
- buf = __vmalloc(c->leb_size, GFP_NOFS);
- if (!buf)
- return -ENOMEM;
-
- sleb = ubifs_scan(c, lnum, 0, buf, 0);
- if (IS_ERR(sleb)) {
- ret = PTR_ERR(sleb);
- if (ret == -EUCLEAN) {
- ubifs_dump_lprops(c);
- ubifs_dump_budg(c, &c->bi);
- }
- goto out;
- }
-
- is_idx = -1;
- list_for_each_entry(snod, &sleb->nodes, list) {
- int found, level = 0;
-
- cond_resched();
-
- if (is_idx == -1)
- is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
-
- if (is_idx && snod->type != UBIFS_IDX_NODE) {
- ubifs_err(c, "indexing node in data LEB %d:%d",
- lnum, snod->offs);
- goto out_destroy;
- }
-
- if (snod->type == UBIFS_IDX_NODE) {
- struct ubifs_idx_node *idx = snod->node;
-
- key_read(c, ubifs_idx_key(c, idx), &snod->key);
- level = le16_to_cpu(idx->level);
- }
-
- found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
- snod->offs, is_idx);
- if (found) {
- if (found < 0)
- goto out_destroy;
- used += ALIGN(snod->len, 8);
- }
- }
-
- free = c->leb_size - sleb->endpt;
- dirty = sleb->endpt - used;
-
- if (free > c->leb_size || free < 0 || dirty > c->leb_size ||
- dirty < 0) {
- ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d",
- lnum, free, dirty);
- goto out_destroy;
- }
-
- if (lp->free + lp->dirty == c->leb_size &&
- free + dirty == c->leb_size)
- if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
- (!is_idx && free == c->leb_size) ||
- lp->free == c->leb_size) {
- /*
- * Empty or freeable LEBs could contain index
- * nodes from an uncompleted commit due to an
- * unclean unmount. Or they could be empty for
- * the same reason. Or it may simply not have been
- * unmapped.
- */
- free = lp->free;
- dirty = lp->dirty;
- is_idx = 0;
- }
-
- if (is_idx && lp->free + lp->dirty == free + dirty &&
- lnum != c->ihead_lnum) {
- /*
- * After an unclean unmount, an index LEB could have a different
- * amount of free space than the value recorded by lprops. That
- * is because the in-the-gaps method may use free space or
- * create free space (as a side-effect of using ubi_leb_change
- * and not writing the whole LEB). The incorrect free space
- * value is not a problem because the index is only ever
- * allocated empty LEBs, so there will never be an attempt to
- * write to the free space at the end of an index LEB - except
- * by the in-the-gaps method for which it is not a problem.
- */
- free = lp->free;
- dirty = lp->dirty;
- }
-
- if (lp->free != free || lp->dirty != dirty)
- goto out_print;
-
- if (is_idx && !(lp->flags & LPROPS_INDEX)) {
- if (free == c->leb_size)
- /* Free but not unmapped LEB, it's fine */
- is_idx = 0;
- else {
- ubifs_err(c, "indexing node without indexing flag");
- goto out_print;
- }
- }
-
- if (!is_idx && (lp->flags & LPROPS_INDEX)) {
- ubifs_err(c, "data node with indexing flag");
- goto out_print;
- }
-
- if (free == c->leb_size)
- lst->empty_lebs += 1;
-
- if (is_idx)
- lst->idx_lebs += 1;
-
- if (!(lp->flags & LPROPS_INDEX))
- lst->total_used += c->leb_size - free - dirty;
- lst->total_free += free;
- lst->total_dirty += dirty;
-
- if (!(lp->flags & LPROPS_INDEX)) {
- int spc = free + dirty;
-
- if (spc < c->dead_wm)
- lst->total_dead += spc;
- else
- lst->total_dark += ubifs_calc_dark(c, spc);
- }
-
- ubifs_scan_destroy(sleb);
- vfree(buf);
- return LPT_SCAN_CONTINUE;
-
-out_print:
- ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
- lnum, lp->free, lp->dirty, lp->flags, free, dirty);
- ubifs_dump_leb(c, lnum);
-out_destroy:
- ubifs_scan_destroy(sleb);
- ret = -EINVAL;
-out:
- vfree(buf);
- return ret;
-}
-
-/**
- * dbg_check_lprops - check all LEB properties.
- * @c: UBIFS file-system description object
- *
- * This function checks all LEB properties and makes sure they are all correct.
- * It returns zero if everything is fine, %-EINVAL if there is an inconsistency
- * and other negative error codes in case of other errors. This function is
- * called while the file system is locked (because of commit start), so no
- * additional locking is required. Note that locking the LPT mutex would cause
- * a circular lock dependency with the TNC mutex.
- */
-int dbg_check_lprops(struct ubifs_info *c)
-{
- int i, err;
- struct ubifs_lp_stats lst;
-
- if (!dbg_is_chk_lprops(c))
- return 0;
-
- /*
- * As we are going to scan the media, the write buffers have to be
- * synchronized.
- */
- for (i = 0; i < c->jhead_cnt; i++) {
- err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
- if (err)
- return err;
- }
-
- memset(&lst, 0, sizeof(struct ubifs_lp_stats));
- err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
- (ubifs_lpt_scan_callback)scan_check_cb,
- &lst);
- if (err && err != -ENOSPC)
- goto out;
-
- if (lst.empty_lebs != c->lst.empty_lebs ||
- lst.idx_lebs != c->lst.idx_lebs ||
- lst.total_free != c->lst.total_free ||
- lst.total_dirty != c->lst.total_dirty ||
- lst.total_used != c->lst.total_used) {
- ubifs_err(c, "bad overall accounting");
- ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
- lst.empty_lebs, lst.idx_lebs, lst.total_free,
- lst.total_dirty, lst.total_used);
- ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
- c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
- c->lst.total_dirty, c->lst.total_used);
- err = -EINVAL;
- goto out;
- }
-
- if (lst.total_dead != c->lst.total_dead ||
- lst.total_dark != c->lst.total_dark) {
- ubifs_err(c, "bad dead/dark space accounting");
- ubifs_err(c, "calculated: total_dead %lld, total_dark %lld",
- lst.total_dead, lst.total_dark);
- ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld",
- c->lst.total_dead, c->lst.total_dark);
- err = -EINVAL;
- goto out;
- }
-
- err = dbg_check_cats(c);
-out:
- return err;
-}
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index 1889170b..92b3fec8 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -31,10 +31,13 @@
* mounted.
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "crc16.h"
#include "ubifs.h"
-#include <linux/crc16.h>
-#include <linux/math64.h>
-#include <linux/slab.h>
+#include "defs.h"
+#include "debug.h"
/**
* do_calc_lpt_geom - calculate sizes for the LPT area.
@@ -48,8 +51,20 @@ static void do_calc_lpt_geom(struct ubifs_info *c)
int i, n, bits, per_leb_wastage, max_pnode_cnt;
long long sz, tot_wastage;
- n = c->main_lebs + c->max_leb_cnt - c->leb_cnt;
- max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+ if (c->program_type != MKFS_PROGRAM_TYPE) {
+ n = c->main_lebs + c->max_leb_cnt - c->leb_cnt;
+ max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+ } else {
+ /*
+ * Different from linux kernel.
+ *
+ * We change it, because 'c->leb_cnt' is not initialized in
+ * mkfs.ubifs when do_calc_lpt_geom() is invoked, 'c->main_lebs'
+ * is calculated by 'c->max_leb_cnt', so the 'c->lpt_hght'
+ * should be calculated by 'c->main_lebs'.
+ */
+ max_pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ }
c->lpt_hght = 1;
n = UBIFS_LPT_FANOUT;
@@ -148,7 +163,7 @@ int ubifs_calc_lpt_geom(struct ubifs_info *c)
}
/**
- * calc_dflt_lpt_geom - calculate default LPT geometry.
+ * ubifs_calc_dflt_lpt_geom - calculate default LPT geometry.
* @c: the UBIFS file-system description object
* @main_lebs: number of main area LEBs is passed and returned here
* @big_lpt: whether the LPT area is "big" is returned here
@@ -159,8 +174,7 @@ int ubifs_calc_lpt_geom(struct ubifs_info *c)
*
* This function returns %0 on success and a negative error code on failure.
*/
-static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs,
- int *big_lpt)
+int ubifs_calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt)
{
int i, lebs_needed;
long long sz;
@@ -275,7 +289,7 @@ uint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos,
const int k = 32 - nrbits;
uint8_t *p = *addr;
int b = *pos;
- uint32_t val;
+ uint32_t val = 0;
const int bytes = (nrbits + b + 7) >> 3;
ubifs_assert(c, nrbits > 0);
@@ -638,8 +652,13 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
lnum = c->lpt_first;
p = buf;
len = 0;
- /* Number of leaf nodes (pnodes) */
- cnt = c->pnode_cnt;
+ /*
+ * Different from linux kernel. The number of leaf nodes (pnodes) should
+ * be calculated by the number of current main LEBs. The 'c->pnode_cnt'
+ * may not be equal to 'DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT)' in
+ * mkfs when 'c->leb_cnt != c->max_leb_cnt' is true.
+ */
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
/*
* To calculate the internal node branches, we keep information about
@@ -655,8 +674,19 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
if (len + c->pnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = ubifs_leb_change(c, lnum++, buf, alen);
+ /*
+ * Different from linux kernel.
+ * The mkfs may partially write data into a certain LEB
+ * of file image, the left unwritten area in the LEB
+ * should be filled with '0xFF'.
+ */
+ if (c->libubi) {
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ } else {
+ memset(p, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, lnum++, buf, c->leb_size);
+ }
if (err)
goto out;
p = buf;
@@ -691,27 +721,45 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
pnode->num += 1;
}
- row = 0;
- for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT)
- row += 1;
+ /*
+ * Different from linux kernel. The 'c->lpt_hght' is calculated by the
+ * 'c->max_leb_cnt', according to the implementation of function
+ * ubifs_pnode_lookup(), there are at least 'c->lpt_hght' cnodes should
+ * be created, otherwise the LPT looking up could be failed after
+ * mouting.
+ */
+ row = c->lpt_hght - 1;
/* Add all nnodes, one level at a time */
while (1) {
/* Number of internal nodes (nnodes) at next level */
cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT);
+ if (cnt == 0)
+ cnt = 1;
for (i = 0; i < cnt; i++) {
if (len + c->nnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen,
alen - len);
- memset(p, 0xff, alen - len);
- err = ubifs_leb_change(c, lnum++, buf, alen);
+ /*
+ * Different from linux kernel.
+ * The mkfs may partially write data into a certain LEB
+ * of file image, the left unwritten area in the LEB
+ * should be filled with '0xFF'.
+ */
+ if (c->libubi) {
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ } else {
+ memset(p, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, lnum++, buf, c->leb_size);
+ }
if (err)
goto out;
p = buf;
len = 0;
}
/* Only 1 nnode at this level, so it is the root */
- if (cnt == 1) {
+ if (row == 0) {
c->lpt_lnum = lnum;
c->lpt_offs = len;
}
@@ -736,8 +784,8 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
p += c->nnode_sz;
len += c->nnode_sz;
}
- /* Only 1 nnode at this level, so it is the root */
- if (cnt == 1)
+ /* Row zero is the top row */
+ if (row == 0)
break;
/* Update the information about the level below */
bcnt = cnt;
@@ -750,8 +798,19 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
if (len + c->lsave_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = ubifs_leb_change(c, lnum++, buf, alen);
+ /*
+ * Different from linux kernel.
+ * The mkfs may partially write data into a certain LEB
+ * of file image, the left unwritten area in the LEB
+ * should be filled with '0xFF'.
+ */
+ if (c->libubi) {
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ } else {
+ memset(p, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, lnum++, buf, c->leb_size);
+ }
if (err)
goto out;
p = buf;
@@ -775,8 +834,19 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
if (len + c->ltab_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = ubifs_leb_change(c, lnum++, buf, alen);
+ /*
+ * Different from linux kernel.
+ * The mkfs may partially write data into a certain LEB
+ * of file image, the left unwritten area in the LEB
+ * should be filled with '0xFF'.
+ */
+ if (c->libubi) {
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ } else {
+ memset(p, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, lnum++, buf, c->leb_size);
+ }
if (err)
goto out;
p = buf;
@@ -795,11 +865,25 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
p += c->ltab_sz;
/* Write remaining buffer */
- memset(p, 0xff, alen - len);
- err = ubifs_leb_change(c, lnum, buf, alen);
+ /*
+ * Different from linux kernel.
+ * The mkfs may partially write data into a certain LEB
+ * of file image, the left unwritten area in the LEB
+ * should be filled with '0xFF'.
+ */
+ if (c->libubi) {
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum, buf, alen);
+ } else {
+ memset(p, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, lnum, buf, c->leb_size);
+ }
if (err)
goto out;
+ if (c->big_lpt && c->lsave)
+ memcpy(c->lsave, lsave, c->lsave_cnt * sizeof(int));
+
err = ubifs_shash_final(c, desc, hash);
if (err)
goto out;
@@ -837,54 +921,6 @@ out:
}
/**
- * ubifs_create_dflt_lpt - create default LPT.
- * @c: UBIFS file-system description object
- * @main_lebs: number of main area LEBs is passed and returned here
- * @lpt_first: LEB number of first LPT LEB
- * @lpt_lebs: number of LEBs for LPT is passed and returned here
- * @big_lpt: use big LPT model is passed and returned here
- * @hash: hash of the LPT is returned here
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
- int *lpt_lebs, int *big_lpt, u8 *hash)
-{
- int node_sz, iopos, err = 0;
- struct ubifs_lprops lps[2];
-
- err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
- if (err)
- return err;
- *lpt_lebs = c->lpt_lebs;
-
- /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */
- c->lpt_first = lpt_first;
- /* Needed by 'set_ltab()' */
- c->lpt_last = lpt_first + c->lpt_lebs - 1;
- /* Needed by 'ubifs_pack_lsave()' */
- c->main_first = c->leb_cnt - *main_lebs;
-
- /*
- * The first pnode contains the LEB properties for the LEBs that contain
- * the root inode node and the root index node of the index tree.
- */
- node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8);
- iopos = ALIGN(node_sz, c->min_io_size);
- lps[0].free = c->leb_size - iopos;
- lps[0].dirty = iopos - node_sz;
- lps[0].flags = LPROPS_INDEX;
-
- node_sz = UBIFS_INO_NODE_SZ;
- iopos = ALIGN(node_sz, c->min_io_size);
- lps[1].free = c->leb_size - iopos;
- lps[1].dirty = iopos - node_sz;
- lps[1].flags = 0;
-
- return ubifs_create_lpt(c, lps, 2, hash);
-}
-
-/**
* update_cats - add LEB properties of a pnode to LEB category lists and heaps.
* @c: UBIFS file-system description object
* @pnode: pnode
@@ -2254,198 +2290,3 @@ out:
kfree(path);
return err;
}
-
-/**
- * dbg_chk_pnode - check a pnode.
- * @c: the UBIFS file-system description object
- * @pnode: pnode to check
- * @col: pnode column
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
- int col)
-{
- int i;
-
- if (pnode->num != col) {
- ubifs_err(c, "pnode num %d expected %d parent num %d iip %d",
- pnode->num, col, pnode->parent->num, pnode->iip);
- return -EINVAL;
- }
- for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
- struct ubifs_lprops *lp, *lprops = &pnode->lprops[i];
- int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i +
- c->main_first;
- int found, cat = lprops->flags & LPROPS_CAT_MASK;
- struct ubifs_lpt_heap *heap;
- struct list_head *list = NULL;
-
- if (lnum >= c->leb_cnt)
- continue;
- if (lprops->lnum != lnum) {
- ubifs_err(c, "bad LEB number %d expected %d",
- lprops->lnum, lnum);
- return -EINVAL;
- }
- if (lprops->flags & LPROPS_TAKEN) {
- if (cat != LPROPS_UNCAT) {
- ubifs_err(c, "LEB %d taken but not uncat %d",
- lprops->lnum, cat);
- return -EINVAL;
- }
- continue;
- }
- if (lprops->flags & LPROPS_INDEX) {
- switch (cat) {
- case LPROPS_UNCAT:
- case LPROPS_DIRTY_IDX:
- case LPROPS_FRDI_IDX:
- break;
- default:
- ubifs_err(c, "LEB %d index but cat %d",
- lprops->lnum, cat);
- return -EINVAL;
- }
- } else {
- switch (cat) {
- case LPROPS_UNCAT:
- case LPROPS_DIRTY:
- case LPROPS_FREE:
- case LPROPS_EMPTY:
- case LPROPS_FREEABLE:
- break;
- default:
- ubifs_err(c, "LEB %d not index but cat %d",
- lprops->lnum, cat);
- return -EINVAL;
- }
- }
- switch (cat) {
- case LPROPS_UNCAT:
- list = &c->uncat_list;
- break;
- case LPROPS_EMPTY:
- list = &c->empty_list;
- break;
- case LPROPS_FREEABLE:
- list = &c->freeable_list;
- break;
- case LPROPS_FRDI_IDX:
- list = &c->frdi_idx_list;
- break;
- }
- found = 0;
- switch (cat) {
- case LPROPS_DIRTY:
- case LPROPS_DIRTY_IDX:
- case LPROPS_FREE:
- heap = &c->lpt_heap[cat - 1];
- if (lprops->hpos < heap->cnt &&
- heap->arr[lprops->hpos] == lprops)
- found = 1;
- break;
- case LPROPS_UNCAT:
- case LPROPS_EMPTY:
- case LPROPS_FREEABLE:
- case LPROPS_FRDI_IDX:
- list_for_each_entry(lp, list, list)
- if (lprops == lp) {
- found = 1;
- break;
- }
- break;
- }
- if (!found) {
- ubifs_err(c, "LEB %d cat %d not found in cat heap/list",
- lprops->lnum, cat);
- return -EINVAL;
- }
- switch (cat) {
- case LPROPS_EMPTY:
- if (lprops->free != c->leb_size) {
- ubifs_err(c, "LEB %d cat %d free %d dirty %d",
- lprops->lnum, cat, lprops->free,
- lprops->dirty);
- return -EINVAL;
- }
- break;
- case LPROPS_FREEABLE:
- case LPROPS_FRDI_IDX:
- if (lprops->free + lprops->dirty != c->leb_size) {
- ubifs_err(c, "LEB %d cat %d free %d dirty %d",
- lprops->lnum, cat, lprops->free,
- lprops->dirty);
- return -EINVAL;
- }
- break;
- }
- }
- return 0;
-}
-
-/**
- * dbg_check_lpt_nodes - check nnodes and pnodes.
- * @c: the UBIFS file-system description object
- * @cnode: next cnode (nnode or pnode) to check
- * @row: row of cnode (root is zero)
- * @col: column of cnode (leftmost is zero)
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
- int row, int col)
-{
- struct ubifs_nnode *nnode, *nn;
- struct ubifs_cnode *cn;
- int num, iip = 0, err;
-
- if (!dbg_is_chk_lprops(c))
- return 0;
-
- while (cnode) {
- ubifs_assert(c, row >= 0);
- nnode = cnode->parent;
- if (cnode->level) {
- /* cnode is a nnode */
- num = calc_nnode_num(row, col);
- if (cnode->num != num) {
- ubifs_err(c, "nnode num %d expected %d parent num %d iip %d",
- cnode->num, num,
- (nnode ? nnode->num : 0), cnode->iip);
- return -EINVAL;
- }
- nn = (struct ubifs_nnode *)cnode;
- while (iip < UBIFS_LPT_FANOUT) {
- cn = nn->nbranch[iip].cnode;
- if (cn) {
- /* Go down */
- row += 1;
- col <<= UBIFS_LPT_FANOUT_SHIFT;
- col += iip;
- iip = 0;
- cnode = cn;
- break;
- }
- /* Go right */
- iip += 1;
- }
- if (iip < UBIFS_LPT_FANOUT)
- continue;
- } else {
- struct ubifs_pnode *pnode;
-
- /* cnode is a pnode */
- pnode = (struct ubifs_pnode *)cnode;
- err = dbg_chk_pnode(c, pnode, col);
- if (err)
- return err;
- }
- /* Go up and to the right */
- row -= 1;
- col >>= UBIFS_LPT_FANOUT_SHIFT;
- iip = cnode->iip + 1;
- cnode = (struct ubifs_cnode *)nnode;
- }
- return 0;
-}
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index c4d07932..b00f75f0 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -13,10 +13,14 @@
* subsystem.
*/
-#include <linux/crc16.h>
-#include <linux/slab.h>
-#include <linux/random.h>
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "crc16.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
static int dbg_populate_lsave(struct ubifs_info *c);
@@ -1030,7 +1034,8 @@ static int get_lpt_node_len(const struct ubifs_info *c, int node_type)
* @buf: buffer
* @len: length of buffer
*/
-static int get_pad_len(const struct ubifs_info *c, uint8_t *buf, int len)
+static int get_pad_len(const struct ubifs_info *c, __unused uint8_t *buf,
+ int len)
{
int offs, pad_len;
@@ -1593,9 +1598,6 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
int ret;
void *buf, *p;
- if (!dbg_is_chk_lprops(c))
- return 0;
-
buf = p = __vmalloc(c->leb_size, GFP_NOFS);
if (!buf) {
ubifs_err(c, "cannot allocate memory for ltab checking");
@@ -1646,190 +1648,12 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
len -= node_len;
}
- err = 0;
out:
vfree(buf);
return err;
}
/**
- * dbg_check_ltab - check the free and dirty space in the ltab.
- * @c: the UBIFS file-system description object
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_check_ltab(struct ubifs_info *c)
-{
- int lnum, err, i, cnt;
-
- if (!dbg_is_chk_lprops(c))
- return 0;
-
- /* Bring the entire tree into memory */
- cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
- for (i = 0; i < cnt; i++) {
- struct ubifs_pnode *pnode;
-
- pnode = ubifs_pnode_lookup(c, i);
- if (IS_ERR(pnode))
- return PTR_ERR(pnode);
- cond_resched();
- }
-
- /* Check nodes */
- err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)c->nroot, 0, 0);
- if (err)
- return err;
-
- /* Check each LEB */
- for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
- err = dbg_check_ltab_lnum(c, lnum);
- if (err) {
- ubifs_err(c, "failed at LEB %d", lnum);
- return err;
- }
- }
-
- dbg_lp("succeeded");
- return 0;
-}
-
-/**
- * dbg_chk_lpt_free_spc - check LPT free space is enough to write entire LPT.
- * @c: the UBIFS file-system description object
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int dbg_chk_lpt_free_spc(struct ubifs_info *c)
-{
- long long free = 0;
- int i;
-
- if (!dbg_is_chk_lprops(c))
- return 0;
-
- for (i = 0; i < c->lpt_lebs; i++) {
- if (c->ltab[i].tgc || c->ltab[i].cmt)
- continue;
- if (i + c->lpt_first == c->nhead_lnum)
- free += c->leb_size - c->nhead_offs;
- else if (c->ltab[i].free == c->leb_size)
- free += c->leb_size;
- }
- if (free < c->lpt_sz) {
- ubifs_err(c, "LPT space error: free %lld lpt_sz %lld",
- free, c->lpt_sz);
- ubifs_dump_lpt_info(c);
- ubifs_dump_lpt_lebs(c);
- dump_stack();
- return -EINVAL;
- }
- return 0;
-}
-
-/**
- * dbg_chk_lpt_sz - check LPT does not write more than LPT size.
- * @c: the UBIFS file-system description object
- * @action: what to do
- * @len: length written
- *
- * This function returns %0 on success and a negative error code on failure.
- * The @action argument may be one of:
- * o %0 - LPT debugging checking starts, initialize debugging variables;
- * o %1 - wrote an LPT node, increase LPT size by @len bytes;
- * o %2 - switched to a different LEB and wasted @len bytes;
- * o %3 - check that we've written the right number of bytes.
- * o %4 - wasted @len bytes;
- */
-int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len)
-{
- struct ubifs_debug_info *d = c->dbg;
- long long chk_lpt_sz, lpt_sz;
- int err = 0;
-
- if (!dbg_is_chk_lprops(c))
- return 0;
-
- switch (action) {
- case 0:
- d->chk_lpt_sz = 0;
- d->chk_lpt_sz2 = 0;
- d->chk_lpt_lebs = 0;
- d->chk_lpt_wastage = 0;
- if (c->dirty_pn_cnt > c->pnode_cnt) {
- ubifs_err(c, "dirty pnodes %d exceed max %d",
- c->dirty_pn_cnt, c->pnode_cnt);
- err = -EINVAL;
- }
- if (c->dirty_nn_cnt > c->nnode_cnt) {
- ubifs_err(c, "dirty nnodes %d exceed max %d",
- c->dirty_nn_cnt, c->nnode_cnt);
- err = -EINVAL;
- }
- return err;
- case 1:
- d->chk_lpt_sz += len;
- return 0;
- case 2:
- d->chk_lpt_sz += len;
- d->chk_lpt_wastage += len;
- d->chk_lpt_lebs += 1;
- return 0;
- case 3:
- chk_lpt_sz = c->leb_size;
- chk_lpt_sz *= d->chk_lpt_lebs;
- chk_lpt_sz += len - c->nhead_offs;
- if (d->chk_lpt_sz != chk_lpt_sz) {
- ubifs_err(c, "LPT wrote %lld but space used was %lld",
- d->chk_lpt_sz, chk_lpt_sz);
- err = -EINVAL;
- }
- if (d->chk_lpt_sz > c->lpt_sz) {
- ubifs_err(c, "LPT wrote %lld but lpt_sz is %lld",
- d->chk_lpt_sz, c->lpt_sz);
- err = -EINVAL;
- }
- if (d->chk_lpt_sz2 && d->chk_lpt_sz != d->chk_lpt_sz2) {
- ubifs_err(c, "LPT layout size %lld but wrote %lld",
- d->chk_lpt_sz, d->chk_lpt_sz2);
- err = -EINVAL;
- }
- if (d->chk_lpt_sz2 && d->new_nhead_offs != len) {
- ubifs_err(c, "LPT new nhead offs: expected %d was %d",
- d->new_nhead_offs, len);
- err = -EINVAL;
- }
- lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
- lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
- lpt_sz += c->ltab_sz;
- if (c->big_lpt)
- lpt_sz += c->lsave_sz;
- if (d->chk_lpt_sz - d->chk_lpt_wastage > lpt_sz) {
- ubifs_err(c, "LPT chk_lpt_sz %lld + waste %lld exceeds %lld",
- d->chk_lpt_sz, d->chk_lpt_wastage, lpt_sz);
- err = -EINVAL;
- }
- if (err) {
- ubifs_dump_lpt_info(c);
- ubifs_dump_lpt_lebs(c);
- dump_stack();
- }
- d->chk_lpt_sz2 = d->chk_lpt_sz;
- d->chk_lpt_sz = 0;
- d->chk_lpt_wastage = 0;
- d->chk_lpt_lebs = 0;
- d->new_nhead_offs = len;
- return err;
- case 4:
- d->chk_lpt_sz += len;
- d->chk_lpt_wastage += len;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/**
* dump_lpt_leb - dump an LPT LEB.
* @c: UBIFS file-system description object
* @lnum: LEB number to dump
@@ -1844,7 +1668,7 @@ static void dump_lpt_leb(const struct ubifs_info *c, int lnum)
int err, len = c->leb_size, node_type, node_num, node_len, offs;
void *buf, *p;
- pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+ pr_err("(pid %d) start dumping LEB %d\n", getpid(), lnum);
buf = p = __vmalloc(c->leb_size, GFP_NOFS);
if (!buf) {
ubifs_err(c, "cannot allocate memory to dump LPT");
@@ -1930,7 +1754,7 @@ static void dump_lpt_leb(const struct ubifs_info *c, int lnum)
len -= node_len;
}
- pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+ pr_err("(pid %d) finish dumping LEB %d\n", getpid(), lnum);
out:
vfree(buf);
return;
@@ -1947,10 +1771,10 @@ void ubifs_dump_lpt_lebs(const struct ubifs_info *c)
{
int i;
- pr_err("(pid %d) start dumping all LPT LEBs\n", current->pid);
+ pr_err("(pid %d) start dumping all LPT LEBs\n", getpid());
for (i = 0; i < c->lpt_lebs; i++)
dump_lpt_leb(c, i + c->lpt_first);
- pr_err("(pid %d) finish dumping all LPT LEBs\n", current->pid);
+ pr_err("(pid %d) finish dumping all LPT LEBs\n", getpid());
}
/**
@@ -1962,36 +1786,7 @@ void ubifs_dump_lpt_lebs(const struct ubifs_info *c)
* Returns zero if lsave has not been populated (this debugging feature is
* disabled) an non-zero if lsave has been populated.
*/
-static int dbg_populate_lsave(struct ubifs_info *c)
+static int dbg_populate_lsave(__unused struct ubifs_info *c)
{
- struct ubifs_lprops *lprops;
- struct ubifs_lpt_heap *heap;
- int i;
-
- if (!dbg_is_chk_gen(c))
- return 0;
- if (get_random_u32_below(4))
- return 0;
-
- for (i = 0; i < c->lsave_cnt; i++)
- c->lsave[i] = c->main_first;
-
- list_for_each_entry(lprops, &c->empty_list, list)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
- list_for_each_entry(lprops, &c->freeable_list, list)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
- list_for_each_entry(lprops, &c->frdi_idx_list, list)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
-
- heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
- for (i = 0; i < heap->cnt; i++)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
- heap = &c->lpt_heap[LPROPS_DIRTY - 1];
- for (i = 0; i < heap->cnt; i++)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
- heap = &c->lpt_heap[LPROPS_FREE - 1];
- for (i = 0; i < heap->cnt; i++)
- c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
-
- return 1;
+ return 0;
}
--
2.13.6
Add implementations for linux kernel printing functions, because these
functions(eg. pr_debug, pr_err, etc.) are widely used in UBIFS linux
kernel libs. No need to define multiple levels in dbg_msg for debuging,
just replace dbg_msg with pr_debug. Now, there are five levels of
printing messages:
0 - none
1 - error message
2 - warning message [default]
3 - notice message
4 - debug message
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 2 +-
ubifs-utils/common/defs.h | 48 ++++++++++++++++++++++++++--
ubifs-utils/common/devtable.c | 26 +++++++--------
ubifs-utils/common/lpt.c | 40 +++++++++++------------
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 64 ++++++++++++++++++-------------------
5 files changed, 112 insertions(+), 68 deletions(-)
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 3329b6f9..c14ba028 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -46,7 +46,7 @@ mkfs_ubifs_SOURCES = \
mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
- -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common
+ -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -rdynamic
EXTRA_DIST += ubifs-utils/common/README
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index 485c50c0..6d99a2fd 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -8,8 +8,10 @@
#include <stdlib.h>
#include <stdio.h>
+#include <unistd.h>
#include <limits.h>
#include <errno.h>
+#include <execinfo.h>
#include "ubifs.h"
@@ -22,10 +24,52 @@ extern struct ubifs_info info_;
enum { MKFS_PROGRAM_TYPE = 0 };
-#define dbg_msg(lvl, fmt, ...) do {if (info_.debug_level >= lvl) \
- printf("%s: %s: " fmt "\n", PROGRAM_NAME, __FUNCTION__, ##__VA_ARGS__); \
+enum { ERR_LEVEL = 1, WARN_LEVEL, INFO_LEVEL, DEBUG_LEVEL };
+
+#define pr_debug(fmt, ...) do { if (info_.debug_level >= DEBUG_LEVEL) \
+ printf("<DEBUG> %s[%d] (%s): %s: " fmt, PROGRAM_NAME, getpid(), \
+ info_.dev_name, __FUNCTION__, ##__VA_ARGS__); \
+} while(0)
+
+#define pr_notice(fmt, ...) do { if (info_.debug_level >= INFO_LEVEL) \
+ printf("<INFO> %s[%d] (%s): %s: " fmt, PROGRAM_NAME, getpid(), \
+ info_.dev_name, __FUNCTION__, ##__VA_ARGS__); \
+} while(0)
+
+#define pr_warn(fmt, ...) do { if (info_.debug_level >= WARN_LEVEL) \
+ printf("<WARN> %s[%d] (%s): %s: " fmt, PROGRAM_NAME, getpid(), \
+ info_.dev_name, __FUNCTION__, ##__VA_ARGS__); \
+} while(0)
+
+#define pr_err(fmt, ...) do { if (info_.debug_level >= ERR_LEVEL) \
+ printf("<ERROR> %s[%d] (%s): %s: " fmt, PROGRAM_NAME, getpid(), \
+ info_.dev_name, __FUNCTION__, ##__VA_ARGS__); \
} while(0)
+#define pr_cont(fmt, ...) do { if (info_.debug_level >= ERR_LEVEL) \
+ printf(fmt, ##__VA_ARGS__); \
+} while(0)
+
+static inline void dump_stack(void)
+{
+#define STACK_SIZE 512
+ int j, nptrs;
+ void *buffer[STACK_SIZE];
+ char **strings;
+
+ if (info_.debug_level < ERR_LEVEL)
+ return;
+
+ nptrs = backtrace(buffer, STACK_SIZE);
+ strings = backtrace_symbols(buffer, nptrs);
+
+ printf("dump_stack:\n");
+ for (j = 0; j < nptrs; j++)
+ printf("%s\n", strings[j]);
+
+ free(strings);
+}
+
#define unlikely(x) (x)
#define do_div(n,base) ({ \
diff --git a/ubifs-utils/common/devtable.c b/ubifs-utils/common/devtable.c
index 39032646..7347f092 100644
--- a/ubifs-utils/common/devtable.c
+++ b/ubifs-utils/common/devtable.c
@@ -148,10 +148,10 @@ static int interpret_table_entry(const char *line)
&start, &increment, &count) < 0)
return sys_errmsg("sscanf failed");
- dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, "
- "minor %u, start %u, inc %u, cnt %u",
- buf, type, mode, uid, gid, major, minor, start,
- increment, count);
+ pr_debug("name %s, type %c, mode %o, uid %u, gid %u, major %u, "
+ "minor %u, start %u, inc %u, cnt %u\n",
+ buf, type, mode, uid, gid, major, minor, start,
+ increment, count);
len = strnlen(buf, 1024);
if (len == 0)
@@ -204,7 +204,7 @@ static int interpret_table_entry(const char *line)
*/
ph_elt = hashtable_search(path_htbl, path);
if (!ph_elt) {
- dbg_msg(3, "inserting '%s' into path hash table", path);
+ pr_debug("inserting '%s' into path hash table\n", path);
ph_elt = malloc(sizeof(struct path_htbl_element));
if (!ph_elt) {
errmsg("cannot allocate %zd bytes of memory",
@@ -252,8 +252,8 @@ static int interpret_table_entry(const char *line)
nh_elt->gid = gid;
nh_elt->dev = makedev(major, minor);
- dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)",
- name, major(nh_elt->dev), minor(nh_elt->dev));
+ pr_debug("inserting '%s' into name hash table (major %d, minor %d)\n",
+ name, major(nh_elt->dev), minor(nh_elt->dev));
if (hashtable_search(ph_elt->name_htbl, name)) {
errmsg("'%s' is referred twice", buf);
@@ -291,8 +291,8 @@ static int interpret_table_entry(const char *line)
sprintf(nm, "%s%d", name, i);
nh_elt->name = nm;
- dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)",
- nm, major(nh_elt->dev), minor(nh_elt->dev));
+ pr_debug("inserting '%s' into name hash table (major %d, minor %d)\n",
+ nm, major(nh_elt->dev), minor(nh_elt->dev));
if (hashtable_search(ph_elt->name_htbl, nm)) {
errmsg("'%s' is referred twice", buf);
@@ -336,7 +336,7 @@ int parse_devtable(const char *tbl_file)
struct stat st;
size_t len;
- dbg_msg(1, "parsing device table file '%s'", tbl_file);
+ pr_debug("parsing device table file '%s'\n", tbl_file);
path_htbl = create_hashtable(128, &r5_hash, &is_equivalent);
if (!path_htbl)
@@ -386,7 +386,7 @@ int parse_devtable(const char *tbl_file)
line = NULL;
}
- dbg_msg(1, "finished parsing");
+ pr_debug("finished parsing\n");
fclose(f);
return 0;
@@ -460,8 +460,8 @@ int override_attributes(struct stat *st, struct path_htbl_element *ph_elt,
"different", strcmp(ph_elt->path, "/") ? ph_elt->path : "",
nh_elt->name);
- dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says",
- nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name);
+ pr_debug("set UID %d, GID %d, mode %o for %s/%s as device table says\n",
+ nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name);
st->st_uid = nh_elt->uid;
st->st_gid = nh_elt->gid;
diff --git a/ubifs-utils/common/lpt.c b/ubifs-utils/common/lpt.c
index 3c55f91b..0723698d 100644
--- a/ubifs-utils/common/lpt.c
+++ b/ubifs-utils/common/lpt.c
@@ -337,7 +337,7 @@ static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave)
*/
static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
{
- dbg_msg(3, "LEB %d free %d dirty %d to %d %d",
+ pr_debug("LEB %d free %d dirty %d to %d %d\n",
lnum, c->ltab[lnum - c->lpt_first].free,
c->ltab[lnum - c->lpt_first].dirty, free, dirty);
c->ltab[lnum - c->lpt_first].free = free;
@@ -566,26 +566,26 @@ int create_lpt(struct ubifs_info *c)
c->nhead_lnum = lnum;
c->nhead_offs = ALIGN(len, c->min_io_size);
- dbg_msg(1, "lpt_sz: %lld", c->lpt_sz);
- dbg_msg(1, "space_bits: %d", c->space_bits);
- dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits);
- dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits);
- dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits);
- dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits);
- dbg_msg(1, "lnum_bits: %d", c->lnum_bits);
- dbg_msg(1, "pnode_sz: %d", c->pnode_sz);
- dbg_msg(1, "nnode_sz: %d", c->nnode_sz);
- dbg_msg(1, "ltab_sz: %d", c->ltab_sz);
- dbg_msg(1, "lsave_sz: %d", c->lsave_sz);
- dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt);
- dbg_msg(1, "lpt_hght: %d", c->lpt_hght);
- dbg_msg(1, "big_lpt: %d", c->big_lpt);
- dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
- dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
- dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ pr_debug("lpt_sz: %lld\n", c->lpt_sz);
+ pr_debug("space_bits: %d\n", c->space_bits);
+ pr_debug("lpt_lnum_bits: %d\n", c->lpt_lnum_bits);
+ pr_debug("lpt_offs_bits: %d\n", c->lpt_offs_bits);
+ pr_debug("lpt_spc_bits: %d\n", c->lpt_spc_bits);
+ pr_debug("pcnt_bits: %d\n", c->pcnt_bits);
+ pr_debug("lnum_bits: %d\n", c->lnum_bits);
+ pr_debug("pnode_sz: %d\n", c->pnode_sz);
+ pr_debug("nnode_sz: %d\n", c->nnode_sz);
+ pr_debug("ltab_sz: %d\n", c->ltab_sz);
+ pr_debug("lsave_sz: %d\n", c->lsave_sz);
+ pr_debug("lsave_cnt: %d\n", c->lsave_cnt);
+ pr_debug("lpt_hght: %d\n", c->lpt_hght);
+ pr_debug("big_lpt: %d\n", c->big_lpt);
+ pr_debug("LPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs);
+ pr_debug("LPT head is at %d:%d\n", c->nhead_lnum, c->nhead_offs);
+ pr_debug("LPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt)
- dbg_msg(1, "LPT lsave is at %d:%d",
- c->lsave_lnum, c->lsave_offs);
+ pr_debug("LPT lsave is at %d:%d\n",
+ c->lsave_lnum, c->lsave_offs);
out:
free(lsave);
free(buf);
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index c2f5a29d..fb99222a 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -249,8 +249,8 @@ static const char *helptext =
"-y, --yes assume the answer is \"yes\" for all questions\n"
"-v, --verbose verbose operation\n"
"-V, --version display version information\n"
-"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n"
-" 2 - files, 3 - more details)\n"
+"-g, --debug=LEVEL display printing information (0 - none, 1 - error message, \n"
+" 2 - warning message[default], 3 - notice message, 4 - debug message)\n"
"-a, --set-inum-attr create user.image-inode-number extended attribute on files\n"
" added to the image. The attribute will contain the inode\n"
" number the file has in the generated image.\n"
@@ -632,7 +632,7 @@ static int get_options(int argc, char**argv)
case 'g':
c->debug_level = strtol(optarg, &endp, 0);
if (*endp != '\0' || endp == optarg ||
- c->debug_level < 0 || c->debug_level > 3)
+ c->debug_level < 0 || c->debug_level > DEBUG_LEVEL)
return errmsg("bad debugging level '%s'",
optarg);
break;
@@ -943,7 +943,7 @@ int write_leb(struct ubifs_info *c, int lnum, int len, void *buf)
{
off_t pos = (off_t)lnum * c->leb_size;
- dbg_msg(3, "LEB %d len %d", lnum, len);
+ pr_debug("LEB %d len %d\n", lnum, len);
memset(buf + len, 0xff, c->leb_size - len);
if (c->libubi)
if (ubi_leb_change_start(c->libubi, c->dev_fd, lnum, c->leb_size))
@@ -980,7 +980,7 @@ static int do_pad(void *buf, int len)
memset(buf + len, 0xff, alen - len);
pad_len = wlen - alen;
- dbg_msg(3, "len %d pad_len %d", len, pad_len);
+ pr_debug("len %d pad_len %d\n", len, pad_len);
buf += alen;
if (pad_len >= (int)UBIFS_PAD_NODE_SZ) {
struct ubifs_ch *ch = buf;
@@ -1068,8 +1068,7 @@ static void set_lprops(int lnum, int offs, int flags)
free = c->leb_size - ALIGN(offs, a);
dirty = c->leb_size - free - ALIGN(offs, 8);
- dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty,
- flags);
+ pr_debug("LEB %d free %d dirty %d flags %d\n", lnum, free, dirty, flags);
if (i < c->main_lebs) {
c->lpt[i].free = free;
c->lpt[i].dirty = dirty;
@@ -1104,7 +1103,7 @@ static int add_to_index(union ubifs_key *key, char *name, int name_len,
{
struct idx_entry *e;
- dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len);
+ pr_debug("LEB %d offs %d len %d\n", lnum, offs, len);
e = xmalloc(sizeof(struct idx_entry));
e->next = NULL;
e->prev = idx_list_last;
@@ -1434,19 +1433,19 @@ static int inode_add_selinux_xattr(struct ubifs_ino_node *host_ino,
if (selabel_lookup(sehnd, &secontext, sepath, st->st_mode) < 0) {
/* Failed to lookup context, assume unlabeled */
secontext = strdup("system_u:object_r:unlabeled_t:s0");
- dbg_msg(2, "missing context: %s\t%s\t%d\n", secontext, sepath,
- st->st_mode);
+ pr_debug("missing context: %s\t%s\t%d\n", secontext, sepath,
+ st->st_mode);
}
- dbg_msg(2, "appling selinux context on sepath=%s, secontext=%s\n",
- sepath, secontext);
+ pr_debug("appling selinux context on sepath=%s, secontext=%s\n",
+ sepath, secontext);
free(sepath);
con_size = strlen(secontext) + 1;
name = strdup(XATTR_NAME_SELINUX);
ret = add_xattr(host_ino, st, inum, name, secontext, con_size);
if (ret < 0)
- dbg_msg(2, "add_xattr failed %d\n", ret);
+ pr_debug("add_xattr failed %d\n", ret);
return ret;
}
@@ -1714,8 +1713,8 @@ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
char *kname;
int len;
- dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum,
- (unsigned int)type, (unsigned long)dir_inum);
+ pr_debug("%s ino %lu type %u dir ino %lu\n", name, (unsigned long)inum,
+ (unsigned int)type, (unsigned long)dir_inum);
memset(dent, 0, UBIFS_DENT_NODE_SZ);
dname.name = (void *)name;
@@ -1916,7 +1915,7 @@ static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink,
{
int fd, flags = 0;
- dbg_msg(2, "%s", path_name);
+ pr_debug("%s\n", path_name);
if (S_ISREG(st->st_mode)) {
fd = open(path_name, O_RDONLY);
@@ -2017,7 +2016,7 @@ static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st,
unsigned char type;
unsigned long long dir_creat_sqnum = ++c->max_sqnum;
- dbg_msg(2, "%s", dir_name);
+ pr_debug("%s\n", dir_name);
if (existing) {
dir = opendir(dir_name);
if (dir == NULL)
@@ -2237,7 +2236,7 @@ static int add_multi_linked_files(void)
unsigned char type = 0;
for (im = hash_table[i]; im; im = im->next) {
- dbg_msg(2, "%s", im->path_name);
+ pr_debug("%s\n", im->path_name);
err = add_non_dir(im->path_name, &im->use_inum,
im->use_nlink, &type, &im->st, NULL);
if (err)
@@ -2342,8 +2341,8 @@ static int add_idx_node(void *node, int child_cnt)
c->old_idx_sz += ALIGN(len, 8);
- dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len,
- c->old_idx_sz);
+ pr_debug("at %d:%d len %d index size %llu\n", lnum, offs, len,
+ c->old_idx_sz);
/* The last index node written will be the root */
c->zroot.lnum = lnum;
@@ -2365,7 +2364,7 @@ static int write_index(void)
int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err;
uint8_t *hashes;
- dbg_msg(1, "leaf node count: %zd", idx_cnt);
+ pr_debug("leaf node count: %zd\n", idx_cnt);
/* Reset the head for the index */
head_flags = LPROPS_INDEX;
@@ -2515,13 +2514,13 @@ static int write_index(void)
free(idx_ptr);
free(idx);
- dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs,
- c->zroot.len);
+ pr_debug("zroot is at %d:%d len %d\n", c->zroot.lnum, c->zroot.offs,
+ c->zroot.len);
/* Set the index head */
c->ihead_lnum = head_lnum;
c->ihead_offs = ALIGN(head_offs, c->min_io_size);
- dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs);
+ pr_debug("ihead is at %d:%d\n", c->ihead_lnum, c->ihead_offs);
/* Flush the last index LEB */
err = flush_nodes();
@@ -2567,13 +2566,13 @@ static int finalize_leb_cnt(void)
printf("\tindex lebs: %d\n", c->lst.idx_lebs);
printf("\tleb_cnt: %d\n", c->leb_cnt);
}
- dbg_msg(1, "total_free: %llu", c->lst.total_free);
- dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty);
- dbg_msg(1, "total_used: %llu", c->lst.total_used);
- dbg_msg(1, "total_dead: %llu", c->lst.total_dead);
- dbg_msg(1, "total_dark: %llu", c->lst.total_dark);
- dbg_msg(1, "index size: %llu", c->old_idx_sz);
- dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs);
+ pr_debug("total_free: %llu\n", c->lst.total_free);
+ pr_debug("total_dirty: %llu\n", c->lst.total_dirty);
+ pr_debug("total_used: %llu\n", c->lst.total_used);
+ pr_debug("total_dead: %llu\n", c->lst.total_dead);
+ pr_debug("total_dark: %llu\n", c->lst.total_dark);
+ pr_debug("index size: %llu\n", c->old_idx_sz);
+ pr_debug("empty_lebs: %d\n", c->lst.empty_lebs);
return 0;
}
@@ -2822,7 +2821,7 @@ static int init(void)
c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size);
c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size);
- dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm);
+ pr_debug("dead_wm %d dark_wm %d\n", c->dead_wm, c->dark_wm);
leb_buf = xmalloc(c->leb_size);
node_buf = xmalloc(NODE_BUFFER_SIZE);
@@ -2954,6 +2953,7 @@ int main(int argc, char *argv[])
info_.program_name = MKFS_PROGRAM_NAME;
info_.program_type = MKFS_PROGRAM_TYPE;
+ info_.debug_level = WARN_LEVEL;
if (crypto_init())
return -1;
--
2.13.6
Adapt dir.c in libubifs, compared with linux kernel implementations:
1. Remove all functions. Only keep an empty source file, fsck will
add new functions for mkdir/link operations without using linux
in-memory inode/dentry.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/dir.c | 1713 --------------------------------------------
1 file changed, 1713 deletions(-)
diff --git a/ubifs-utils/libubifs/dir.c b/ubifs-utils/libubifs/dir.c
index c77ea57f..9d5f6446 100644
--- a/ubifs-utils/libubifs/dir.c
+++ b/ubifs-utils/libubifs/dir.c
@@ -29,1716 +29,3 @@
*/
#include "ubifs.h"
-
-/**
- * inherit_flags - inherit flags of the parent inode.
- * @dir: parent inode
- * @mode: new inode mode flags
- *
- * This is a helper function for 'ubifs_new_inode()' which inherits flag of the
- * parent directory inode @dir. UBIFS inodes inherit the following flags:
- * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on
- * sub-directory basis;
- * o %UBIFS_SYNC_FL - useful for the same reasons;
- * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories.
- *
- * This function returns the inherited flags.
- */
-static int inherit_flags(const struct inode *dir, umode_t mode)
-{
- int flags;
- const struct ubifs_inode *ui = ubifs_inode(dir);
-
- if (!S_ISDIR(dir->i_mode))
- /*
- * The parent is not a directory, which means that an extended
- * attribute inode is being created. No flags.
- */
- return 0;
-
- flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL);
- if (!S_ISDIR(mode))
- /* The "DIRSYNC" flag only applies to directories */
- flags &= ~UBIFS_DIRSYNC_FL;
- return flags;
-}
-
-/**
- * ubifs_new_inode - allocate new UBIFS inode object.
- * @c: UBIFS file-system description object
- * @dir: parent directory inode
- * @mode: inode mode flags
- * @is_xattr: whether the inode is xattr inode
- *
- * This function finds an unused inode number, allocates new inode and
- * initializes it. Non-xattr new inode may be written with xattrs(selinux/
- * encryption) before writing dentry, which could cause inconsistent problem
- * when powercut happens between two operations. To deal with it, non-xattr
- * new inode is initialized with zero-nlink and added into orphan list, caller
- * should make sure that inode is relinked later, and make sure that orphan
- * removing and journal writing into an committing atomic operation. Returns
- * new inode in case of success and an error code in case of failure.
- */
-struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
- umode_t mode, bool is_xattr)
-{
- int err;
- struct inode *inode;
- struct ubifs_inode *ui;
- bool encrypted = false;
-
- inode = new_inode(c->vfs_sb);
- ui = ubifs_inode(inode);
- if (!inode)
- return ERR_PTR(-ENOMEM);
-
- /*
- * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and
- * marking them dirty in file write path (see 'file_update_time()').
- * UBIFS has to fully control "clean <-> dirty" transitions of inodes
- * to make budgeting work.
- */
- inode->i_flags |= S_NOCMTIME;
-
- inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
- simple_inode_init_ts(inode);
- inode->i_mapping->nrpages = 0;
-
- if (!is_xattr) {
- err = fscrypt_prepare_new_inode(dir, inode, &encrypted);
- if (err) {
- ubifs_err(c, "fscrypt_prepare_new_inode failed: %i", err);
- goto out_iput;
- }
- }
-
- switch (mode & S_IFMT) {
- case S_IFREG:
- inode->i_mapping->a_ops = &ubifs_file_address_operations;
- inode->i_op = &ubifs_file_inode_operations;
- inode->i_fop = &ubifs_file_operations;
- break;
- case S_IFDIR:
- inode->i_op = &ubifs_dir_inode_operations;
- inode->i_fop = &ubifs_dir_operations;
- inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;
- break;
- case S_IFLNK:
- inode->i_op = &ubifs_symlink_inode_operations;
- break;
- case S_IFSOCK:
- case S_IFIFO:
- case S_IFBLK:
- case S_IFCHR:
- inode->i_op = &ubifs_file_inode_operations;
- break;
- default:
- BUG();
- }
-
- ui->flags = inherit_flags(dir, mode);
- ubifs_set_inode_flags(inode);
- if (S_ISREG(mode))
- ui->compr_type = c->default_compr;
- else
- ui->compr_type = UBIFS_COMPR_NONE;
- ui->synced_i_size = 0;
-
- spin_lock(&c->cnt_lock);
- /* Inode number overflow is currently not supported */
- if (c->highest_inum >= INUM_WARN_WATERMARK) {
- if (c->highest_inum >= INUM_WATERMARK) {
- spin_unlock(&c->cnt_lock);
- ubifs_err(c, "out of inode numbers");
- err = -EINVAL;
- goto out_iput;
- }
- ubifs_warn(c, "running out of inode numbers (current %lu, max %u)",
- (unsigned long)c->highest_inum, INUM_WATERMARK);
- }
-
- inode->i_ino = ++c->highest_inum;
- /*
- * The creation sequence number remains with this inode for its
- * lifetime. All nodes for this inode have a greater sequence number,
- * and so it is possible to distinguish obsolete nodes belonging to a
- * previous incarnation of the same inode number - for example, for the
- * purpose of rebuilding the index.
- */
- ui->creat_sqnum = ++c->max_sqnum;
- spin_unlock(&c->cnt_lock);
-
- if (!is_xattr) {
- set_nlink(inode, 0);
- err = ubifs_add_orphan(c, inode->i_ino);
- if (err) {
- ubifs_err(c, "ubifs_add_orphan failed: %i", err);
- goto out_iput;
- }
- down_read(&c->commit_sem);
- ui->del_cmtno = c->cmt_no;
- up_read(&c->commit_sem);
- }
-
- if (encrypted) {
- err = fscrypt_set_context(inode, NULL);
- if (err) {
- if (!is_xattr) {
- set_nlink(inode, 1);
- ubifs_delete_orphan(c, inode->i_ino);
- }
- ubifs_err(c, "fscrypt_set_context failed: %i", err);
- goto out_iput;
- }
- }
-
- return inode;
-
-out_iput:
- make_bad_inode(inode);
- iput(inode);
- return ERR_PTR(err);
-}
-
-static int dbg_check_name(const struct ubifs_info *c,
- const struct ubifs_dent_node *dent,
- const struct fscrypt_name *nm)
-{
- if (!dbg_is_chk_gen(c))
- return 0;
- if (le16_to_cpu(dent->nlen) != fname_len(nm))
- return -EINVAL;
- if (memcmp(dent->name, fname_name(nm), fname_len(nm)))
- return -EINVAL;
- return 0;
-}
-
-static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
- unsigned int flags)
-{
- int err;
- union ubifs_key key;
- struct inode *inode = NULL;
- struct ubifs_dent_node *dent = NULL;
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct fscrypt_name nm;
-
- dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
-
- err = fscrypt_prepare_lookup(dir, dentry, &nm);
- if (err == -ENOENT)
- return d_splice_alias(NULL, dentry);
- if (err)
- return ERR_PTR(err);
-
- if (fname_len(&nm) > UBIFS_MAX_NLEN) {
- inode = ERR_PTR(-ENAMETOOLONG);
- goto done;
- }
-
- dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
- if (!dent) {
- inode = ERR_PTR(-ENOMEM);
- goto done;
- }
-
- if (fname_name(&nm) == NULL) {
- if (nm.hash & ~UBIFS_S_KEY_HASH_MASK)
- goto done; /* ENOENT */
- dent_key_init_hash(c, &key, dir->i_ino, nm.hash);
- err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash);
- } else {
- dent_key_init(c, &key, dir->i_ino, &nm);
- err = ubifs_tnc_lookup_nm(c, &key, dent, &nm);
- }
-
- if (err) {
- if (err == -ENOENT)
- dbg_gen("not found");
- else
- inode = ERR_PTR(err);
- goto done;
- }
-
- if (dbg_check_name(c, dent, &nm)) {
- inode = ERR_PTR(-EINVAL);
- goto done;
- }
-
- inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum));
- if (IS_ERR(inode)) {
- /*
- * This should not happen. Probably the file-system needs
- * checking.
- */
- err = PTR_ERR(inode);
- ubifs_err(c, "dead directory entry '%pd', error %d",
- dentry, err);
- ubifs_ro_mode(c, err);
- goto done;
- }
-
- if (IS_ENCRYPTED(dir) &&
- (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
- !fscrypt_has_permitted_context(dir, inode)) {
- ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu",
- dir->i_ino, inode->i_ino);
- iput(inode);
- inode = ERR_PTR(-EPERM);
- }
-
-done:
- kfree(dent);
- fscrypt_free_filename(&nm);
- return d_splice_alias(inode, dentry);
-}
-
-static int ubifs_prepare_create(struct inode *dir, struct dentry *dentry,
- struct fscrypt_name *nm)
-{
- if (fscrypt_is_nokey_name(dentry))
- return -ENOKEY;
-
- return fscrypt_setup_filename(dir, &dentry->d_name, 0, nm);
-}
-
-static int ubifs_create(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode, bool excl)
-{
- struct inode *inode;
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
- .dirtied_ino = 1 };
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- struct fscrypt_name nm;
- int err, sz_change;
-
- /*
- * Budget request settings: new inode, new direntry, changing the
- * parent directory inode.
- */
-
- dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
- dentry, mode, dir->i_ino);
-
- err = ubifs_budget_space(c, &req);
- if (err)
- return err;
-
- err = ubifs_prepare_create(dir, dentry, &nm);
- if (err)
- goto out_budg;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- inode = ubifs_new_inode(c, dir, mode, false);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_fname;
- }
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err)
- goto out_inode;
-
- set_nlink(inode, 1);
- mutex_lock(&dir_ui->ui_mutex);
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
- if (err)
- goto out_cancel;
- mutex_unlock(&dir_ui->ui_mutex);
-
- ubifs_release_budget(c, &req);
- fscrypt_free_filename(&nm);
- insert_inode_hash(inode);
- d_instantiate(dentry, inode);
- return 0;
-
-out_cancel:
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- mutex_unlock(&dir_ui->ui_mutex);
- set_nlink(inode, 0);
-out_inode:
- iput(inode);
-out_fname:
- fscrypt_free_filename(&nm);
-out_budg:
- ubifs_release_budget(c, &req);
- ubifs_err(c, "cannot create regular file, error %d", err);
- return err;
-}
-
-static struct inode *create_whiteout(struct inode *dir, struct dentry *dentry)
-{
- int err;
- umode_t mode = S_IFCHR | WHITEOUT_MODE;
- struct inode *inode;
- struct ubifs_info *c = dir->i_sb->s_fs_info;
-
- /*
- * Create an inode('nlink = 1') for whiteout without updating journal,
- * let ubifs_jnl_rename() store it on flash to complete rename whiteout
- * atomically.
- */
-
- dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
- dentry, mode, dir->i_ino);
-
- inode = ubifs_new_inode(c, dir, mode, false);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_free;
- }
-
- init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
- ubifs_assert(c, inode->i_op == &ubifs_file_inode_operations);
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err)
- goto out_inode;
-
- /* The dir size is updated by do_rename. */
- insert_inode_hash(inode);
-
- return inode;
-
-out_inode:
- iput(inode);
-out_free:
- ubifs_err(c, "cannot create whiteout file, error %d", err);
- return ERR_PTR(err);
-}
-
-/**
- * lock_2_inodes - a wrapper for locking two UBIFS inodes.
- * @inode1: first inode
- * @inode2: second inode
- *
- * We do not implement any tricks to guarantee strict lock ordering, because
- * VFS has already done it for us on the @i_mutex. So this is just a simple
- * wrapper function.
- */
-static void lock_2_inodes(struct inode *inode1, struct inode *inode2)
-{
- mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
- mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
-}
-
-/**
- * unlock_2_inodes - a wrapper for unlocking two UBIFS inodes.
- * @inode1: first inode
- * @inode2: second inode
- */
-static void unlock_2_inodes(struct inode *inode1, struct inode *inode2)
-{
- mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
- mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
-}
-
-static int ubifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
- struct file *file, umode_t mode)
-{
- struct dentry *dentry = file->f_path.dentry;
- struct inode *inode;
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
- .dirtied_ino = 1};
- struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
- struct ubifs_inode *ui;
- int err, instantiated = 0;
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: new inode, new direntry, changing the
- * parent directory inode.
- * Allocate budget separately for new dirtied inode, the budget will
- * be released via writeback.
- */
-
- dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
- dentry, mode, dir->i_ino);
-
- err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
- if (err)
- return err;
-
- err = ubifs_budget_space(c, &req);
- if (err) {
- fscrypt_free_filename(&nm);
- return err;
- }
-
- err = ubifs_budget_space(c, &ino_req);
- if (err) {
- ubifs_release_budget(c, &req);
- fscrypt_free_filename(&nm);
- return err;
- }
-
- inode = ubifs_new_inode(c, dir, mode, false);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_budg;
- }
- ui = ubifs_inode(inode);
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err)
- goto out_inode;
-
- set_nlink(inode, 1);
- mutex_lock(&ui->ui_mutex);
- insert_inode_hash(inode);
- d_tmpfile(file, inode);
- ubifs_assert(c, ui->dirty);
-
- instantiated = 1;
- mutex_unlock(&ui->ui_mutex);
-
- lock_2_inodes(dir, inode);
- err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 1);
- if (err)
- goto out_cancel;
- unlock_2_inodes(dir, inode);
-
- ubifs_release_budget(c, &req);
- fscrypt_free_filename(&nm);
-
- return finish_open_simple(file, 0);
-
-out_cancel:
- unlock_2_inodes(dir, inode);
-out_inode:
- if (!instantiated)
- iput(inode);
-out_budg:
- ubifs_release_budget(c, &req);
- if (!instantiated)
- ubifs_release_budget(c, &ino_req);
- fscrypt_free_filename(&nm);
- ubifs_err(c, "cannot create temporary file, error %d", err);
- return err;
-}
-
-/**
- * vfs_dent_type - get VFS directory entry type.
- * @type: UBIFS directory entry type
- *
- * This function converts UBIFS directory entry type into VFS directory entry
- * type.
- */
-static unsigned int vfs_dent_type(uint8_t type)
-{
- switch (type) {
- case UBIFS_ITYPE_REG:
- return DT_REG;
- case UBIFS_ITYPE_DIR:
- return DT_DIR;
- case UBIFS_ITYPE_LNK:
- return DT_LNK;
- case UBIFS_ITYPE_BLK:
- return DT_BLK;
- case UBIFS_ITYPE_CHR:
- return DT_CHR;
- case UBIFS_ITYPE_FIFO:
- return DT_FIFO;
- case UBIFS_ITYPE_SOCK:
- return DT_SOCK;
- default:
- BUG();
- }
- return 0;
-}
-
-/*
- * The classical Unix view for directory is that it is a linear array of
- * (name, inode number) entries. Linux/VFS assumes this model as well.
- * Particularly, 'readdir()' call wants us to return a directory entry offset
- * which later may be used to continue 'readdir()'ing the directory or to
- * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this
- * model because directory entries are identified by keys, which may collide.
- *
- * UBIFS uses directory entry hash value for directory offsets, so
- * 'seekdir()'/'telldir()' may not always work because of possible key
- * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work
- * properly by means of saving full directory entry name in the private field
- * of the file description object.
- *
- * This means that UBIFS cannot support NFS which requires full
- * 'seekdir()'/'telldir()' support.
- */
-static int ubifs_readdir(struct file *file, struct dir_context *ctx)
-{
- int fstr_real_len = 0, err = 0;
- struct fscrypt_name nm;
- struct fscrypt_str fstr = {0};
- union ubifs_key key;
- struct ubifs_dent_node *dent;
- struct inode *dir = file_inode(file);
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- bool encrypted = IS_ENCRYPTED(dir);
-
- dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos);
-
- if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2)
- /*
- * The directory was seek'ed to a senseless position or there
- * are no more entries.
- */
- return 0;
-
- if (encrypted) {
- err = fscrypt_prepare_readdir(dir);
- if (err)
- return err;
-
- err = fscrypt_fname_alloc_buffer(UBIFS_MAX_NLEN, &fstr);
- if (err)
- return err;
-
- fstr_real_len = fstr.len;
- }
-
- if (file->f_version == 0) {
- /*
- * The file was seek'ed, which means that @file->private_data
- * is now invalid. This may also be just the first
- * 'ubifs_readdir()' invocation, in which case
- * @file->private_data is NULL, and the below code is
- * basically a no-op.
- */
- kfree(file->private_data);
- file->private_data = NULL;
- }
-
- /*
- * 'generic_file_llseek()' unconditionally sets @file->f_version to
- * zero, and we use this for detecting whether the file was seek'ed.
- */
- file->f_version = 1;
-
- /* File positions 0 and 1 correspond to "." and ".." */
- if (ctx->pos < 2) {
- ubifs_assert(c, !file->private_data);
- if (!dir_emit_dots(file, ctx)) {
- if (encrypted)
- fscrypt_fname_free_buffer(&fstr);
- return 0;
- }
-
- /* Find the first entry in TNC and save it */
- lowest_dent_key(c, &key, dir->i_ino);
- fname_len(&nm) = 0;
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- err = PTR_ERR(dent);
- goto out;
- }
-
- ctx->pos = key_hash_flash(c, &dent->key);
- file->private_data = dent;
- }
-
- dent = file->private_data;
- if (!dent) {
- /*
- * The directory was seek'ed to and is now readdir'ed.
- * Find the entry corresponding to @ctx->pos or the closest one.
- */
- dent_key_init_hash(c, &key, dir->i_ino, ctx->pos);
- fname_len(&nm) = 0;
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- err = PTR_ERR(dent);
- goto out;
- }
- ctx->pos = key_hash_flash(c, &dent->key);
- file->private_data = dent;
- }
-
- while (1) {
- dbg_gen("ino %llu, new f_pos %#x",
- (unsigned long long)le64_to_cpu(dent->inum),
- key_hash_flash(c, &dent->key));
- ubifs_assert(c, le64_to_cpu(dent->ch.sqnum) >
- ubifs_inode(dir)->creat_sqnum);
-
- fname_len(&nm) = le16_to_cpu(dent->nlen);
- fname_name(&nm) = dent->name;
-
- if (encrypted) {
- fstr.len = fstr_real_len;
-
- err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c,
- &dent->key),
- le32_to_cpu(dent->cookie),
- &nm.disk_name, &fstr);
- if (err)
- goto out;
- } else {
- fstr.len = fname_len(&nm);
- fstr.name = fname_name(&nm);
- }
-
- if (!dir_emit(ctx, fstr.name, fstr.len,
- le64_to_cpu(dent->inum),
- vfs_dent_type(dent->type))) {
- if (encrypted)
- fscrypt_fname_free_buffer(&fstr);
- return 0;
- }
-
- /* Switch to the next entry */
- key_read(c, &dent->key, &key);
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- err = PTR_ERR(dent);
- goto out;
- }
-
- kfree(file->private_data);
- ctx->pos = key_hash_flash(c, &dent->key);
- file->private_data = dent;
- cond_resched();
- }
-
-out:
- kfree(file->private_data);
- file->private_data = NULL;
-
- if (encrypted)
- fscrypt_fname_free_buffer(&fstr);
-
- if (err != -ENOENT)
- ubifs_err(c, "cannot find next direntry, error %d", err);
- else
- /*
- * -ENOENT is a non-fatal error in this context, the TNC uses
- * it to indicate that the cursor moved past the current directory
- * and readdir() has to stop.
- */
- err = 0;
-
-
- /* 2 is a special value indicating that there are no more direntries */
- ctx->pos = 2;
- return err;
-}
-
-/* Free saved readdir() state when the directory is closed */
-static int ubifs_dir_release(struct inode *dir, struct file *file)
-{
- kfree(file->private_data);
- file->private_data = NULL;
- return 0;
-}
-
-static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
- struct dentry *dentry)
-{
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct inode *inode = d_inode(old_dentry);
- struct ubifs_inode *ui = ubifs_inode(inode);
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- int err, sz_change;
- struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2,
- .dirtied_ino_d = ALIGN(ui->data_len, 8) };
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: new direntry, changing the target inode,
- * changing the parent inode.
- */
-
- dbg_gen("dent '%pd' to ino %lu (nlink %d) in dir ino %lu",
- dentry, inode->i_ino,
- inode->i_nlink, dir->i_ino);
- ubifs_assert(c, inode_is_locked(dir));
- ubifs_assert(c, inode_is_locked(inode));
-
- err = fscrypt_prepare_link(old_dentry, dir, dentry);
- if (err)
- return err;
-
- err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
- if (err)
- return err;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- err = dbg_check_synced_i_size(c, inode);
- if (err)
- goto out_fname;
-
- err = ubifs_budget_space(c, &req);
- if (err)
- goto out_fname;
-
- lock_2_inodes(dir, inode);
-
- inc_nlink(inode);
- ihold(inode);
- inode_set_ctime_current(inode);
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, inode->i_nlink == 1);
- if (err)
- goto out_cancel;
- unlock_2_inodes(dir, inode);
-
- ubifs_release_budget(c, &req);
- d_instantiate(dentry, inode);
- fscrypt_free_filename(&nm);
- return 0;
-
-out_cancel:
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- drop_nlink(inode);
- unlock_2_inodes(dir, inode);
- ubifs_release_budget(c, &req);
- iput(inode);
-out_fname:
- fscrypt_free_filename(&nm);
- return err;
-}
-
-static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
-{
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct inode *inode = d_inode(dentry);
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- int err, sz_change, budgeted = 1;
- struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
- unsigned int saved_nlink = inode->i_nlink;
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: deletion direntry, deletion inode (+1 for
- * @dirtied_ino), changing the parent directory inode. If budgeting
- * fails, go ahead anyway because we have extra space reserved for
- * deletions.
- */
-
- dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu",
- dentry, inode->i_ino,
- inode->i_nlink, dir->i_ino);
-
- err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
- if (err)
- return err;
-
- err = ubifs_purge_xattrs(inode);
- if (err)
- return err;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- ubifs_assert(c, inode_is_locked(dir));
- ubifs_assert(c, inode_is_locked(inode));
- err = dbg_check_synced_i_size(c, inode);
- if (err)
- goto out_fname;
-
- err = ubifs_budget_space(c, &req);
- if (err) {
- if (err != -ENOSPC)
- goto out_fname;
- budgeted = 0;
- }
-
- lock_2_inodes(dir, inode);
- inode_set_ctime_current(inode);
- drop_nlink(inode);
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
- if (err)
- goto out_cancel;
- unlock_2_inodes(dir, inode);
-
- if (budgeted)
- ubifs_release_budget(c, &req);
- else {
- /* We've deleted something - clean the "no space" flags */
- c->bi.nospace = c->bi.nospace_rp = 0;
- smp_wmb();
- }
- fscrypt_free_filename(&nm);
- return 0;
-
-out_cancel:
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- set_nlink(inode, saved_nlink);
- unlock_2_inodes(dir, inode);
- if (budgeted)
- ubifs_release_budget(c, &req);
-out_fname:
- fscrypt_free_filename(&nm);
- return err;
-}
-
-/**
- * ubifs_check_dir_empty - check if a directory is empty or not.
- * @dir: VFS inode object of the directory to check
- *
- * This function checks if directory @dir is empty. Returns zero if the
- * directory is empty, %-ENOTEMPTY if it is not, and other negative error codes
- * in case of errors.
- */
-int ubifs_check_dir_empty(struct inode *dir)
-{
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct fscrypt_name nm = { 0 };
- struct ubifs_dent_node *dent;
- union ubifs_key key;
- int err;
-
- lowest_dent_key(c, &key, dir->i_ino);
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- err = PTR_ERR(dent);
- if (err == -ENOENT)
- err = 0;
- } else {
- kfree(dent);
- err = -ENOTEMPTY;
- }
- return err;
-}
-
-static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct inode *inode = d_inode(dentry);
- int err, sz_change, budgeted = 1;
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: deletion direntry, deletion inode and
- * changing the parent inode. If budgeting fails, go ahead anyway
- * because we have extra space reserved for deletions.
- */
-
- dbg_gen("directory '%pd', ino %lu in dir ino %lu", dentry,
- inode->i_ino, dir->i_ino);
- ubifs_assert(c, inode_is_locked(dir));
- ubifs_assert(c, inode_is_locked(inode));
- err = ubifs_check_dir_empty(d_inode(dentry));
- if (err)
- return err;
-
- err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
- if (err)
- return err;
-
- err = ubifs_purge_xattrs(inode);
- if (err)
- return err;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- err = ubifs_budget_space(c, &req);
- if (err) {
- if (err != -ENOSPC)
- goto out_fname;
- budgeted = 0;
- }
-
- lock_2_inodes(dir, inode);
- inode_set_ctime_current(inode);
- clear_nlink(inode);
- drop_nlink(dir);
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
- if (err)
- goto out_cancel;
- unlock_2_inodes(dir, inode);
-
- if (budgeted)
- ubifs_release_budget(c, &req);
- else {
- /* We've deleted something - clean the "no space" flags */
- c->bi.nospace = c->bi.nospace_rp = 0;
- smp_wmb();
- }
- fscrypt_free_filename(&nm);
- return 0;
-
-out_cancel:
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inc_nlink(dir);
- set_nlink(inode, 2);
- unlock_2_inodes(dir, inode);
- if (budgeted)
- ubifs_release_budget(c, &req);
-out_fname:
- fscrypt_free_filename(&nm);
- return err;
-}
-
-static int ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode)
-{
- struct inode *inode;
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- int err, sz_change;
- struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
- .dirtied_ino = 1};
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: new inode, new direntry and changing parent
- * directory inode.
- */
-
- dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
- dentry, mode, dir->i_ino);
-
- err = ubifs_budget_space(c, &req);
- if (err)
- return err;
-
- err = ubifs_prepare_create(dir, dentry, &nm);
- if (err)
- goto out_budg;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- inode = ubifs_new_inode(c, dir, S_IFDIR | mode, false);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_fname;
- }
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err)
- goto out_inode;
-
- set_nlink(inode, 1);
- mutex_lock(&dir_ui->ui_mutex);
- insert_inode_hash(inode);
- inc_nlink(inode);
- inc_nlink(dir);
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
- if (err) {
- ubifs_err(c, "cannot create directory, error %d", err);
- goto out_cancel;
- }
- mutex_unlock(&dir_ui->ui_mutex);
-
- ubifs_release_budget(c, &req);
- d_instantiate(dentry, inode);
- fscrypt_free_filename(&nm);
- return 0;
-
-out_cancel:
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- drop_nlink(dir);
- mutex_unlock(&dir_ui->ui_mutex);
- set_nlink(inode, 0);
-out_inode:
- iput(inode);
-out_fname:
- fscrypt_free_filename(&nm);
-out_budg:
- ubifs_release_budget(c, &req);
- return err;
-}
-
-static int ubifs_mknod(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode, dev_t rdev)
-{
- struct inode *inode;
- struct ubifs_inode *ui;
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- union ubifs_dev_desc *dev = NULL;
- int sz_change;
- int err, devlen = 0;
- struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
- .dirtied_ino = 1 };
- struct fscrypt_name nm;
-
- /*
- * Budget request settings: new inode, new direntry and changing parent
- * directory inode.
- */
-
- dbg_gen("dent '%pd' in dir ino %lu", dentry, dir->i_ino);
-
- if (S_ISBLK(mode) || S_ISCHR(mode)) {
- dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
- if (!dev)
- return -ENOMEM;
- devlen = ubifs_encode_dev(dev, rdev);
- }
-
- req.new_ino_d = ALIGN(devlen, 8);
- err = ubifs_budget_space(c, &req);
- if (err) {
- kfree(dev);
- return err;
- }
-
- err = ubifs_prepare_create(dir, dentry, &nm);
- if (err) {
- kfree(dev);
- goto out_budg;
- }
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- inode = ubifs_new_inode(c, dir, mode, false);
- if (IS_ERR(inode)) {
- kfree(dev);
- err = PTR_ERR(inode);
- goto out_fname;
- }
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err) {
- kfree(dev);
- goto out_inode;
- }
-
- init_special_inode(inode, inode->i_mode, rdev);
- inode->i_size = ubifs_inode(inode)->ui_size = devlen;
- ui = ubifs_inode(inode);
- ui->data = dev;
- ui->data_len = devlen;
- set_nlink(inode, 1);
-
- mutex_lock(&dir_ui->ui_mutex);
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
- if (err)
- goto out_cancel;
- mutex_unlock(&dir_ui->ui_mutex);
-
- ubifs_release_budget(c, &req);
- insert_inode_hash(inode);
- d_instantiate(dentry, inode);
- fscrypt_free_filename(&nm);
- return 0;
-
-out_cancel:
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- mutex_unlock(&dir_ui->ui_mutex);
- set_nlink(inode, 0);
-out_inode:
- iput(inode);
-out_fname:
- fscrypt_free_filename(&nm);
-out_budg:
- ubifs_release_budget(c, &req);
- return err;
-}
-
-static int ubifs_symlink(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, const char *symname)
-{
- struct inode *inode;
- struct ubifs_inode *ui;
- struct ubifs_inode *dir_ui = ubifs_inode(dir);
- struct ubifs_info *c = dir->i_sb->s_fs_info;
- int err, sz_change, len = strlen(symname);
- struct fscrypt_str disk_link;
- struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
- .dirtied_ino = 1 };
- struct fscrypt_name nm;
-
- dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry,
- symname, dir->i_ino);
-
- err = fscrypt_prepare_symlink(dir, symname, len, UBIFS_MAX_INO_DATA,
- &disk_link);
- if (err)
- return err;
-
- /*
- * Budget request settings: new inode, new direntry and changing parent
- * directory inode.
- */
- req.new_ino_d = ALIGN(disk_link.len - 1, 8);
- err = ubifs_budget_space(c, &req);
- if (err)
- return err;
-
- err = ubifs_prepare_create(dir, dentry, &nm);
- if (err)
- goto out_budg;
-
- sz_change = CALC_DENT_SIZE(fname_len(&nm));
-
- inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO, false);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_fname;
- }
-
- err = ubifs_init_security(dir, inode, &dentry->d_name);
- if (err)
- goto out_inode;
-
- ui = ubifs_inode(inode);
- ui->data = kmalloc(disk_link.len, GFP_NOFS);
- if (!ui->data) {
- err = -ENOMEM;
- goto out_inode;
- }
-
- if (IS_ENCRYPTED(inode)) {
- disk_link.name = ui->data; /* encrypt directly into ui->data */
- err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
- if (err)
- goto out_inode;
- } else {
- memcpy(ui->data, disk_link.name, disk_link.len);
- inode->i_link = ui->data;
- }
-
- /*
- * The terminating zero byte is not written to the flash media and it
- * is put just to make later in-memory string processing simpler. Thus,
- * data length is @disk_link.len - 1, not @disk_link.len.
- */
- ui->data_len = disk_link.len - 1;
- inode->i_size = ubifs_inode(inode)->ui_size = disk_link.len - 1;
- set_nlink(inode, 1);
-
- mutex_lock(&dir_ui->ui_mutex);
- dir->i_size += sz_change;
- dir_ui->ui_size = dir->i_size;
- inode_set_mtime_to_ts(dir,
- inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
- err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
- if (err)
- goto out_cancel;
- mutex_unlock(&dir_ui->ui_mutex);
-
- insert_inode_hash(inode);
- d_instantiate(dentry, inode);
- err = 0;
- goto out_fname;
-
-out_cancel:
- dir->i_size -= sz_change;
- dir_ui->ui_size = dir->i_size;
- mutex_unlock(&dir_ui->ui_mutex);
- set_nlink(inode, 0);
-out_inode:
- /* Free inode->i_link before inode is marked as bad. */
- fscrypt_free_inode(inode);
- iput(inode);
-out_fname:
- fscrypt_free_filename(&nm);
-out_budg:
- ubifs_release_budget(c, &req);
- return err;
-}
-
-/**
- * lock_4_inodes - a wrapper for locking three UBIFS inodes.
- * @inode1: first inode
- * @inode2: second inode
- * @inode3: third inode
- * @inode4: fourth inode
- *
- * This function is used for 'ubifs_rename()' and @inode1 may be the same as
- * @inode2 whereas @inode3 and @inode4 may be %NULL.
- *
- * We do not implement any tricks to guarantee strict lock ordering, because
- * VFS has already done it for us on the @i_mutex. So this is just a simple
- * wrapper function.
- */
-static void lock_4_inodes(struct inode *inode1, struct inode *inode2,
- struct inode *inode3, struct inode *inode4)
-{
- mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
- if (inode2 != inode1)
- mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
- if (inode3)
- mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3);
- if (inode4)
- mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4);
-}
-
-/**
- * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename.
- * @inode1: first inode
- * @inode2: second inode
- * @inode3: third inode
- * @inode4: fourth inode
- */
-static void unlock_4_inodes(struct inode *inode1, struct inode *inode2,
- struct inode *inode3, struct inode *inode4)
-{
- if (inode4)
- mutex_unlock(&ubifs_inode(inode4)->ui_mutex);
- if (inode3)
- mutex_unlock(&ubifs_inode(inode3)->ui_mutex);
- if (inode1 != inode2)
- mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
- mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
-}
-
-static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- unsigned int flags)
-{
- struct ubifs_info *c = old_dir->i_sb->s_fs_info;
- struct inode *old_inode = d_inode(old_dentry);
- struct inode *new_inode = d_inode(new_dentry);
- struct inode *whiteout = NULL;
- struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
- struct ubifs_inode *whiteout_ui = NULL;
- int err, release, sync = 0, move = (new_dir != old_dir);
- int is_dir = S_ISDIR(old_inode->i_mode);
- int unlink = !!new_inode, new_sz, old_sz;
- struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
- .dirtied_ino = 3 };
- struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
- .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
- struct ubifs_budget_req wht_req;
- unsigned int saved_nlink;
- struct fscrypt_name old_nm, new_nm;
-
- /*
- * Budget request settings:
- * req: deletion direntry, new direntry, removing the old inode,
- * and changing old and new parent directory inodes.
- *
- * wht_req: new whiteout inode for RENAME_WHITEOUT.
- *
- * ino_req: marks the target inode as dirty and does not write it.
- */
-
- dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x",
- old_dentry, old_inode->i_ino, old_dir->i_ino,
- new_dentry, new_dir->i_ino, flags);
-
- if (unlink) {
- ubifs_assert(c, inode_is_locked(new_inode));
-
- /* Budget for old inode's data when its nlink > 1. */
- req.dirtied_ino_d = ALIGN(ubifs_inode(new_inode)->data_len, 8);
- err = ubifs_purge_xattrs(new_inode);
- if (err)
- return err;
- }
-
- if (unlink && is_dir) {
- err = ubifs_check_dir_empty(new_inode);
- if (err)
- return err;
- }
-
- err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_nm);
- if (err)
- return err;
-
- err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_nm);
- if (err) {
- fscrypt_free_filename(&old_nm);
- return err;
- }
-
- new_sz = CALC_DENT_SIZE(fname_len(&new_nm));
- old_sz = CALC_DENT_SIZE(fname_len(&old_nm));
-
- err = ubifs_budget_space(c, &req);
- if (err) {
- fscrypt_free_filename(&old_nm);
- fscrypt_free_filename(&new_nm);
- return err;
- }
- err = ubifs_budget_space(c, &ino_req);
- if (err) {
- fscrypt_free_filename(&old_nm);
- fscrypt_free_filename(&new_nm);
- ubifs_release_budget(c, &req);
- return err;
- }
-
- if (flags & RENAME_WHITEOUT) {
- union ubifs_dev_desc *dev = NULL;
-
- dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
- if (!dev) {
- err = -ENOMEM;
- goto out_release;
- }
-
- /*
- * The whiteout inode without dentry is pinned in memory,
- * umount won't happen during rename process because we
- * got parent dentry.
- */
- whiteout = create_whiteout(old_dir, old_dentry);
- if (IS_ERR(whiteout)) {
- err = PTR_ERR(whiteout);
- kfree(dev);
- goto out_release;
- }
-
- whiteout_ui = ubifs_inode(whiteout);
- whiteout_ui->data = dev;
- whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0));
- ubifs_assert(c, !whiteout_ui->dirty);
-
- memset(&wht_req, 0, sizeof(struct ubifs_budget_req));
- wht_req.new_ino = 1;
- wht_req.new_ino_d = ALIGN(whiteout_ui->data_len, 8);
- /*
- * To avoid deadlock between space budget (holds ui_mutex and
- * waits wb work) and writeback work(waits ui_mutex), do space
- * budget before ubifs inodes locked.
- */
- err = ubifs_budget_space(c, &wht_req);
- if (err) {
- iput(whiteout);
- goto out_release;
- }
- set_nlink(whiteout, 1);
-
- /* Add the old_dentry size to the old_dir size. */
- old_sz -= CALC_DENT_SIZE(fname_len(&old_nm));
- }
-
- lock_4_inodes(old_dir, new_dir, new_inode, whiteout);
-
- /*
- * Like most other Unix systems, set the @i_ctime for inodes on a
- * rename.
- */
- simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
-
- /* We must adjust parent link count when renaming directories */
- if (is_dir) {
- if (move) {
- /*
- * @old_dir loses a link because we are moving
- * @old_inode to a different directory.
- */
- drop_nlink(old_dir);
- /*
- * @new_dir only gains a link if we are not also
- * overwriting an existing directory.
- */
- if (!unlink)
- inc_nlink(new_dir);
- } else {
- /*
- * @old_inode is not moving to a different directory,
- * but @old_dir still loses a link if we are
- * overwriting an existing directory.
- */
- if (unlink)
- drop_nlink(old_dir);
- }
- }
-
- old_dir->i_size -= old_sz;
- ubifs_inode(old_dir)->ui_size = old_dir->i_size;
-
- /*
- * And finally, if we unlinked a direntry which happened to have the
- * same name as the moved direntry, we have to decrement @i_nlink of
- * the unlinked inode.
- */
- if (unlink) {
- /*
- * Directories cannot have hard-links, so if this is a
- * directory, just clear @i_nlink.
- */
- saved_nlink = new_inode->i_nlink;
- if (is_dir)
- clear_nlink(new_inode);
- else
- drop_nlink(new_inode);
- } else {
- new_dir->i_size += new_sz;
- ubifs_inode(new_dir)->ui_size = new_dir->i_size;
- }
-
- /*
- * Do not ask 'ubifs_jnl_rename()' to flush write-buffer if @old_inode
- * is dirty, because this will be done later on at the end of
- * 'ubifs_rename()'.
- */
- if (IS_SYNC(old_inode)) {
- sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
- if (unlink && IS_SYNC(new_inode))
- sync = 1;
- /*
- * S_SYNC flag of whiteout inherits from the old_dir, and we
- * have already checked the old dir inode. So there is no need
- * to check whiteout.
- */
- }
-
- err = ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir,
- new_inode, &new_nm, whiteout, sync, !!whiteout);
- if (err)
- goto out_cancel;
-
- unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
- ubifs_release_budget(c, &req);
-
- if (whiteout) {
- ubifs_release_budget(c, &wht_req);
- iput(whiteout);
- }
-
- mutex_lock(&old_inode_ui->ui_mutex);
- release = old_inode_ui->dirty;
- mark_inode_dirty_sync(old_inode);
- mutex_unlock(&old_inode_ui->ui_mutex);
-
- if (release)
- ubifs_release_budget(c, &ino_req);
- if (IS_SYNC(old_inode))
- /*
- * Rename finished here. Although old inode cannot be updated
- * on flash, old ctime is not a big problem, don't return err
- * code to userspace.
- */
- old_inode->i_sb->s_op->write_inode(old_inode, NULL);
-
- fscrypt_free_filename(&old_nm);
- fscrypt_free_filename(&new_nm);
- return 0;
-
-out_cancel:
- if (unlink) {
- set_nlink(new_inode, saved_nlink);
- } else {
- new_dir->i_size -= new_sz;
- ubifs_inode(new_dir)->ui_size = new_dir->i_size;
- }
- old_dir->i_size += old_sz;
- ubifs_inode(old_dir)->ui_size = old_dir->i_size;
- if (is_dir) {
- if (move) {
- inc_nlink(old_dir);
- if (!unlink)
- drop_nlink(new_dir);
- } else {
- if (unlink)
- inc_nlink(old_dir);
- }
- }
- unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
- if (whiteout) {
- ubifs_release_budget(c, &wht_req);
- set_nlink(whiteout, 0);
- iput(whiteout);
- }
-out_release:
- ubifs_release_budget(c, &ino_req);
- ubifs_release_budget(c, &req);
- fscrypt_free_filename(&old_nm);
- fscrypt_free_filename(&new_nm);
- return err;
-}
-
-static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-{
- struct ubifs_info *c = old_dir->i_sb->s_fs_info;
- struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
- .dirtied_ino = 2 };
- int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
- struct inode *fst_inode = d_inode(old_dentry);
- struct inode *snd_inode = d_inode(new_dentry);
- int err;
- struct fscrypt_name fst_nm, snd_nm;
-
- ubifs_assert(c, fst_inode && snd_inode);
-
- /*
- * Budget request settings: changing two direntries, changing the two
- * parent directory inodes.
- */
-
- dbg_gen("dent '%pd' ino %lu in dir ino %lu exchange dent '%pd' ino %lu in dir ino %lu",
- old_dentry, fst_inode->i_ino, old_dir->i_ino,
- new_dentry, snd_inode->i_ino, new_dir->i_ino);
-
- err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &fst_nm);
- if (err)
- return err;
-
- err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &snd_nm);
- if (err) {
- fscrypt_free_filename(&fst_nm);
- return err;
- }
-
- err = ubifs_budget_space(c, &req);
- if (err)
- goto out;
-
- lock_4_inodes(old_dir, new_dir, NULL, NULL);
-
- simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
-
- if (old_dir != new_dir) {
- if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
- inc_nlink(new_dir);
- drop_nlink(old_dir);
- }
- else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
- drop_nlink(new_dir);
- inc_nlink(old_dir);
- }
- }
-
- err = ubifs_jnl_xrename(c, old_dir, fst_inode, &fst_nm, new_dir,
- snd_inode, &snd_nm, sync);
-
- unlock_4_inodes(old_dir, new_dir, NULL, NULL);
- ubifs_release_budget(c, &req);
-
-out:
- fscrypt_free_filename(&fst_nm);
- fscrypt_free_filename(&snd_nm);
- return err;
-}
-
-static int ubifs_rename(struct mnt_idmap *idmap,
- struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- unsigned int flags)
-{
- int err;
- struct ubifs_info *c = old_dir->i_sb->s_fs_info;
-
- if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE))
- return -EINVAL;
-
- ubifs_assert(c, inode_is_locked(old_dir));
- ubifs_assert(c, inode_is_locked(new_dir));
-
- err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
- flags);
- if (err)
- return err;
-
- if (flags & RENAME_EXCHANGE)
- return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry);
-
- return do_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
-}
-
-int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,
- struct kstat *stat, u32 request_mask, unsigned int flags)
-{
- loff_t size;
- struct inode *inode = d_inode(path->dentry);
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- mutex_lock(&ui->ui_mutex);
-
- if (ui->flags & UBIFS_APPEND_FL)
- stat->attributes |= STATX_ATTR_APPEND;
- if (ui->flags & UBIFS_COMPR_FL)
- stat->attributes |= STATX_ATTR_COMPRESSED;
- if (ui->flags & UBIFS_CRYPT_FL)
- stat->attributes |= STATX_ATTR_ENCRYPTED;
- if (ui->flags & UBIFS_IMMUTABLE_FL)
- stat->attributes |= STATX_ATTR_IMMUTABLE;
-
- stat->attributes_mask |= (STATX_ATTR_APPEND |
- STATX_ATTR_COMPRESSED |
- STATX_ATTR_ENCRYPTED |
- STATX_ATTR_IMMUTABLE);
-
- generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
- stat->blksize = UBIFS_BLOCK_SIZE;
- stat->size = ui->ui_size;
-
- /*
- * Unfortunately, the 'stat()' system call was designed for block
- * device based file systems, and it is not appropriate for UBIFS,
- * because UBIFS does not have notion of "block". For example, it is
- * difficult to tell how many block a directory takes - it actually
- * takes less than 300 bytes, but we have to round it to block size,
- * which introduces large mistake. This makes utilities like 'du' to
- * report completely senseless numbers. This is the reason why UBIFS
- * goes the same way as JFFS2 - it reports zero blocks for everything
- * but regular files, which makes more sense than reporting completely
- * wrong sizes.
- */
- if (S_ISREG(inode->i_mode)) {
- size = ui->xattr_size;
- size += stat->size;
- size = ALIGN(size, UBIFS_BLOCK_SIZE);
- /*
- * Note, user-space expects 512-byte blocks count irrespectively
- * of what was reported in @stat->size.
- */
- stat->blocks = size >> 9;
- } else
- stat->blocks = 0;
- mutex_unlock(&ui->ui_mutex);
- return 0;
-}
-
-const struct inode_operations ubifs_dir_inode_operations = {
- .lookup = ubifs_lookup,
- .create = ubifs_create,
- .link = ubifs_link,
- .symlink = ubifs_symlink,
- .unlink = ubifs_unlink,
- .mkdir = ubifs_mkdir,
- .rmdir = ubifs_rmdir,
- .mknod = ubifs_mknod,
- .rename = ubifs_rename,
- .setattr = ubifs_setattr,
- .getattr = ubifs_getattr,
- .listxattr = ubifs_listxattr,
- .update_time = ubifs_update_time,
- .tmpfile = ubifs_tmpfile,
- .fileattr_get = ubifs_fileattr_get,
- .fileattr_set = ubifs_fileattr_set,
-};
-
-const struct file_operations ubifs_dir_operations = {
- .llseek = generic_file_llseek,
- .release = ubifs_dir_release,
- .read = generic_read_dir,
- .iterate_shared = ubifs_readdir,
- .fsync = ubifs_fsync,
- .unlocked_ioctl = ubifs_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ubifs_compat_ioctl,
-#endif
-};
--
2.13.6
Adapt super.c in libubifs, compared with linux kernel implementations:
1. It contains all definitions in common/super.c(Message printing
functions are replaced with linux kernel styles).
2. Remove some functions(eg. ubifs_iget, ubifs_dirty_inode) which won't
be used in fsck/mkfs.
3. Remove unused variables initialization in some functions(eg.
init_constants_early, init_constants_sb).
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/super.c | 2219 ++++--------------------------------------
1 file changed, 177 insertions(+), 2042 deletions(-)
diff --git a/ubifs-utils/libubifs/super.c b/ubifs-utils/libubifs/super.c
index 09e270d6..1cbbfcac 100644
--- a/ubifs-utils/libubifs/super.c
+++ b/ubifs-utils/libubifs/super.c
@@ -14,487 +14,190 @@
* corresponding subsystems, but most of it is here.
*/
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/ctype.h>
-#include <linux/kthread.h>
-#include <linux/parser.h>
-#include <linux/seq_file.h>
-#include <linux/mount.h>
-#include <linux/math64.h>
-#include <linux/writeback.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
-static int ubifs_default_version_set(const char *val, const struct kernel_param *kp)
-{
- int n = 0, ret;
-
- ret = kstrtoint(val, 10, &n);
- if (ret != 0 || n < 4 || n > UBIFS_FORMAT_VERSION)
- return -EINVAL;
- return param_set_int(val, kp);
-}
-
-static const struct kernel_param_ops ubifs_default_version_ops = {
- .set = ubifs_default_version_set,
- .get = param_get_int,
-};
-
-int ubifs_default_version = UBIFS_FORMAT_VERSION;
-module_param_cb(default_version, &ubifs_default_version_ops, &ubifs_default_version, 0600);
-
-/*
- * Maximum amount of memory we may 'kmalloc()' without worrying that we are
- * allocating too much.
- */
-#define UBIFS_KMALLOC_OK (128*1024)
-
-/* Slab cache for UBIFS inodes */
-static struct kmem_cache *ubifs_inode_slab;
-
-/* UBIFS TNC shrinker description */
-static struct shrinker *ubifs_shrinker_info;
+atomic_long_t ubifs_clean_zn_cnt;
+static const int default_debug_level = WARN_LEVEL;
/**
- * validate_inode - validate inode.
- * @c: UBIFS file-system description object
- * @inode: the inode to validate
+ * open_ubi - open the libubi.
+ * @c: the UBIFS file-system description object
+ * @node: name of the UBI volume character device to fetch information about
*
- * This is a helper function for 'ubifs_iget()' which validates various fields
- * of a newly built inode to make sure they contain sane values and prevent
- * possible vulnerabilities. Returns zero if the inode is all right and
- * a non-zero error code if not.
+ * This function opens libubi, and initialize device & volume information
+ * according to @node. Returns %0 in case of success and %-1 in case of failure.
*/
-static int validate_inode(struct ubifs_info *c, const struct inode *inode)
+int open_ubi(struct ubifs_info *c, const char *node)
{
- int err;
- const struct ubifs_inode *ui = ubifs_inode(inode);
-
- if (inode->i_size > c->max_inode_sz) {
- ubifs_err(c, "inode is too large (%lld)",
- (long long)inode->i_size);
- return 1;
- }
-
- if (ui->compr_type >= UBIFS_COMPR_TYPES_CNT) {
- ubifs_err(c, "unknown compression type %d", ui->compr_type);
- return 2;
- }
-
- if (ui->xattr_names + ui->xattr_cnt > XATTR_LIST_MAX)
- return 3;
+ struct stat st;
- if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA)
- return 4;
+ if (stat(node, &st) || !S_ISCHR(st.st_mode))
+ return -1;
- if (ui->xattr && !S_ISREG(inode->i_mode))
- return 5;
+ c->libubi = libubi_open();
+ if (!c->libubi)
+ return -1;
+ if (ubi_get_vol_info(c->libubi, node, &c->vi))
+ goto out_err;
+ if (ubi_get_dev_info1(c->libubi, c->vi.dev_num, &c->di))
+ goto out_err;
- if (!ubifs_compr_present(c, ui->compr_type)) {
- ubifs_warn(c, "inode %lu uses '%s' compression, but it was not compiled in",
- inode->i_ino, ubifs_compr_name(c, ui->compr_type));
- }
+ return 0;
- err = dbg_check_dir(c, inode);
- return err;
+out_err:
+ close_ubi(c);
+ return -1;
}
-struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
+void close_ubi(struct ubifs_info *c)
{
- int err;
- union ubifs_key key;
- struct ubifs_ino_node *ino;
- struct ubifs_info *c = sb->s_fs_info;
- struct inode *inode;
- struct ubifs_inode *ui;
-
- dbg_gen("inode %lu", inum);
-
- inode = iget_locked(sb, inum);
- if (!inode)
- return ERR_PTR(-ENOMEM);
- if (!(inode->i_state & I_NEW))
- return inode;
- ui = ubifs_inode(inode);
-
- ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
- if (!ino) {
- err = -ENOMEM;
- goto out;
+ if (c->libubi) {
+ libubi_close(c->libubi);
+ c->libubi = NULL;
}
+}
- ino_key_init(c, &key, inode->i_ino);
+/**
+ * open_target - open the output target.
+ * @c: the UBIFS file-system description object
+ *
+ * Open the output target. The target can be an UBI volume
+ * or a file.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
+ */
+int open_target(struct ubifs_info *c)
+{
+ if (c->libubi) {
+ c->dev_fd = open(c->dev_name, O_RDWR | O_EXCL);
- err = ubifs_tnc_lookup(c, &key, ino);
- if (err)
- goto out_ino;
-
- inode->i_flags |= S_NOCMTIME;
-
- if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
- inode->i_flags |= S_NOATIME;
-
- set_nlink(inode, le32_to_cpu(ino->nlink));
- i_uid_write(inode, le32_to_cpu(ino->uid));
- i_gid_write(inode, le32_to_cpu(ino->gid));
- inode_set_atime(inode, (int64_t)le64_to_cpu(ino->atime_sec),
- le32_to_cpu(ino->atime_nsec));
- inode_set_mtime(inode, (int64_t)le64_to_cpu(ino->mtime_sec),
- le32_to_cpu(ino->mtime_nsec));
- inode_set_ctime(inode, (int64_t)le64_to_cpu(ino->ctime_sec),
- le32_to_cpu(ino->ctime_nsec));
- inode->i_mode = le32_to_cpu(ino->mode);
- inode->i_size = le64_to_cpu(ino->size);
-
- ui->data_len = le32_to_cpu(ino->data_len);
- ui->flags = le32_to_cpu(ino->flags);
- ui->compr_type = le16_to_cpu(ino->compr_type);
- ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum);
- ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
- ui->xattr_size = le32_to_cpu(ino->xattr_size);
- ui->xattr_names = le32_to_cpu(ino->xattr_names);
- ui->synced_i_size = ui->ui_size = inode->i_size;
-
- ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0;
-
- err = validate_inode(c, inode);
- if (err)
- goto out_invalid;
-
- switch (inode->i_mode & S_IFMT) {
- case S_IFREG:
- inode->i_mapping->a_ops = &ubifs_file_address_operations;
- inode->i_op = &ubifs_file_inode_operations;
- inode->i_fop = &ubifs_file_operations;
- if (ui->xattr) {
- ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
- if (!ui->data) {
- err = -ENOMEM;
- goto out_ino;
- }
- memcpy(ui->data, ino->data, ui->data_len);
- ((char *)ui->data)[ui->data_len] = '\0';
- } else if (ui->data_len != 0) {
- err = 10;
- goto out_invalid;
- }
- break;
- case S_IFDIR:
- inode->i_op = &ubifs_dir_inode_operations;
- inode->i_fop = &ubifs_dir_operations;
- if (ui->data_len != 0) {
- err = 11;
- goto out_invalid;
+ if (c->dev_fd == -1) {
+ ubifs_err(c, "cannot open the UBI volume. %s",
+ strerror(errno));
+ return -errno;
}
- break;
- case S_IFLNK:
- inode->i_op = &ubifs_symlink_inode_operations;
- if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
- err = 12;
- goto out_invalid;
- }
- ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
- if (!ui->data) {
- err = -ENOMEM;
- goto out_ino;
- }
- memcpy(ui->data, ino->data, ui->data_len);
- ((char *)ui->data)[ui->data_len] = '\0';
- break;
- case S_IFBLK:
- case S_IFCHR:
- {
- dev_t rdev;
- union ubifs_dev_desc *dev;
-
- ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
- if (!ui->data) {
- err = -ENOMEM;
- goto out_ino;
+ if (ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
+ close(c->dev_fd);
+ ubifs_err(c, "ubi_set_property(set direct_write) failed. %s",
+ strerror(errno));
+ return -errno;
}
-
- dev = (union ubifs_dev_desc *)ino->data;
- if (ui->data_len == sizeof(dev->new))
- rdev = new_decode_dev(le32_to_cpu(dev->new));
- else if (ui->data_len == sizeof(dev->huge))
- rdev = huge_decode_dev(le64_to_cpu(dev->huge));
- else {
- err = 13;
- goto out_invalid;
- }
- memcpy(ui->data, ino->data, ui->data_len);
- inode->i_op = &ubifs_file_inode_operations;
- init_special_inode(inode, inode->i_mode, rdev);
- break;
- }
- case S_IFSOCK:
- case S_IFIFO:
- inode->i_op = &ubifs_file_inode_operations;
- init_special_inode(inode, inode->i_mode, 0);
- if (ui->data_len != 0) {
- err = 14;
- goto out_invalid;
+ } else {
+ c->dev_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ if (c->dev_fd == -1) {
+ ubifs_err(c, "cannot create output file. %s",
+ strerror(errno));
+ return -errno;
}
- break;
- default:
- err = 15;
- goto out_invalid;
}
-
- kfree(ino);
- ubifs_set_inode_flags(inode);
- unlock_new_inode(inode);
- return inode;
-
-out_invalid:
- ubifs_err(c, "inode %lu validation failed, error %d", inode->i_ino, err);
- ubifs_dump_node(c, ino, UBIFS_MAX_INO_NODE_SZ);
- ubifs_dump_inode(c, inode);
- err = -EINVAL;
-out_ino:
- kfree(ino);
-out:
- ubifs_err(c, "failed to read inode %lu, error %d", inode->i_ino, err);
- iget_failed(inode);
- return ERR_PTR(err);
-}
-
-static struct inode *ubifs_alloc_inode(struct super_block *sb)
-{
- struct ubifs_inode *ui;
-
- ui = alloc_inode_sb(sb, ubifs_inode_slab, GFP_NOFS);
- if (!ui)
- return NULL;
-
- memset((void *)ui + sizeof(struct inode), 0,
- sizeof(struct ubifs_inode) - sizeof(struct inode));
- mutex_init(&ui->ui_mutex);
- init_rwsem(&ui->xattr_sem);
- spin_lock_init(&ui->ui_lock);
- return &ui->vfs_inode;
-};
-
-static void ubifs_free_inode(struct inode *inode)
-{
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- kfree(ui->data);
- fscrypt_free_inode(inode);
-
- kmem_cache_free(ubifs_inode_slab, ui);
+ return 0;
}
-/*
- * Note, Linux write-back code calls this without 'i_mutex'.
+/**
+ * close_target - close the output target.
+ * @c: the UBIFS file-system description object
+ *
+ * Close the output target. If the target was an UBI
+ * volume, also close libubi.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
*/
-static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
-{
- int err = 0;
- struct ubifs_info *c = inode->i_sb->s_fs_info;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- ubifs_assert(c, !ui->xattr);
- if (is_bad_inode(inode))
- return 0;
-
- mutex_lock(&ui->ui_mutex);
- /*
- * Due to races between write-back forced by budgeting
- * (see 'sync_some_inodes()') and background write-back, the inode may
- * have already been synchronized, do not do this again. This might
- * also happen if it was synchronized in an VFS operation, e.g.
- * 'ubifs_link()'.
- */
- if (!ui->dirty) {
- mutex_unlock(&ui->ui_mutex);
- return 0;
- }
-
- /*
- * As an optimization, do not write orphan inodes to the media just
- * because this is not needed.
- */
- dbg_gen("inode %lu, mode %#x, nlink %u",
- inode->i_ino, (int)inode->i_mode, inode->i_nlink);
- if (inode->i_nlink) {
- err = ubifs_jnl_write_inode(c, inode);
- if (err)
- ubifs_err(c, "can't write inode %lu, error %d",
- inode->i_ino, err);
- else
- err = dbg_check_inode_size(c, inode, ui->ui_size);
- }
-
- ui->dirty = 0;
- mutex_unlock(&ui->ui_mutex);
- ubifs_release_dirty_inode_budget(c, ui);
- return err;
-}
-
-static int ubifs_drop_inode(struct inode *inode)
-{
- int drop = generic_drop_inode(inode);
-
- if (!drop)
- drop = fscrypt_drop_inode(inode);
-
- return drop;
-}
-
-static void ubifs_evict_inode(struct inode *inode)
-{
- int err;
- struct ubifs_info *c = inode->i_sb->s_fs_info;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- if (ui->xattr)
- /*
- * Extended attribute inode deletions are fully handled in
- * 'ubifs_removexattr()'. These inodes are special and have
- * limited usage, so there is nothing to do here.
- */
- goto out;
-
- dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode);
- ubifs_assert(c, !atomic_read(&inode->i_count));
-
- truncate_inode_pages_final(&inode->i_data);
-
- if (inode->i_nlink)
- goto done;
-
- if (is_bad_inode(inode))
- goto out;
-
- ui->ui_size = inode->i_size = 0;
- err = ubifs_jnl_delete_inode(c, inode);
- if (err)
- /*
- * Worst case we have a lost orphan inode wasting space, so a
- * simple error message is OK here.
- */
- ubifs_err(c, "can't delete inode %lu, error %d",
- inode->i_ino, err);
-
-out:
- if (ui->dirty)
- ubifs_release_dirty_inode_budget(c, ui);
- else {
- /* We've deleted something - clean the "no space" flags */
- c->bi.nospace = c->bi.nospace_rp = 0;
- smp_wmb();
- }
-done:
- clear_inode(inode);
- fscrypt_put_encryption_info(inode);
-}
-
-static void ubifs_dirty_inode(struct inode *inode, int flags)
+int close_target(struct ubifs_info *c)
{
- struct ubifs_info *c = inode->i_sb->s_fs_info;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- ubifs_assert(c, mutex_is_locked(&ui->ui_mutex));
- if (!ui->dirty) {
- ui->dirty = 1;
- dbg_gen("inode %lu", inode->i_ino);
+ if (c->dev_fd >= 0) {
+ if (c->libubi && ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 0)) {
+ ubifs_err(c, "ubi_set_property(clear direct_write) failed. %s",
+ strerror(errno));
+ return -errno;
+ }
+ if (close(c->dev_fd) == -1) {
+ ubifs_err(c, "cannot close the target. %s",
+ strerror(errno));
+ return -errno;
+ }
}
-}
-
-static int ubifs_statfs(struct dentry *dentry, struct kstatfs *buf)
-{
- struct ubifs_info *c = dentry->d_sb->s_fs_info;
- unsigned long long free;
- __le32 *uuid = (__le32 *)c->uuid;
-
- free = ubifs_get_free_space(c);
- dbg_gen("free space %lld bytes (%lld blocks)",
- free, free >> UBIFS_BLOCK_SHIFT);
-
- buf->f_type = UBIFS_SUPER_MAGIC;
- buf->f_bsize = UBIFS_BLOCK_SIZE;
- buf->f_blocks = c->block_cnt;
- buf->f_bfree = free >> UBIFS_BLOCK_SHIFT;
- if (free > c->report_rp_size)
- buf->f_bavail = (free - c->report_rp_size) >> UBIFS_BLOCK_SHIFT;
- else
- buf->f_bavail = 0;
- buf->f_files = 0;
- buf->f_ffree = 0;
- buf->f_namelen = UBIFS_MAX_NLEN;
- buf->f_fsid.val[0] = le32_to_cpu(uuid[0]) ^ le32_to_cpu(uuid[2]);
- buf->f_fsid.val[1] = le32_to_cpu(uuid[1]) ^ le32_to_cpu(uuid[3]);
- ubifs_assert(c, buf->f_bfree <= c->block_cnt);
return 0;
}
-static int ubifs_show_options(struct seq_file *s, struct dentry *root)
+/**
+ * check_volume_empty - check if the UBI volume is empty.
+ * @c: the UBIFS file-system description object
+ *
+ * This function checks if the UBI volume is empty by looking if its LEBs are
+ * mapped or not.
+ *
+ * Returns %0 in case of success, %1 is the volume is not empty,
+ * and a negative error code in case of failure.
+ */
+int check_volume_empty(struct ubifs_info *c)
{
- struct ubifs_info *c = root->d_sb->s_fs_info;
-
- if (c->mount_opts.unmount_mode == 2)
- seq_puts(s, ",fast_unmount");
- else if (c->mount_opts.unmount_mode == 1)
- seq_puts(s, ",norm_unmount");
-
- if (c->mount_opts.bulk_read == 2)
- seq_puts(s, ",bulk_read");
- else if (c->mount_opts.bulk_read == 1)
- seq_puts(s, ",no_bulk_read");
-
- if (c->mount_opts.chk_data_crc == 2)
- seq_puts(s, ",chk_data_crc");
- else if (c->mount_opts.chk_data_crc == 1)
- seq_puts(s, ",no_chk_data_crc");
-
- if (c->mount_opts.override_compr) {
- seq_printf(s, ",compr=%s",
- ubifs_compr_name(c, c->mount_opts.compr_type));
- }
-
- seq_printf(s, ",assert=%s", ubifs_assert_action_name(c));
- seq_printf(s, ",ubi=%d,vol=%d", c->vi.ubi_num, c->vi.vol_id);
+ int lnum, err;
+ for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) {
+ err = ubi_is_mapped(c->dev_fd, lnum);
+ if (err < 0)
+ return err;
+ if (err == 1)
+ return 1;
+ }
return 0;
}
-static int ubifs_sync_fs(struct super_block *sb, int wait)
-{
- int i, err;
- struct ubifs_info *c = sb->s_fs_info;
-
- /*
- * Zero @wait is just an advisory thing to help the file system shove
- * lots of data into the queues, and there will be the second
- * '->sync_fs()' call, with non-zero @wait.
- */
- if (!wait)
- return 0;
-
- /*
- * Synchronize write buffers, because 'ubifs_run_commit()' does not
- * do this if it waits for an already running commit.
- */
- for (i = 0; i < c->jhead_cnt; i++) {
- err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
- if (err)
- return err;
+void init_ubifs_info(struct ubifs_info *c, int program_type)
+{
+ spin_lock_init(&c->cnt_lock);
+ spin_lock_init(&c->cs_lock);
+ spin_lock_init(&c->buds_lock);
+ spin_lock_init(&c->space_lock);
+ spin_lock_init(&c->orphan_lock);
+ init_rwsem(&c->commit_sem);
+ mutex_init(&c->lp_mutex);
+ mutex_init(&c->tnc_mutex);
+ mutex_init(&c->log_mutex);
+ c->buds = RB_ROOT;
+ c->old_idx = RB_ROOT;
+ c->size_tree = RB_ROOT;
+ c->orph_tree = RB_ROOT;
+ INIT_LIST_HEAD(&c->idx_gc);
+ INIT_LIST_HEAD(&c->replay_list);
+ INIT_LIST_HEAD(&c->replay_buds);
+ INIT_LIST_HEAD(&c->uncat_list);
+ INIT_LIST_HEAD(&c->empty_list);
+ INIT_LIST_HEAD(&c->freeable_list);
+ INIT_LIST_HEAD(&c->frdi_idx_list);
+ INIT_LIST_HEAD(&c->unclean_leb_list);
+ INIT_LIST_HEAD(&c->old_buds);
+ INIT_LIST_HEAD(&c->orph_list);
+ INIT_LIST_HEAD(&c->orph_new);
+ c->no_chk_data_crc = 1;
+
+ c->highest_inum = UBIFS_FIRST_INO;
+ c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
+
+ c->program_type = program_type;
+ switch (c->program_type) {
+ case MKFS_PROGRAM_TYPE:
+ c->program_name = MKFS_PROGRAM_NAME;
+ break;
+ default:
+ assert(0);
+ break;
}
-
- /*
- * Strictly speaking, it is not necessary to commit the journal here,
- * synchronizing write-buffers would be enough. But committing makes
- * UBIFS free space predictions much more accurate, so we want to let
- * the user be able to get more accurate results of 'statfs()' after
- * they synchronize the file system.
- */
- err = ubifs_run_commit(c);
- if (err)
- return err;
-
- return ubi_sync(c->vi.ubi_num);
+ c->dev_fd = -1;
+ c->debug_level = default_debug_level;
}
/**
@@ -506,46 +209,49 @@ static int ubifs_sync_fs(struct super_block *sb, int wait)
* requirements. Returns zero in case of success and a negative error code in
* case of failure.
*/
-static int init_constants_early(struct ubifs_info *c)
+int init_constants_early(struct ubifs_info *c)
{
+#define NOR_MAX_WRITESZ 64
if (c->vi.corrupted) {
ubifs_warn(c, "UBI volume is corrupted - read-only mode");
c->ro_media = 1;
}
- if (c->di.ro_mode) {
- ubifs_msg(c, "read-only UBI device");
- c->ro_media = 1;
- }
-
- if (c->vi.vol_type == UBI_STATIC_VOLUME) {
+ if (c->vi.type == UBI_STATIC_VOLUME) {
ubifs_msg(c, "static UBI volume - read-only mode");
c->ro_media = 1;
}
- c->leb_cnt = c->vi.size;
- c->leb_size = c->vi.usable_leb_size;
- c->leb_start = c->di.leb_start;
+ c->max_inode_sz = key_max_inode_size(c);
+ c->leb_cnt = c->vi.rsvd_lebs;
+ c->leb_size = c->vi.leb_size;
c->half_leb_size = c->leb_size / 2;
c->min_io_size = c->di.min_io_size;
c->min_io_shift = fls(c->min_io_size) - 1;
- c->max_write_size = c->di.max_write_size;
+ if (c->min_io_size == 1)
+ /*
+ * Different from linux kernel, the max write size of nor flash
+ * is not exposed in sysfs, just reset @c->max_write_size.
+ */
+ c->max_write_size = NOR_MAX_WRITESZ;
+ else
+ c->max_write_size = c->di.min_io_size;
c->max_write_shift = fls(c->max_write_size) - 1;
if (c->leb_size < UBIFS_MIN_LEB_SZ) {
- ubifs_errc(c, "too small LEBs (%d bytes), min. is %d bytes",
- c->leb_size, UBIFS_MIN_LEB_SZ);
+ ubifs_err(c, "too small LEBs (%d bytes), min. is %d bytes",
+ c->leb_size, UBIFS_MIN_LEB_SZ);
return -EINVAL;
}
if (c->leb_cnt < UBIFS_MIN_LEB_CNT) {
- ubifs_errc(c, "too few LEBs (%d), min. is %d",
- c->leb_cnt, UBIFS_MIN_LEB_CNT);
+ ubifs_err(c, "too few LEBs (%d), min. is %d",
+ c->leb_cnt, UBIFS_MIN_LEB_CNT);
return -EINVAL;
}
if (!is_power_of_2(c->min_io_size)) {
- ubifs_errc(c, "bad min. I/O size %d", c->min_io_size);
+ ubifs_err(c, "bad min. I/O size %d", c->min_io_size);
return -EINVAL;
}
@@ -556,8 +262,8 @@ static int init_constants_early(struct ubifs_info *c)
if (c->max_write_size < c->min_io_size ||
c->max_write_size % c->min_io_size ||
!is_power_of_2(c->max_write_size)) {
- ubifs_errc(c, "bad write buffer size %d for %d min. I/O unit",
- c->max_write_size, c->min_io_size);
+ ubifs_err(c, "bad write buffer size %d for %d min. I/O unit",
+ c->max_write_size, c->min_io_size);
return -EINVAL;
}
@@ -630,11 +336,6 @@ static int init_constants_early(struct ubifs_info *c)
*/
c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ;
- /* Buffer size for bulk-reads */
- c->max_bu_buf_len = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ;
- if (c->max_bu_buf_len > c->leb_size)
- c->max_bu_buf_len = c->leb_size;
-
/* Log is ready, preserve one LEB for commits. */
c->min_log_bytes = c->leb_size;
@@ -670,7 +371,7 @@ static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad)
* makes sure they are all right. Returns zero in case of success and a
* negative error code in case of failure.
*/
-static int init_constants_sb(struct ubifs_info *c)
+int init_constants_sb(struct ubifs_info *c)
{
int tmp, err;
long long tmp64;
@@ -760,26 +461,9 @@ static int init_constants_sb(struct ubifs_info *c)
* the master node has been read. It also checks various UBIFS parameters and
* makes sure they are all right.
*/
-static void init_constants_master(struct ubifs_info *c)
+void init_constants_master(struct ubifs_info *c)
{
- long long tmp64;
-
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
- c->report_rp_size = ubifs_reported_space(c, c->rp_size);
-
- /*
- * Calculate total amount of FS blocks. This number is not used
- * internally because it does not make much sense for UBIFS, but it is
- * necessary to report something for the 'statfs()' call.
- *
- * Subtract the LEB reserved for GC, the LEB which is reserved for
- * deletions, minimum LEBs for the index, and assume only one journal
- * head is available.
- */
- tmp64 = c->main_lebs - 1 - 1 - MIN_INDEX_LEBS - c->jhead_cnt + 1;
- tmp64 *= (long long)c->leb_size - c->leb_overhead;
- tmp64 = ubifs_reported_space(c, tmp64);
- c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT;
}
/**
@@ -793,7 +477,7 @@ static void init_constants_master(struct ubifs_info *c)
* returns zero in case of success and a negative error code in case of
* failure.
*/
-static int take_gc_lnum(struct ubifs_info *c)
+int take_gc_lnum(struct ubifs_info *c)
{
int err;
@@ -815,7 +499,7 @@ static int take_gc_lnum(struct ubifs_info *c)
* This helper function allocates and initializes UBIFS write-buffers. Returns
* zero in case of success and %-ENOMEM in case of failure.
*/
-static int alloc_wbufs(struct ubifs_info *c)
+int alloc_wbufs(struct ubifs_info *c)
{
int i, err;
@@ -845,7 +529,6 @@ static int alloc_wbufs(struct ubifs_info *c)
* Garbage Collector head does not need to be synchronized by timer.
* Also GC head nodes are not grouped.
*/
- c->jheads[GCHD].wbuf.no_timer = 1;
c->jheads[GCHD].grouped = 0;
return 0;
@@ -870,7 +553,7 @@ out_wbuf:
* free_wbufs - free write-buffers.
* @c: UBIFS file-system description object
*/
-static void free_wbufs(struct ubifs_info *c)
+void free_wbufs(struct ubifs_info *c)
{
int i;
@@ -889,7 +572,7 @@ static void free_wbufs(struct ubifs_info *c)
* free_orphans - free orphans.
* @c: UBIFS file-system description object
*/
-static void free_orphans(struct ubifs_info *c)
+void free_orphans(struct ubifs_info *c)
{
struct ubifs_orphan *orph;
@@ -926,257 +609,13 @@ static void free_buds(struct ubifs_info *c)
}
/**
- * check_volume_empty - check if the UBI volume is empty.
- * @c: UBIFS file-system description object
- *
- * This function checks if the UBIFS volume is empty by looking if its LEBs are
- * mapped or not. The result of checking is stored in the @c->empty variable.
- * Returns zero in case of success and a negative error code in case of
- * failure.
- */
-static int check_volume_empty(struct ubifs_info *c)
-{
- int lnum, err;
-
- c->empty = 1;
- for (lnum = 0; lnum < c->leb_cnt; lnum++) {
- err = ubifs_is_mapped(c, lnum);
- if (unlikely(err < 0))
- return err;
- if (err == 1) {
- c->empty = 0;
- break;
- }
-
- cond_resched();
- }
-
- return 0;
-}
-
-/*
- * UBIFS mount options.
- *
- * Opt_fast_unmount: do not run a journal commit before un-mounting
- * Opt_norm_unmount: run a journal commit before un-mounting
- * Opt_bulk_read: enable bulk-reads
- * Opt_no_bulk_read: disable bulk-reads
- * Opt_chk_data_crc: check CRCs when reading data nodes
- * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
- * Opt_override_compr: override default compressor
- * Opt_assert: set ubifs_assert() action
- * Opt_auth_key: The key name used for authentication
- * Opt_auth_hash_name: The hash type used for authentication
- * Opt_err: just end of array marker
- */
-enum {
- Opt_fast_unmount,
- Opt_norm_unmount,
- Opt_bulk_read,
- Opt_no_bulk_read,
- Opt_chk_data_crc,
- Opt_no_chk_data_crc,
- Opt_override_compr,
- Opt_assert,
- Opt_auth_key,
- Opt_auth_hash_name,
- Opt_ignore,
- Opt_err,
-};
-
-static const match_table_t tokens = {
- {Opt_fast_unmount, "fast_unmount"},
- {Opt_norm_unmount, "norm_unmount"},
- {Opt_bulk_read, "bulk_read"},
- {Opt_no_bulk_read, "no_bulk_read"},
- {Opt_chk_data_crc, "chk_data_crc"},
- {Opt_no_chk_data_crc, "no_chk_data_crc"},
- {Opt_override_compr, "compr=%s"},
- {Opt_auth_key, "auth_key=%s"},
- {Opt_auth_hash_name, "auth_hash_name=%s"},
- {Opt_ignore, "ubi=%s"},
- {Opt_ignore, "vol=%s"},
- {Opt_assert, "assert=%s"},
- {Opt_err, NULL},
-};
-
-/**
- * parse_standard_option - parse a standard mount option.
- * @option: the option to parse
- *
- * Normally, standard mount options like "sync" are passed to file-systems as
- * flags. However, when a "rootflags=" kernel boot parameter is used, they may
- * be present in the options string. This function tries to deal with this
- * situation and parse standard options. Returns 0 if the option was not
- * recognized, and the corresponding integer flag if it was.
- *
- * UBIFS is only interested in the "sync" option, so do not check for anything
- * else.
- */
-static int parse_standard_option(const char *option)
-{
-
- pr_notice("UBIFS: parse %s\n", option);
- if (!strcmp(option, "sync"))
- return SB_SYNCHRONOUS;
- return 0;
-}
-
-/**
- * ubifs_parse_options - parse mount parameters.
- * @c: UBIFS file-system description object
- * @options: parameters to parse
- * @is_remount: non-zero if this is FS re-mount
- *
- * This function parses UBIFS mount options and returns zero in case success
- * and a negative error code in case of failure.
- */
-static int ubifs_parse_options(struct ubifs_info *c, char *options,
- int is_remount)
-{
- char *p;
- substring_t args[MAX_OPT_ARGS];
-
- if (!options)
- return 0;
-
- while ((p = strsep(&options, ","))) {
- int token;
-
- if (!*p)
- continue;
-
- token = match_token(p, tokens, args);
- switch (token) {
- /*
- * %Opt_fast_unmount and %Opt_norm_unmount options are ignored.
- * We accept them in order to be backward-compatible. But this
- * should be removed at some point.
- */
- case Opt_fast_unmount:
- c->mount_opts.unmount_mode = 2;
- break;
- case Opt_norm_unmount:
- c->mount_opts.unmount_mode = 1;
- break;
- case Opt_bulk_read:
- c->mount_opts.bulk_read = 2;
- c->bulk_read = 1;
- break;
- case Opt_no_bulk_read:
- c->mount_opts.bulk_read = 1;
- c->bulk_read = 0;
- break;
- case Opt_chk_data_crc:
- c->mount_opts.chk_data_crc = 2;
- c->no_chk_data_crc = 0;
- break;
- case Opt_no_chk_data_crc:
- c->mount_opts.chk_data_crc = 1;
- c->no_chk_data_crc = 1;
- break;
- case Opt_override_compr:
- {
- char *name = match_strdup(&args[0]);
-
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "none"))
- c->mount_opts.compr_type = UBIFS_COMPR_NONE;
- else if (!strcmp(name, "lzo"))
- c->mount_opts.compr_type = UBIFS_COMPR_LZO;
- else if (!strcmp(name, "zlib"))
- c->mount_opts.compr_type = UBIFS_COMPR_ZLIB;
- else if (!strcmp(name, "zstd"))
- c->mount_opts.compr_type = UBIFS_COMPR_ZSTD;
- else {
- ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready?
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- c->mount_opts.override_compr = 1;
- c->default_compr = c->mount_opts.compr_type;
- break;
- }
- case Opt_assert:
- {
- char *act = match_strdup(&args[0]);
-
- if (!act)
- return -ENOMEM;
- if (!strcmp(act, "report"))
- c->assert_action = ASSACT_REPORT;
- else if (!strcmp(act, "read-only"))
- c->assert_action = ASSACT_RO;
- else if (!strcmp(act, "panic"))
- c->assert_action = ASSACT_PANIC;
- else {
- ubifs_err(c, "unknown assert action \"%s\"", act);
- kfree(act);
- return -EINVAL;
- }
- kfree(act);
- break;
- }
- case Opt_auth_key:
- if (!is_remount) {
- c->auth_key_name = kstrdup(args[0].from,
- GFP_KERNEL);
- if (!c->auth_key_name)
- return -ENOMEM;
- }
- break;
- case Opt_auth_hash_name:
- if (!is_remount) {
- c->auth_hash_name = kstrdup(args[0].from,
- GFP_KERNEL);
- if (!c->auth_hash_name)
- return -ENOMEM;
- }
- break;
- case Opt_ignore:
- break;
- default:
- {
- unsigned long flag;
- struct super_block *sb = c->vfs_sb;
-
- flag = parse_standard_option(p);
- if (!flag) {
- ubifs_err(c, "unrecognized mount option \"%s\" or missing value",
- p);
- return -EINVAL;
- }
- sb->s_flags |= flag;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/*
- * ubifs_release_options - release mount parameters which have been dumped.
- * @c: UBIFS file-system description object
- */
-static void ubifs_release_options(struct ubifs_info *c)
-{
- kfree(c->auth_key_name);
- c->auth_key_name = NULL;
- kfree(c->auth_hash_name);
- c->auth_hash_name = NULL;
-}
-
-/**
* destroy_journal - destroy journal data structures.
* @c: UBIFS file-system description object
*
* This function destroys journal data structures including those that may have
* been created by recovery functions.
*/
-static void destroy_journal(struct ubifs_info *c)
+void destroy_journal(struct ubifs_info *c)
{
while (!list_empty(&c->unclean_leb_list)) {
struct ubifs_unclean_leb *ucleb;
@@ -1199,1307 +638,3 @@ static void destroy_journal(struct ubifs_info *c)
ubifs_tnc_close(c);
free_buds(c);
}
-
-/**
- * bu_init - initialize bulk-read information.
- * @c: UBIFS file-system description object
- */
-static void bu_init(struct ubifs_info *c)
-{
- ubifs_assert(c, c->bulk_read == 1);
-
- if (c->bu.buf)
- return; /* Already initialized */
-
-again:
- c->bu.buf = kmalloc(c->max_bu_buf_len, GFP_KERNEL | __GFP_NOWARN);
- if (!c->bu.buf) {
- if (c->max_bu_buf_len > UBIFS_KMALLOC_OK) {
- c->max_bu_buf_len = UBIFS_KMALLOC_OK;
- goto again;
- }
-
- /* Just disable bulk-read */
- ubifs_warn(c, "cannot allocate %d bytes of memory for bulk-read, disabling it",
- c->max_bu_buf_len);
- c->mount_opts.bulk_read = 1;
- c->bulk_read = 0;
- return;
- }
-}
-
-/**
- * check_free_space - check if there is enough free space to mount.
- * @c: UBIFS file-system description object
- *
- * This function makes sure UBIFS has enough free space to be mounted in
- * read/write mode. UBIFS must always have some free space to allow deletions.
- */
-static int check_free_space(struct ubifs_info *c)
-{
- ubifs_assert(c, c->dark_wm > 0);
- if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) {
- ubifs_err(c, "insufficient free space to mount in R/W mode");
- ubifs_dump_budg(c, &c->bi);
- ubifs_dump_lprops(c);
- return -ENOSPC;
- }
- return 0;
-}
-
-/**
- * mount_ubifs - mount UBIFS file-system.
- * @c: UBIFS file-system description object
- *
- * This function mounts UBIFS file system. Returns zero in case of success and
- * a negative error code in case of failure.
- */
-static int mount_ubifs(struct ubifs_info *c)
-{
- int err;
- long long x, y;
- size_t sz;
-
- c->ro_mount = !!sb_rdonly(c->vfs_sb);
- /* Suppress error messages while probing if SB_SILENT is set */
- c->probing = !!(c->vfs_sb->s_flags & SB_SILENT);
-
- err = init_constants_early(c);
- if (err)
- return err;
-
- err = ubifs_debugging_init(c);
- if (err)
- return err;
-
- err = ubifs_sysfs_register(c);
- if (err)
- goto out_debugging;
-
- err = check_volume_empty(c);
- if (err)
- goto out_free;
-
- if (c->empty && (c->ro_mount || c->ro_media)) {
- /*
- * This UBI volume is empty, and read-only, or the file system
- * is mounted read-only - we cannot format it.
- */
- ubifs_err(c, "can't format empty UBI volume: read-only %s",
- c->ro_media ? "UBI volume" : "mount");
- err = -EROFS;
- goto out_free;
- }
-
- if (c->ro_media && !c->ro_mount) {
- ubifs_err(c, "cannot mount read-write - read-only media");
- err = -EROFS;
- goto out_free;
- }
-
- /*
- * The requirement for the buffer is that it should fit indexing B-tree
- * height amount of integers. We assume the height if the TNC tree will
- * never exceed 64.
- */
- err = -ENOMEM;
- c->bottom_up_buf = kmalloc_array(BOTTOM_UP_HEIGHT, sizeof(int),
- GFP_KERNEL);
- if (!c->bottom_up_buf)
- goto out_free;
-
- c->sbuf = vmalloc(c->leb_size);
- if (!c->sbuf)
- goto out_free;
-
- if (!c->ro_mount) {
- c->ileb_buf = vmalloc(c->leb_size);
- if (!c->ileb_buf)
- goto out_free;
- }
-
- if (c->bulk_read == 1)
- bu_init(c);
-
- if (!c->ro_mount) {
- c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
- UBIFS_CIPHER_BLOCK_SIZE,
- GFP_KERNEL);
- if (!c->write_reserve_buf)
- goto out_free;
- }
-
- c->mounting = 1;
-
- if (c->auth_key_name) {
- if (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) {
- err = ubifs_init_authentication(c);
- if (err)
- goto out_free;
- } else {
- ubifs_err(c, "auth_key_name, but UBIFS is built without"
- " authentication support");
- err = -EINVAL;
- goto out_free;
- }
- }
-
- err = ubifs_read_superblock(c);
- if (err)
- goto out_auth;
-
- c->probing = 0;
-
- /*
- * Make sure the compressor which is set as default in the superblock
- * or overridden by mount options is actually compiled in.
- */
- if (!ubifs_compr_present(c, c->default_compr)) {
- ubifs_err(c, "'compressor \"%s\" is not compiled in",
- ubifs_compr_name(c, c->default_compr));
- err = -ENOTSUPP;
- goto out_auth;
- }
-
- err = init_constants_sb(c);
- if (err)
- goto out_auth;
-
- sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2;
- c->cbuf = kmalloc(sz, GFP_NOFS);
- if (!c->cbuf) {
- err = -ENOMEM;
- goto out_auth;
- }
-
- err = alloc_wbufs(c);
- if (err)
- goto out_cbuf;
-
- sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id);
- if (!c->ro_mount) {
- /* Create background thread */
- c->bgt = kthread_run(ubifs_bg_thread, c, "%s", c->bgt_name);
- if (IS_ERR(c->bgt)) {
- err = PTR_ERR(c->bgt);
- c->bgt = NULL;
- ubifs_err(c, "cannot spawn \"%s\", error %d",
- c->bgt_name, err);
- goto out_wbufs;
- }
- }
-
- err = ubifs_read_master(c);
- if (err)
- goto out_master;
-
- init_constants_master(c);
-
- if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) {
- ubifs_msg(c, "recovery needed");
- c->need_recovery = 1;
- }
-
- if (c->need_recovery && !c->ro_mount) {
- err = ubifs_recover_inl_heads(c, c->sbuf);
- if (err)
- goto out_master;
- }
-
- err = ubifs_lpt_init(c, 1, !c->ro_mount);
- if (err)
- goto out_master;
-
- if (!c->ro_mount && c->space_fixup) {
- err = ubifs_fixup_free_space(c);
- if (err)
- goto out_lpt;
- }
-
- if (!c->ro_mount && !c->need_recovery) {
- /*
- * Set the "dirty" flag so that if we reboot uncleanly we
- * will notice this immediately on the next mount.
- */
- c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
- err = ubifs_write_master(c);
- if (err)
- goto out_lpt;
- }
-
- /*
- * Handle offline signed images: Now that the master node is
- * written and its validation no longer depends on the hash
- * in the superblock, we can update the offline signed
- * superblock with a HMAC version,
- */
- if (ubifs_authenticated(c) && ubifs_hmac_zero(c, c->sup_node->hmac)) {
- err = ubifs_hmac_wkm(c, c->sup_node->hmac_wkm);
- if (err)
- goto out_lpt;
- c->superblock_need_write = 1;
- }
-
- if (!c->ro_mount && c->superblock_need_write) {
- err = ubifs_write_sb_node(c, c->sup_node);
- if (err)
- goto out_lpt;
- c->superblock_need_write = 0;
- }
-
- err = dbg_check_idx_size(c, c->bi.old_idx_sz);
- if (err)
- goto out_lpt;
-
- err = ubifs_replay_journal(c);
- if (err)
- goto out_journal;
-
- /* Calculate 'min_idx_lebs' after journal replay */
- c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
-
- err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
- if (err)
- goto out_orphans;
-
- if (!c->ro_mount) {
- int lnum;
-
- err = check_free_space(c);
- if (err)
- goto out_orphans;
-
- /* Check for enough log space */
- lnum = c->lhead_lnum + 1;
- if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
- lnum = UBIFS_LOG_LNUM;
- if (lnum == c->ltail_lnum) {
- err = ubifs_consolidate_log(c);
- if (err)
- goto out_orphans;
- }
-
- if (c->need_recovery) {
- if (!ubifs_authenticated(c)) {
- err = ubifs_recover_size(c, true);
- if (err)
- goto out_orphans;
- }
-
- err = ubifs_rcvry_gc_commit(c);
- if (err)
- goto out_orphans;
-
- if (ubifs_authenticated(c)) {
- err = ubifs_recover_size(c, false);
- if (err)
- goto out_orphans;
- }
- } else {
- err = take_gc_lnum(c);
- if (err)
- goto out_orphans;
-
- /*
- * GC LEB may contain garbage if there was an unclean
- * reboot, and it should be un-mapped.
- */
- err = ubifs_leb_unmap(c, c->gc_lnum);
- if (err)
- goto out_orphans;
- }
-
- err = dbg_check_lprops(c);
- if (err)
- goto out_orphans;
- } else if (c->need_recovery) {
- err = ubifs_recover_size(c, false);
- if (err)
- goto out_orphans;
- } else {
- /*
- * Even if we mount read-only, we have to set space in GC LEB
- * to proper value because this affects UBIFS free space
- * reporting. We do not want to have a situation when
- * re-mounting from R/O to R/W changes amount of free space.
- */
- err = take_gc_lnum(c);
- if (err)
- goto out_orphans;
- }
-
- spin_lock(&ubifs_infos_lock);
- list_add_tail(&c->infos_list, &ubifs_infos);
- spin_unlock(&ubifs_infos_lock);
-
- if (c->need_recovery) {
- if (c->ro_mount)
- ubifs_msg(c, "recovery deferred");
- else {
- c->need_recovery = 0;
- ubifs_msg(c, "recovery completed");
- /*
- * GC LEB has to be empty and taken at this point. But
- * the journal head LEBs may also be accounted as
- * "empty taken" if they are empty.
- */
- ubifs_assert(c, c->lst.taken_empty_lebs > 0);
- }
- } else
- ubifs_assert(c, c->lst.taken_empty_lebs > 0);
-
- err = dbg_check_filesystem(c);
- if (err)
- goto out_infos;
-
- dbg_debugfs_init_fs(c);
-
- c->mounting = 0;
-
- ubifs_msg(c, "UBIFS: mounted UBI device %d, volume %d, name \"%s\"%s",
- c->vi.ubi_num, c->vi.vol_id, c->vi.name,
- c->ro_mount ? ", R/O mode" : "");
- x = (long long)c->main_lebs * c->leb_size;
- y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes;
- ubifs_msg(c, "LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes",
- c->leb_size, c->leb_size >> 10, c->min_io_size,
- c->max_write_size);
- ubifs_msg(c, "FS size: %lld bytes (%lld MiB, %d LEBs), max %d LEBs, journal size %lld bytes (%lld MiB, %d LEBs)",
- x, x >> 20, c->main_lebs, c->max_leb_cnt,
- y, y >> 20, c->log_lebs + c->max_bud_cnt);
- ubifs_msg(c, "reserved for root: %llu bytes (%llu KiB)",
- c->report_rp_size, c->report_rp_size >> 10);
- ubifs_msg(c, "media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s",
- c->fmt_version, c->ro_compat_version,
- UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION, c->uuid,
- c->big_lpt ? ", big LPT model" : ", small LPT model");
-
- dbg_gen("default compressor: %s", ubifs_compr_name(c, c->default_compr));
- dbg_gen("data journal heads: %d",
- c->jhead_cnt - NONDATA_JHEADS_CNT);
- dbg_gen("log LEBs: %d (%d - %d)",
- c->log_lebs, UBIFS_LOG_LNUM, c->log_last);
- dbg_gen("LPT area LEBs: %d (%d - %d)",
- c->lpt_lebs, c->lpt_first, c->lpt_last);
- dbg_gen("orphan area LEBs: %d (%d - %d)",
- c->orph_lebs, c->orph_first, c->orph_last);
- dbg_gen("main area LEBs: %d (%d - %d)",
- c->main_lebs, c->main_first, c->leb_cnt - 1);
- dbg_gen("index LEBs: %d", c->lst.idx_lebs);
- dbg_gen("total index bytes: %llu (%llu KiB, %llu MiB)",
- c->bi.old_idx_sz, c->bi.old_idx_sz >> 10,
- c->bi.old_idx_sz >> 20);
- dbg_gen("key hash type: %d", c->key_hash_type);
- dbg_gen("tree fanout: %d", c->fanout);
- dbg_gen("reserved GC LEB: %d", c->gc_lnum);
- dbg_gen("max. znode size %d", c->max_znode_sz);
- dbg_gen("max. index node size %d", c->max_idx_node_sz);
- dbg_gen("node sizes: data %zu, inode %zu, dentry %zu",
- UBIFS_DATA_NODE_SZ, UBIFS_INO_NODE_SZ, UBIFS_DENT_NODE_SZ);
- dbg_gen("node sizes: trun %zu, sb %zu, master %zu",
- UBIFS_TRUN_NODE_SZ, UBIFS_SB_NODE_SZ, UBIFS_MST_NODE_SZ);
- dbg_gen("node sizes: ref %zu, cmt. start %zu, orph %zu",
- UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ);
- dbg_gen("max. node sizes: data %zu, inode %zu dentry %zu, idx %d",
- UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ,
- UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout));
- dbg_gen("dead watermark: %d", c->dead_wm);
- dbg_gen("dark watermark: %d", c->dark_wm);
- dbg_gen("LEB overhead: %d", c->leb_overhead);
- x = (long long)c->main_lebs * c->dark_wm;
- dbg_gen("max. dark space: %lld (%lld KiB, %lld MiB)",
- x, x >> 10, x >> 20);
- dbg_gen("maximum bud bytes: %lld (%lld KiB, %lld MiB)",
- c->max_bud_bytes, c->max_bud_bytes >> 10,
- c->max_bud_bytes >> 20);
- dbg_gen("BG commit bud bytes: %lld (%lld KiB, %lld MiB)",
- c->bg_bud_bytes, c->bg_bud_bytes >> 10,
- c->bg_bud_bytes >> 20);
- dbg_gen("current bud bytes %lld (%lld KiB, %lld MiB)",
- c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20);
- dbg_gen("max. seq. number: %llu", c->max_sqnum);
- dbg_gen("commit number: %llu", c->cmt_no);
- dbg_gen("max. xattrs per inode: %d", ubifs_xattr_max_cnt(c));
- dbg_gen("max orphans: %d", c->max_orphans);
-
- return 0;
-
-out_infos:
- spin_lock(&ubifs_infos_lock);
- list_del(&c->infos_list);
- spin_unlock(&ubifs_infos_lock);
-out_orphans:
- free_orphans(c);
-out_journal:
- destroy_journal(c);
-out_lpt:
- ubifs_lpt_free(c, 0);
-out_master:
- kfree(c->mst_node);
- kfree(c->rcvrd_mst_node);
- if (c->bgt)
- kthread_stop(c->bgt);
-out_wbufs:
- free_wbufs(c);
-out_cbuf:
- kfree(c->cbuf);
-out_auth:
- ubifs_exit_authentication(c);
-out_free:
- kfree(c->write_reserve_buf);
- kfree(c->bu.buf);
- vfree(c->ileb_buf);
- vfree(c->sbuf);
- kfree(c->bottom_up_buf);
- kfree(c->sup_node);
- ubifs_sysfs_unregister(c);
-out_debugging:
- ubifs_debugging_exit(c);
- return err;
-}
-
-/**
- * ubifs_umount - un-mount UBIFS file-system.
- * @c: UBIFS file-system description object
- *
- * Note, this function is called to free allocated resourced when un-mounting,
- * as well as free resources when an error occurred while we were half way
- * through mounting (error path cleanup function). So it has to make sure the
- * resource was actually allocated before freeing it.
- */
-static void ubifs_umount(struct ubifs_info *c)
-{
- dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num,
- c->vi.vol_id);
-
- dbg_debugfs_exit_fs(c);
- spin_lock(&ubifs_infos_lock);
- list_del(&c->infos_list);
- spin_unlock(&ubifs_infos_lock);
-
- if (c->bgt)
- kthread_stop(c->bgt);
-
- destroy_journal(c);
- free_wbufs(c);
- free_orphans(c);
- ubifs_lpt_free(c, 0);
- ubifs_exit_authentication(c);
-
- ubifs_release_options(c);
- kfree(c->cbuf);
- kfree(c->rcvrd_mst_node);
- kfree(c->mst_node);
- kfree(c->write_reserve_buf);
- kfree(c->bu.buf);
- vfree(c->ileb_buf);
- vfree(c->sbuf);
- kfree(c->bottom_up_buf);
- kfree(c->sup_node);
- ubifs_debugging_exit(c);
- ubifs_sysfs_unregister(c);
-}
-
-/**
- * ubifs_remount_rw - re-mount in read-write mode.
- * @c: UBIFS file-system description object
- *
- * UBIFS avoids allocating many unnecessary resources when mounted in read-only
- * mode. This function allocates the needed resources and re-mounts UBIFS in
- * read-write mode.
- */
-static int ubifs_remount_rw(struct ubifs_info *c)
-{
- int err, lnum;
-
- if (c->rw_incompat) {
- ubifs_err(c, "the file-system is not R/W-compatible");
- ubifs_msg(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
- c->fmt_version, c->ro_compat_version,
- UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION);
- return -EROFS;
- }
-
- mutex_lock(&c->umount_mutex);
- dbg_save_space_info(c);
- c->remounting_rw = 1;
- c->ro_mount = 0;
-
- if (c->space_fixup) {
- err = ubifs_fixup_free_space(c);
- if (err)
- goto out;
- }
-
- err = check_free_space(c);
- if (err)
- goto out;
-
- if (c->need_recovery) {
- ubifs_msg(c, "completing deferred recovery");
- err = ubifs_write_rcvrd_mst_node(c);
- if (err)
- goto out;
- if (!ubifs_authenticated(c)) {
- err = ubifs_recover_size(c, true);
- if (err)
- goto out;
- }
- err = ubifs_clean_lebs(c, c->sbuf);
- if (err)
- goto out;
- err = ubifs_recover_inl_heads(c, c->sbuf);
- if (err)
- goto out;
- } else {
- /* A readonly mount is not allowed to have orphans */
- ubifs_assert(c, c->tot_orphans == 0);
- err = ubifs_clear_orphans(c);
- if (err)
- goto out;
- }
-
- if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) {
- c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
- err = ubifs_write_master(c);
- if (err)
- goto out;
- }
-
- if (c->superblock_need_write) {
- struct ubifs_sb_node *sup = c->sup_node;
-
- err = ubifs_write_sb_node(c, sup);
- if (err)
- goto out;
-
- c->superblock_need_write = 0;
- }
-
- c->ileb_buf = vmalloc(c->leb_size);
- if (!c->ileb_buf) {
- err = -ENOMEM;
- goto out;
- }
-
- c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
- UBIFS_CIPHER_BLOCK_SIZE, GFP_KERNEL);
- if (!c->write_reserve_buf) {
- err = -ENOMEM;
- goto out;
- }
-
- err = ubifs_lpt_init(c, 0, 1);
- if (err)
- goto out;
-
- /* Create background thread */
- c->bgt = kthread_run(ubifs_bg_thread, c, "%s", c->bgt_name);
- if (IS_ERR(c->bgt)) {
- err = PTR_ERR(c->bgt);
- c->bgt = NULL;
- ubifs_err(c, "cannot spawn \"%s\", error %d",
- c->bgt_name, err);
- goto out;
- }
-
- c->orph_buf = vmalloc(c->leb_size);
- if (!c->orph_buf) {
- err = -ENOMEM;
- goto out;
- }
-
- /* Check for enough log space */
- lnum = c->lhead_lnum + 1;
- if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
- lnum = UBIFS_LOG_LNUM;
- if (lnum == c->ltail_lnum) {
- err = ubifs_consolidate_log(c);
- if (err)
- goto out;
- }
-
- if (c->need_recovery) {
- err = ubifs_rcvry_gc_commit(c);
- if (err)
- goto out;
-
- if (ubifs_authenticated(c)) {
- err = ubifs_recover_size(c, false);
- if (err)
- goto out;
- }
- } else {
- err = ubifs_leb_unmap(c, c->gc_lnum);
- }
- if (err)
- goto out;
-
- dbg_gen("re-mounted read-write");
- c->remounting_rw = 0;
-
- if (c->need_recovery) {
- c->need_recovery = 0;
- ubifs_msg(c, "deferred recovery completed");
- } else {
- /*
- * Do not run the debugging space check if the were doing
- * recovery, because when we saved the information we had the
- * file-system in a state where the TNC and lprops has been
- * modified in memory, but all the I/O operations (including a
- * commit) were deferred. So the file-system was in
- * "non-committed" state. Now the file-system is in committed
- * state, and of course the amount of free space will change
- * because, for example, the old index size was imprecise.
- */
- err = dbg_check_space_info(c);
- }
-
- mutex_unlock(&c->umount_mutex);
- return err;
-
-out:
- c->ro_mount = 1;
- vfree(c->orph_buf);
- c->orph_buf = NULL;
- if (c->bgt) {
- kthread_stop(c->bgt);
- c->bgt = NULL;
- }
- kfree(c->write_reserve_buf);
- c->write_reserve_buf = NULL;
- vfree(c->ileb_buf);
- c->ileb_buf = NULL;
- ubifs_lpt_free(c, 1);
- c->remounting_rw = 0;
- mutex_unlock(&c->umount_mutex);
- return err;
-}
-
-/**
- * ubifs_remount_ro - re-mount in read-only mode.
- * @c: UBIFS file-system description object
- *
- * We assume VFS has stopped writing. Possibly the background thread could be
- * running a commit, however kthread_stop will wait in that case.
- */
-static void ubifs_remount_ro(struct ubifs_info *c)
-{
- int i, err;
-
- ubifs_assert(c, !c->need_recovery);
- ubifs_assert(c, !c->ro_mount);
-
- mutex_lock(&c->umount_mutex);
- if (c->bgt) {
- kthread_stop(c->bgt);
- c->bgt = NULL;
- }
-
- dbg_save_space_info(c);
-
- for (i = 0; i < c->jhead_cnt; i++) {
- err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
- if (err)
- ubifs_ro_mode(c, err);
- }
-
- c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
- c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
- c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
- err = ubifs_write_master(c);
- if (err)
- ubifs_ro_mode(c, err);
-
- vfree(c->orph_buf);
- c->orph_buf = NULL;
- kfree(c->write_reserve_buf);
- c->write_reserve_buf = NULL;
- vfree(c->ileb_buf);
- c->ileb_buf = NULL;
- ubifs_lpt_free(c, 1);
- c->ro_mount = 1;
- err = dbg_check_space_info(c);
- if (err)
- ubifs_ro_mode(c, err);
- mutex_unlock(&c->umount_mutex);
-}
-
-static void ubifs_put_super(struct super_block *sb)
-{
- int i;
- struct ubifs_info *c = sb->s_fs_info;
-
- ubifs_msg(c, "un-mount UBI device %d", c->vi.ubi_num);
-
- /*
- * The following asserts are only valid if there has not been a failure
- * of the media. For example, there will be dirty inodes if we failed
- * to write them back because of I/O errors.
- */
- if (!c->ro_error) {
- ubifs_assert(c, c->bi.idx_growth == 0);
- ubifs_assert(c, c->bi.dd_growth == 0);
- ubifs_assert(c, c->bi.data_growth == 0);
- }
-
- /*
- * The 'c->umount_lock' prevents races between UBIFS memory shrinker
- * and file system un-mount. Namely, it prevents the shrinker from
- * picking this superblock for shrinking - it will be just skipped if
- * the mutex is locked.
- */
- mutex_lock(&c->umount_mutex);
- if (!c->ro_mount) {
- /*
- * First of all kill the background thread to make sure it does
- * not interfere with un-mounting and freeing resources.
- */
- if (c->bgt) {
- kthread_stop(c->bgt);
- c->bgt = NULL;
- }
-
- /*
- * On fatal errors c->ro_error is set to 1, in which case we do
- * not write the master node.
- */
- if (!c->ro_error) {
- int err;
-
- /* Synchronize write-buffers */
- for (i = 0; i < c->jhead_cnt; i++) {
- err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
- if (err)
- ubifs_ro_mode(c, err);
- }
-
- /*
- * We are being cleanly unmounted which means the
- * orphans were killed - indicate this in the master
- * node. Also save the reserved GC LEB number.
- */
- c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
- c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
- c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
- err = ubifs_write_master(c);
- if (err)
- /*
- * Recovery will attempt to fix the master area
- * next mount, so we just print a message and
- * continue to unmount normally.
- */
- ubifs_err(c, "failed to write master node, error %d",
- err);
- } else {
- for (i = 0; i < c->jhead_cnt; i++)
- /* Make sure write-buffer timers are canceled */
- hrtimer_cancel(&c->jheads[i].wbuf.timer);
- }
- }
-
- ubifs_umount(c);
- ubi_close_volume(c->ubi);
- mutex_unlock(&c->umount_mutex);
-}
-
-static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
-{
- int err;
- struct ubifs_info *c = sb->s_fs_info;
-
- sync_filesystem(sb);
- dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
-
- err = ubifs_parse_options(c, data, 1);
- if (err) {
- ubifs_err(c, "invalid or unknown remount parameter");
- return err;
- }
-
- if (c->ro_mount && !(*flags & SB_RDONLY)) {
- if (c->ro_error) {
- ubifs_msg(c, "cannot re-mount R/W due to prior errors");
- return -EROFS;
- }
- if (c->ro_media) {
- ubifs_msg(c, "cannot re-mount R/W - UBI volume is R/O");
- return -EROFS;
- }
- err = ubifs_remount_rw(c);
- if (err)
- return err;
- } else if (!c->ro_mount && (*flags & SB_RDONLY)) {
- if (c->ro_error) {
- ubifs_msg(c, "cannot re-mount R/O due to prior errors");
- return -EROFS;
- }
- ubifs_remount_ro(c);
- }
-
- if (c->bulk_read == 1)
- bu_init(c);
- else {
- dbg_gen("disable bulk-read");
- mutex_lock(&c->bu_mutex);
- kfree(c->bu.buf);
- c->bu.buf = NULL;
- mutex_unlock(&c->bu_mutex);
- }
-
- if (!c->need_recovery)
- ubifs_assert(c, c->lst.taken_empty_lebs > 0);
-
- return 0;
-}
-
-const struct super_operations ubifs_super_operations = {
- .alloc_inode = ubifs_alloc_inode,
- .free_inode = ubifs_free_inode,
- .put_super = ubifs_put_super,
- .write_inode = ubifs_write_inode,
- .drop_inode = ubifs_drop_inode,
- .evict_inode = ubifs_evict_inode,
- .statfs = ubifs_statfs,
- .dirty_inode = ubifs_dirty_inode,
- .remount_fs = ubifs_remount_fs,
- .show_options = ubifs_show_options,
- .sync_fs = ubifs_sync_fs,
-};
-
-/**
- * open_ubi - parse UBI device name string and open the UBI device.
- * @name: UBI volume name
- * @mode: UBI volume open mode
- *
- * The primary method of mounting UBIFS is by specifying the UBI volume
- * character device node path. However, UBIFS may also be mounted without any
- * character device node using one of the following methods:
- *
- * o ubiX_Y - mount UBI device number X, volume Y;
- * o ubiY - mount UBI device number 0, volume Y;
- * o ubiX:NAME - mount UBI device X, volume with name NAME;
- * o ubi:NAME - mount UBI device 0, volume with name NAME.
- *
- * Alternative '!' separator may be used instead of ':' (because some shells
- * like busybox may interpret ':' as an NFS host name separator). This function
- * returns UBI volume description object in case of success and a negative
- * error code in case of failure.
- */
-static struct ubi_volume_desc *open_ubi(const char *name, int mode)
-{
- struct ubi_volume_desc *ubi;
- int dev, vol;
- char *endptr;
-
- if (!name || !*name)
- return ERR_PTR(-EINVAL);
-
- /* First, try to open using the device node path method */
- ubi = ubi_open_volume_path(name, mode);
- if (!IS_ERR(ubi))
- return ubi;
-
- /* Try the "nodev" method */
- if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i')
- return ERR_PTR(-EINVAL);
-
- /* ubi:NAME method */
- if ((name[3] == ':' || name[3] == '!') && name[4] != '\0')
- return ubi_open_volume_nm(0, name + 4, mode);
-
- if (!isdigit(name[3]))
- return ERR_PTR(-EINVAL);
-
- dev = simple_strtoul(name + 3, &endptr, 0);
-
- /* ubiY method */
- if (*endptr == '\0')
- return ubi_open_volume(0, dev, mode);
-
- /* ubiX_Y method */
- if (*endptr == '_' && isdigit(endptr[1])) {
- vol = simple_strtoul(endptr + 1, &endptr, 0);
- if (*endptr != '\0')
- return ERR_PTR(-EINVAL);
- return ubi_open_volume(dev, vol, mode);
- }
-
- /* ubiX:NAME method */
- if ((*endptr == ':' || *endptr == '!') && endptr[1] != '\0')
- return ubi_open_volume_nm(dev, ++endptr, mode);
-
- return ERR_PTR(-EINVAL);
-}
-
-static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
-{
- struct ubifs_info *c;
-
- c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
- if (c) {
- spin_lock_init(&c->cnt_lock);
- spin_lock_init(&c->cs_lock);
- spin_lock_init(&c->buds_lock);
- spin_lock_init(&c->space_lock);
- spin_lock_init(&c->orphan_lock);
- init_rwsem(&c->commit_sem);
- mutex_init(&c->lp_mutex);
- mutex_init(&c->tnc_mutex);
- mutex_init(&c->log_mutex);
- mutex_init(&c->umount_mutex);
- mutex_init(&c->bu_mutex);
- mutex_init(&c->write_reserve_mutex);
- init_waitqueue_head(&c->cmt_wq);
- c->buds = RB_ROOT;
- c->old_idx = RB_ROOT;
- c->size_tree = RB_ROOT;
- c->orph_tree = RB_ROOT;
- INIT_LIST_HEAD(&c->infos_list);
- INIT_LIST_HEAD(&c->idx_gc);
- INIT_LIST_HEAD(&c->replay_list);
- INIT_LIST_HEAD(&c->replay_buds);
- INIT_LIST_HEAD(&c->uncat_list);
- INIT_LIST_HEAD(&c->empty_list);
- INIT_LIST_HEAD(&c->freeable_list);
- INIT_LIST_HEAD(&c->frdi_idx_list);
- INIT_LIST_HEAD(&c->unclean_leb_list);
- INIT_LIST_HEAD(&c->old_buds);
- INIT_LIST_HEAD(&c->orph_list);
- INIT_LIST_HEAD(&c->orph_new);
- c->no_chk_data_crc = 1;
- c->assert_action = ASSACT_RO;
-
- c->highest_inum = UBIFS_FIRST_INO;
- c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
-
- ubi_get_volume_info(ubi, &c->vi);
- ubi_get_device_info(c->vi.ubi_num, &c->di);
- }
- return c;
-}
-
-static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
-{
- struct ubifs_info *c = sb->s_fs_info;
- struct inode *root;
- int err;
-
- c->vfs_sb = sb;
- /* Re-open the UBI device in read-write mode */
- c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE);
- if (IS_ERR(c->ubi)) {
- err = PTR_ERR(c->ubi);
- goto out;
- }
-
- err = ubifs_parse_options(c, data, 0);
- if (err)
- goto out_close;
-
- /*
- * UBIFS provides 'backing_dev_info' in order to disable read-ahead. For
- * UBIFS, I/O is not deferred, it is done immediately in read_folio,
- * which means the user would have to wait not just for their own I/O
- * but the read-ahead I/O as well i.e. completely pointless.
- *
- * Read-ahead will be disabled because @sb->s_bdi->ra_pages is 0. Also
- * @sb->s_bdi->capabilities are initialized to 0 so there won't be any
- * writeback happening.
- */
- err = super_setup_bdi_name(sb, "ubifs_%d_%d", c->vi.ubi_num,
- c->vi.vol_id);
- if (err)
- goto out_close;
- sb->s_bdi->ra_pages = 0;
- sb->s_bdi->io_pages = 0;
-
- sb->s_fs_info = c;
- sb->s_magic = UBIFS_SUPER_MAGIC;
- sb->s_blocksize = UBIFS_BLOCK_SIZE;
- sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT;
- sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c);
- if (c->max_inode_sz > MAX_LFS_FILESIZE)
- sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
- sb->s_op = &ubifs_super_operations;
- sb->s_xattr = ubifs_xattr_handlers;
- fscrypt_set_ops(sb, &ubifs_crypt_operations);
-
- mutex_lock(&c->umount_mutex);
- err = mount_ubifs(c);
- if (err) {
- ubifs_assert(c, err < 0);
- goto out_unlock;
- }
-
- /* Read the root inode */
- root = ubifs_iget(sb, UBIFS_ROOT_INO);
- if (IS_ERR(root)) {
- err = PTR_ERR(root);
- goto out_umount;
- }
-
- sb->s_root = d_make_root(root);
- if (!sb->s_root) {
- err = -ENOMEM;
- goto out_umount;
- }
-
- import_uuid(&sb->s_uuid, c->uuid);
-
- mutex_unlock(&c->umount_mutex);
- return 0;
-
-out_umount:
- ubifs_umount(c);
-out_unlock:
- mutex_unlock(&c->umount_mutex);
-out_close:
- ubifs_release_options(c);
- ubi_close_volume(c->ubi);
-out:
- return err;
-}
-
-static int sb_test(struct super_block *sb, void *data)
-{
- struct ubifs_info *c1 = data;
- struct ubifs_info *c = sb->s_fs_info;
-
- return c->vi.cdev == c1->vi.cdev;
-}
-
-static int sb_set(struct super_block *sb, void *data)
-{
- sb->s_fs_info = data;
- return set_anon_super(sb, NULL);
-}
-
-static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
- const char *name, void *data)
-{
- struct ubi_volume_desc *ubi;
- struct ubifs_info *c;
- struct super_block *sb;
- int err;
-
- dbg_gen("name %s, flags %#x", name, flags);
-
- /*
- * Get UBI device number and volume ID. Mount it read-only so far
- * because this might be a new mount point, and UBI allows only one
- * read-write user at a time.
- */
- ubi = open_ubi(name, UBI_READONLY);
- if (IS_ERR(ubi)) {
- if (!(flags & SB_SILENT))
- pr_err("UBIFS error (pid: %d): cannot open \"%s\", error %d",
- current->pid, name, (int)PTR_ERR(ubi));
- return ERR_CAST(ubi);
- }
-
- c = alloc_ubifs_info(ubi);
- if (!c) {
- err = -ENOMEM;
- goto out_close;
- }
-
- dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
-
- sb = sget(fs_type, sb_test, sb_set, flags, c);
- if (IS_ERR(sb)) {
- err = PTR_ERR(sb);
- kfree(c);
- goto out_close;
- }
-
- if (sb->s_root) {
- struct ubifs_info *c1 = sb->s_fs_info;
- kfree(c);
- /* A new mount point for already mounted UBIFS */
- dbg_gen("this ubi volume is already mounted");
- if (!!(flags & SB_RDONLY) != c1->ro_mount) {
- err = -EBUSY;
- goto out_deact;
- }
- } else {
- err = ubifs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
- if (err)
- goto out_deact;
- /* We do not support atime */
- sb->s_flags |= SB_ACTIVE;
- if (IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
- ubifs_msg(c, "full atime support is enabled.");
- else
- sb->s_flags |= SB_NOATIME;
- }
-
- /* 'fill_super()' opens ubi again so we must close it here */
- ubi_close_volume(ubi);
-
- return dget(sb->s_root);
-
-out_deact:
- deactivate_locked_super(sb);
-out_close:
- ubi_close_volume(ubi);
- return ERR_PTR(err);
-}
-
-static void kill_ubifs_super(struct super_block *s)
-{
- struct ubifs_info *c = s->s_fs_info;
- kill_anon_super(s);
- kfree(c);
-}
-
-static struct file_system_type ubifs_fs_type = {
- .name = "ubifs",
- .owner = THIS_MODULE,
- .mount = ubifs_mount,
- .kill_sb = kill_ubifs_super,
-};
-MODULE_ALIAS_FS("ubifs");
-
-/*
- * Inode slab cache constructor.
- */
-static void inode_slab_ctor(void *obj)
-{
- struct ubifs_inode *ui = obj;
- inode_init_once(&ui->vfs_inode);
-}
-
-static int __init ubifs_init(void)
-{
- int err = -ENOMEM;
-
- BUILD_BUG_ON(sizeof(struct ubifs_ch) != 24);
-
- /* Make sure node sizes are 8-byte aligned */
- BUILD_BUG_ON(UBIFS_CH_SZ & 7);
- BUILD_BUG_ON(UBIFS_INO_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_SB_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_MST_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_REF_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_CS_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_ORPH_NODE_SZ & 7);
-
- BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ & 7);
- BUILD_BUG_ON(UBIFS_MAX_NODE_SZ & 7);
- BUILD_BUG_ON(MIN_WRITE_SZ & 7);
-
- /* Check min. node size */
- BUILD_BUG_ON(UBIFS_INO_NODE_SZ < MIN_WRITE_SZ);
- BUILD_BUG_ON(UBIFS_DENT_NODE_SZ < MIN_WRITE_SZ);
- BUILD_BUG_ON(UBIFS_XENT_NODE_SZ < MIN_WRITE_SZ);
- BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ < MIN_WRITE_SZ);
-
- BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
- BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
- BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ > UBIFS_MAX_NODE_SZ);
- BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ > UBIFS_MAX_NODE_SZ);
-
- /* Defined node sizes */
- BUILD_BUG_ON(UBIFS_SB_NODE_SZ != 4096);
- BUILD_BUG_ON(UBIFS_MST_NODE_SZ != 512);
- BUILD_BUG_ON(UBIFS_INO_NODE_SZ != 160);
- BUILD_BUG_ON(UBIFS_REF_NODE_SZ != 64);
-
- /*
- * We use 2 bit wide bit-fields to store compression type, which should
- * be amended if more compressors are added. The bit-fields are:
- * @compr_type in 'struct ubifs_inode', @default_compr in
- * 'struct ubifs_info' and @compr_type in 'struct ubifs_mount_opts'.
- */
- BUILD_BUG_ON(UBIFS_COMPR_TYPES_CNT > 4);
-
- /*
- * We require that PAGE_SIZE is greater-than-or-equal-to
- * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2.
- */
- if (PAGE_SIZE < UBIFS_BLOCK_SIZE) {
- pr_err("UBIFS error (pid %d): VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes",
- current->pid, (unsigned int)PAGE_SIZE);
- return -EINVAL;
- }
-
- ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab",
- sizeof(struct ubifs_inode), 0,
- SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT |
- SLAB_ACCOUNT, &inode_slab_ctor);
- if (!ubifs_inode_slab)
- return -ENOMEM;
-
- ubifs_shrinker_info = shrinker_alloc(0, "ubifs-slab");
- if (!ubifs_shrinker_info)
- goto out_slab;
-
- ubifs_shrinker_info->count_objects = ubifs_shrink_count;
- ubifs_shrinker_info->scan_objects = ubifs_shrink_scan;
-
- shrinker_register(ubifs_shrinker_info);
-
- err = ubifs_compressors_init();
- if (err)
- goto out_shrinker;
-
- dbg_debugfs_init();
-
- err = ubifs_sysfs_init();
- if (err)
- goto out_dbg;
-
- err = register_filesystem(&ubifs_fs_type);
- if (err) {
- pr_err("UBIFS error (pid %d): cannot register file system, error %d",
- current->pid, err);
- goto out_sysfs;
- }
- return 0;
-
-out_sysfs:
- ubifs_sysfs_exit();
-out_dbg:
- dbg_debugfs_exit();
- ubifs_compressors_exit();
-out_shrinker:
- shrinker_free(ubifs_shrinker_info);
-out_slab:
- kmem_cache_destroy(ubifs_inode_slab);
- return err;
-}
-/* late_initcall to let compressors initialize first */
-late_initcall(ubifs_init);
-
-static void __exit ubifs_exit(void)
-{
- WARN_ON(!list_empty(&ubifs_infos));
- WARN_ON(atomic_long_read(&ubifs_clean_zn_cnt) != 0);
-
- dbg_debugfs_exit();
- ubifs_sysfs_exit();
- ubifs_compressors_exit();
- shrinker_free(ubifs_shrinker_info);
-
- /*
- * Make sure all delayed rcu free inodes are flushed before we
- * destroy cache.
- */
- rcu_barrier();
- kmem_cache_destroy(ubifs_inode_slab);
- unregister_filesystem(&ubifs_fs_type);
-}
-module_exit(ubifs_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION(__stringify(UBIFS_VERSION));
-MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");
-MODULE_DESCRIPTION("UBIFS - UBI File System");
--
2.13.6
Adapt debug subsystem(debug.c, debug.h) in libubifs, compared with
linux kernel implementations:
1. Only keep the disk data and space statistics dumping implementations,
dbg_walk_index and add_size which are used by fsck, other debuging
related implementations and sysfs interfaces are removed, because
fsck will check fs in another way.
2. Change the behavior of ubifs_assert_failed(), make filesystem
readonly when assertion is failed.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/debug.c | 2090 +-----------------------------------------
ubifs-utils/libubifs/debug.h | 259 ++----
ubifs-utils/libubifs/ubifs.h | 2 -
3 files changed, 95 insertions(+), 2256 deletions(-)
diff --git a/ubifs-utils/libubifs/debug.c b/ubifs-utils/libubifs/debug.c
index ac77ac1f..a2109906 100644
--- a/ubifs-utils/libubifs/debug.c
+++ b/ubifs-utils/libubifs/debug.c
@@ -15,13 +15,17 @@
* various local functions of those subsystems.
*/
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/math64.h>
-#include <linux/uaccess.h>
-#include <linux/random.h>
-#include <linux/ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
static DEFINE_SPINLOCK(dbg_lock);
@@ -221,76 +225,6 @@ static void dump_ch(const struct ubifs_ch *ch)
pr_err("\tlen %u\n", le32_to_cpu(ch->len));
}
-void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
-{
- const struct ubifs_inode *ui = ubifs_inode(inode);
- struct fscrypt_name nm = {0};
- union ubifs_key key;
- struct ubifs_dent_node *dent, *pdent = NULL;
- int count = 2;
-
- pr_err("Dump in-memory inode:");
- pr_err("\tinode %lu\n", inode->i_ino);
- pr_err("\tsize %llu\n",
- (unsigned long long)i_size_read(inode));
- pr_err("\tnlink %u\n", inode->i_nlink);
- pr_err("\tuid %u\n", (unsigned int)i_uid_read(inode));
- pr_err("\tgid %u\n", (unsigned int)i_gid_read(inode));
- pr_err("\tatime %u.%u\n",
- (unsigned int) inode_get_atime_sec(inode),
- (unsigned int) inode_get_atime_nsec(inode));
- pr_err("\tmtime %u.%u\n",
- (unsigned int) inode_get_mtime_sec(inode),
- (unsigned int) inode_get_mtime_nsec(inode));
- pr_err("\tctime %u.%u\n",
- (unsigned int) inode_get_ctime_sec(inode),
- (unsigned int) inode_get_ctime_nsec(inode));
- pr_err("\tcreat_sqnum %llu\n", ui->creat_sqnum);
- pr_err("\txattr_size %u\n", ui->xattr_size);
- pr_err("\txattr_cnt %u\n", ui->xattr_cnt);
- pr_err("\txattr_names %u\n", ui->xattr_names);
- pr_err("\tdirty %u\n", ui->dirty);
- pr_err("\txattr %u\n", ui->xattr);
- pr_err("\tbulk_read %u\n", ui->bulk_read);
- pr_err("\tsynced_i_size %llu\n",
- (unsigned long long)ui->synced_i_size);
- pr_err("\tui_size %llu\n",
- (unsigned long long)ui->ui_size);
- pr_err("\tflags %d\n", ui->flags);
- pr_err("\tcompr_type %d\n", ui->compr_type);
- pr_err("\tlast_page_read %lu\n", ui->last_page_read);
- pr_err("\tread_in_a_row %lu\n", ui->read_in_a_row);
- pr_err("\tdata_len %d\n", ui->data_len);
-
- if (!S_ISDIR(inode->i_mode))
- return;
-
- pr_err("List of directory entries:\n");
- ubifs_assert(c, !mutex_is_locked(&c->tnc_mutex));
-
- lowest_dent_key(c, &key, inode->i_ino);
- while (1) {
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- if (PTR_ERR(dent) != -ENOENT)
- pr_err("error %ld\n", PTR_ERR(dent));
- break;
- }
-
- pr_err("\t%d: inode %llu, type %s, len %d\n",
- count++, (unsigned long long) le64_to_cpu(dent->inum),
- get_dent_type(dent->type),
- le16_to_cpu(dent->nlen));
-
- fname_name(&nm) = dent->name;
- fname_len(&nm) = le16_to_cpu(dent->nlen);
- kfree(pdent);
- pdent = dent;
- key_read(c, &dent->key, &key);
- }
- kfree(pdent);
-}
-
void ubifs_dump_node(const struct ubifs_info *c, const void *node, int node_len)
{
int i, n, type, safe_len, max_node_len, min_node_len;
@@ -301,7 +235,7 @@ void ubifs_dump_node(const struct ubifs_info *c, const void *node, int node_len)
/* If the magic is incorrect, just hexdump the first bytes */
if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) {
pr_err("Not a node, first %zu bytes:", UBIFS_CH_SZ);
- print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 32, 1,
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 1,
(void *)node, UBIFS_CH_SZ, 1);
return;
}
@@ -331,7 +265,7 @@ void ubifs_dump_node(const struct ubifs_info *c, const void *node, int node_len)
safe_len > UBIFS_CH_SZ ?
safe_len - (int)UBIFS_CH_SZ : 0);
if (safe_len > UBIFS_CH_SZ)
- print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 32, 1,
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 1,
(void *)node + UBIFS_CH_SZ,
safe_len - UBIFS_CH_SZ, 0);
goto out_unlock;
@@ -509,7 +443,7 @@ void ubifs_dump_node(const struct ubifs_info *c, const void *node, int node_len)
le32_to_cpu(ch->len) - (unsigned int)UBIFS_DATA_NODE_SZ);
pr_err("\tdata (length = %d):\n",
safe_len - (int)UBIFS_DATA_NODE_SZ);
- print_hex_dump(KERN_ERR, "\t", DUMP_PREFIX_OFFSET, 32, 1,
+ print_hex_dump("\t", DUMP_PREFIX_OFFSET, 32, 1,
(void *)&dn->data,
safe_len - (int)UBIFS_DATA_NODE_SZ, 0);
break;
@@ -580,28 +514,11 @@ out_unlock:
spin_unlock(&dbg_lock);
}
-void ubifs_dump_budget_req(const struct ubifs_budget_req *req)
-{
- spin_lock(&dbg_lock);
- pr_err("Budgeting request: new_ino %d, dirtied_ino %d\n",
- req->new_ino, req->dirtied_ino);
- pr_err("\tnew_ino_d %d, dirtied_ino_d %d\n",
- req->new_ino_d, req->dirtied_ino_d);
- pr_err("\tnew_page %d, dirtied_page %d\n",
- req->new_page, req->dirtied_page);
- pr_err("\tnew_dent %d, mod_dent %d\n",
- req->new_dent, req->mod_dent);
- pr_err("\tidx_growth %d\n", req->idx_growth);
- pr_err("\tdata_growth %d dd_growth %d\n",
- req->data_growth, req->dd_growth);
- spin_unlock(&dbg_lock);
-}
-
void ubifs_dump_lstats(const struct ubifs_lp_stats *lst)
{
spin_lock(&dbg_lock);
pr_err("(pid %d) Lprops statistics: empty_lebs %d, idx_lebs %d\n",
- current->pid, lst->empty_lebs, lst->idx_lebs);
+ getpid(), lst->empty_lebs, lst->idx_lebs);
pr_err("\ttaken_empty_lebs %d, total_free %lld, total_dirty %lld\n",
lst->taken_empty_lebs, lst->total_free, lst->total_dirty);
pr_err("\ttotal_used %lld, total_dark %lld, total_dead %lld\n",
@@ -620,7 +537,7 @@ void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
spin_lock(&c->space_lock);
spin_lock(&dbg_lock);
pr_err("(pid %d) Budgeting info: data budget sum %lld, total budget sum %lld\n",
- current->pid, bi->data_growth + bi->dd_growth,
+ getpid(), bi->data_growth + bi->dd_growth,
bi->data_growth + bi->dd_growth + bi->idx_growth);
pr_err("\tbudg_data_growth %lld, budg_dd_growth %lld, budg_idx_growth %lld\n",
bi->data_growth, bi->dd_growth, bi->idx_growth);
@@ -774,7 +691,7 @@ void ubifs_dump_lprops(struct ubifs_info *c)
struct ubifs_lprops lp;
struct ubifs_lp_stats lst;
- pr_err("(pid %d) start dumping LEB properties\n", current->pid);
+ pr_err("(pid %d) start dumping LEB properties\n", getpid());
ubifs_get_lp_stats(c, &lst);
ubifs_dump_lstats(&lst);
@@ -787,7 +704,7 @@ void ubifs_dump_lprops(struct ubifs_info *c)
ubifs_dump_lprop(c, &lp);
}
- pr_err("(pid %d) finish dumping LEB properties\n", current->pid);
+ pr_err("(pid %d) finish dumping LEB properties\n", getpid());
}
void ubifs_dump_lpt_info(struct ubifs_info *c)
@@ -795,7 +712,7 @@ void ubifs_dump_lpt_info(struct ubifs_info *c)
int i;
spin_lock(&dbg_lock);
- pr_err("(pid %d) dumping LPT information\n", current->pid);
+ pr_err("(pid %d) dumping LPT information\n", getpid());
pr_err("\tlpt_sz: %lld\n", c->lpt_sz);
pr_err("\tpnode_sz: %d\n", c->pnode_sz);
pr_err("\tnnode_sz: %d\n", c->nnode_sz);
@@ -834,7 +751,7 @@ void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
struct ubifs_scan_node *snod;
void *buf;
- pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+ pr_err("(pid %d) start dumping LEB %d\n", getpid(), lnum);
buf = __vmalloc(c->leb_size, GFP_NOFS);
if (!buf) {
@@ -858,7 +775,7 @@ void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
}
- pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+ pr_err("(pid %d) finish dumping LEB %d\n", getpid(), lnum);
ubifs_scan_destroy(sleb);
out:
@@ -905,12 +822,13 @@ void ubifs_dump_znode(const struct ubifs_info *c,
spin_unlock(&dbg_lock);
}
-void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
+void ubifs_dump_heap(__unused struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ int cat)
{
int i;
pr_err("(pid %d) start dumping heap cat %d (%d elements)\n",
- current->pid, cat, heap->cnt);
+ getpid(), cat, heap->cnt);
for (i = 0; i < heap->cnt; i++) {
struct ubifs_lprops *lprops = heap->arr[i];
@@ -918,15 +836,15 @@ void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
i, lprops->lnum, lprops->hpos, lprops->free,
lprops->dirty, lprops->flags);
}
- pr_err("(pid %d) finish dumping heap\n", current->pid);
+ pr_err("(pid %d) finish dumping heap\n", getpid());
}
-void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+void ubifs_dump_pnode(__unused struct ubifs_info *c, struct ubifs_pnode *pnode,
struct ubifs_nnode *parent, int iip)
{
int i;
- pr_err("(pid %d) dumping pnode:\n", current->pid);
+ pr_err("(pid %d) dumping pnode:\n", getpid());
pr_err("\taddress %zx parent %zx cnext %zx\n",
(size_t)pnode, (size_t)parent, (size_t)pnode->cnext);
pr_err("\tflags %lu iip %d level %d num %d\n",
@@ -939,632 +857,6 @@ void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
}
}
-void ubifs_dump_tnc(struct ubifs_info *c)
-{
- struct ubifs_znode *znode;
- int level;
-
- pr_err("\n");
- pr_err("(pid %d) start dumping TNC tree\n", current->pid);
- znode = ubifs_tnc_levelorder_next(c, c->zroot.znode, NULL);
- level = znode->level;
- pr_err("== Level %d ==\n", level);
- while (znode) {
- if (level != znode->level) {
- level = znode->level;
- pr_err("== Level %d ==\n", level);
- }
- ubifs_dump_znode(c, znode);
- znode = ubifs_tnc_levelorder_next(c, c->zroot.znode, znode);
- }
- pr_err("(pid %d) finish dumping TNC tree\n", current->pid);
-}
-
-static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode,
- void *priv)
-{
- ubifs_dump_znode(c, znode);
- return 0;
-}
-
-/**
- * ubifs_dump_index - dump the on-flash index.
- * @c: UBIFS file-system description object
- *
- * This function dumps whole UBIFS indexing B-tree, unlike 'ubifs_dump_tnc()'
- * which dumps only in-memory znodes and does not read znodes which from flash.
- */
-void ubifs_dump_index(struct ubifs_info *c)
-{
- dbg_walk_index(c, NULL, dump_znode, NULL);
-}
-
-/**
- * dbg_save_space_info - save information about flash space.
- * @c: UBIFS file-system description object
- *
- * This function saves information about UBIFS free space, dirty space, etc, in
- * order to check it later.
- */
-void dbg_save_space_info(struct ubifs_info *c)
-{
- struct ubifs_debug_info *d = c->dbg;
- int freeable_cnt;
-
- spin_lock(&c->space_lock);
- memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
- memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info));
- d->saved_idx_gc_cnt = c->idx_gc_cnt;
-
- /*
- * We use a dirty hack here and zero out @c->freeable_cnt, because it
- * affects the free space calculations, and UBIFS might not know about
- * all freeable eraseblocks. Indeed, we know about freeable eraseblocks
- * only when we read their lprops, and we do this only lazily, upon the
- * need. So at any given point of time @c->freeable_cnt might be not
- * exactly accurate.
- *
- * Just one example about the issue we hit when we did not zero
- * @c->freeable_cnt.
- * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the
- * amount of free space in @d->saved_free
- * 2. We re-mount R/W, which makes UBIFS to read the "lsave"
- * information from flash, where we cache LEBs from various
- * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()'
- * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()'
- * -> 'ubifs_get_pnode()' -> 'update_cats()'
- * -> 'ubifs_add_to_cat()').
- * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt
- * becomes %1.
- * 4. We calculate the amount of free space when the re-mount is
- * finished in 'dbg_check_space_info()' and it does not match
- * @d->saved_free.
- */
- freeable_cnt = c->freeable_cnt;
- c->freeable_cnt = 0;
- d->saved_free = ubifs_get_free_space_nolock(c);
- c->freeable_cnt = freeable_cnt;
- spin_unlock(&c->space_lock);
-}
-
-/**
- * dbg_check_space_info - check flash space information.
- * @c: UBIFS file-system description object
- *
- * This function compares current flash space information with the information
- * which was saved when the 'dbg_save_space_info()' function was called.
- * Returns zero if the information has not changed, and %-EINVAL if it has
- * changed.
- */
-int dbg_check_space_info(struct ubifs_info *c)
-{
- struct ubifs_debug_info *d = c->dbg;
- struct ubifs_lp_stats lst;
- long long free;
- int freeable_cnt;
-
- spin_lock(&c->space_lock);
- freeable_cnt = c->freeable_cnt;
- c->freeable_cnt = 0;
- free = ubifs_get_free_space_nolock(c);
- c->freeable_cnt = freeable_cnt;
- spin_unlock(&c->space_lock);
-
- if (free != d->saved_free) {
- ubifs_err(c, "free space changed from %lld to %lld",
- d->saved_free, free);
- goto out;
- }
-
- return 0;
-
-out:
- ubifs_msg(c, "saved lprops statistics dump");
- ubifs_dump_lstats(&d->saved_lst);
- ubifs_msg(c, "saved budgeting info dump");
- ubifs_dump_budg(c, &d->saved_bi);
- ubifs_msg(c, "saved idx_gc_cnt %d", d->saved_idx_gc_cnt);
- ubifs_msg(c, "current lprops statistics dump");
- ubifs_get_lp_stats(c, &lst);
- ubifs_dump_lstats(&lst);
- ubifs_msg(c, "current budgeting info dump");
- ubifs_dump_budg(c, &c->bi);
- dump_stack();
- return -EINVAL;
-}
-
-/**
- * dbg_check_synced_i_size - check synchronized inode size.
- * @c: UBIFS file-system description object
- * @inode: inode to check
- *
- * If inode is clean, synchronized inode size has to be equivalent to current
- * inode size. This function has to be called only for locked inodes (@i_mutex
- * has to be locked). Returns %0 if synchronized inode size if correct, and
- * %-EINVAL if not.
- */
-int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
-{
- int err = 0;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- if (!dbg_is_chk_gen(c))
- return 0;
- if (!S_ISREG(inode->i_mode))
- return 0;
-
- mutex_lock(&ui->ui_mutex);
- spin_lock(&ui->ui_lock);
- if (ui->ui_size != ui->synced_i_size && !ui->dirty) {
- ubifs_err(c, "ui_size is %lld, synced_i_size is %lld, but inode is clean",
- ui->ui_size, ui->synced_i_size);
- ubifs_err(c, "i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino,
- inode->i_mode, i_size_read(inode));
- dump_stack();
- err = -EINVAL;
- }
- spin_unlock(&ui->ui_lock);
- mutex_unlock(&ui->ui_mutex);
- return err;
-}
-
-/*
- * dbg_check_dir - check directory inode size and link count.
- * @c: UBIFS file-system description object
- * @dir: the directory to calculate size for
- * @size: the result is returned here
- *
- * This function makes sure that directory size and link count are correct.
- * Returns zero in case of success and a negative error code in case of
- * failure.
- *
- * Note, it is good idea to make sure the @dir->i_mutex is locked before
- * calling this function.
- */
-int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
-{
- unsigned int nlink = 2;
- union ubifs_key key;
- struct ubifs_dent_node *dent, *pdent = NULL;
- struct fscrypt_name nm = {0};
- loff_t size = UBIFS_INO_NODE_SZ;
-
- if (!dbg_is_chk_gen(c))
- return 0;
-
- if (!S_ISDIR(dir->i_mode))
- return 0;
-
- lowest_dent_key(c, &key, dir->i_ino);
- while (1) {
- int err;
-
- dent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(dent)) {
- err = PTR_ERR(dent);
- if (err == -ENOENT)
- break;
- kfree(pdent);
- return err;
- }
-
- fname_name(&nm) = dent->name;
- fname_len(&nm) = le16_to_cpu(dent->nlen);
- size += CALC_DENT_SIZE(fname_len(&nm));
- if (dent->type == UBIFS_ITYPE_DIR)
- nlink += 1;
- kfree(pdent);
- pdent = dent;
- key_read(c, &dent->key, &key);
- }
- kfree(pdent);
-
- if (i_size_read(dir) != size) {
- ubifs_err(c, "directory inode %lu has size %llu, but calculated size is %llu",
- dir->i_ino, (unsigned long long)i_size_read(dir),
- (unsigned long long)size);
- ubifs_dump_inode(c, dir);
- dump_stack();
- return -EINVAL;
- }
- if (dir->i_nlink != nlink) {
- ubifs_err(c, "directory inode %lu has nlink %u, but calculated nlink is %u",
- dir->i_ino, dir->i_nlink, nlink);
- ubifs_dump_inode(c, dir);
- dump_stack();
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * dbg_check_key_order - make sure that colliding keys are properly ordered.
- * @c: UBIFS file-system description object
- * @zbr1: first zbranch
- * @zbr2: following zbranch
- *
- * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of
- * names of the direntries/xentries which are referred by the keys. This
- * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes
- * sure the name of direntry/xentry referred by @zbr1 is less than
- * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not,
- * and a negative error code in case of failure.
- */
-static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
- struct ubifs_zbranch *zbr2)
-{
- int err, nlen1, nlen2, cmp;
- struct ubifs_dent_node *dent1, *dent2;
- union ubifs_key key;
- char key_buf[DBG_KEY_BUF_LEN];
-
- ubifs_assert(c, !keys_cmp(c, &zbr1->key, &zbr2->key));
- dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
- if (!dent1)
- return -ENOMEM;
- dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
- if (!dent2) {
- err = -ENOMEM;
- goto out_free;
- }
-
- err = ubifs_tnc_read_node(c, zbr1, dent1);
- if (err)
- goto out_free;
- err = ubifs_validate_entry(c, dent1);
- if (err)
- goto out_free;
-
- err = ubifs_tnc_read_node(c, zbr2, dent2);
- if (err)
- goto out_free;
- err = ubifs_validate_entry(c, dent2);
- if (err)
- goto out_free;
-
- /* Make sure node keys are the same as in zbranch */
- err = 1;
- key_read(c, &dent1->key, &key);
- if (keys_cmp(c, &zbr1->key, &key)) {
- ubifs_err(c, "1st entry at %d:%d has key %s", zbr1->lnum,
- zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
- DBG_KEY_BUF_LEN));
- ubifs_err(c, "but it should have key %s according to tnc",
- dbg_snprintf_key(c, &zbr1->key, key_buf,
- DBG_KEY_BUF_LEN));
- ubifs_dump_node(c, dent1, UBIFS_MAX_DENT_NODE_SZ);
- goto out_free;
- }
-
- key_read(c, &dent2->key, &key);
- if (keys_cmp(c, &zbr2->key, &key)) {
- ubifs_err(c, "2nd entry at %d:%d has key %s", zbr1->lnum,
- zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
- DBG_KEY_BUF_LEN));
- ubifs_err(c, "but it should have key %s according to tnc",
- dbg_snprintf_key(c, &zbr2->key, key_buf,
- DBG_KEY_BUF_LEN));
- ubifs_dump_node(c, dent2, UBIFS_MAX_DENT_NODE_SZ);
- goto out_free;
- }
-
- nlen1 = le16_to_cpu(dent1->nlen);
- nlen2 = le16_to_cpu(dent2->nlen);
-
- cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2));
- if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) {
- err = 0;
- goto out_free;
- }
- if (cmp == 0 && nlen1 == nlen2)
- ubifs_err(c, "2 xent/dent nodes with the same name");
- else
- ubifs_err(c, "bad order of colliding key %s",
- dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
-
- ubifs_msg(c, "first node at %d:%d\n", zbr1->lnum, zbr1->offs);
- ubifs_dump_node(c, dent1, UBIFS_MAX_DENT_NODE_SZ);
- ubifs_msg(c, "second node at %d:%d\n", zbr2->lnum, zbr2->offs);
- ubifs_dump_node(c, dent2, UBIFS_MAX_DENT_NODE_SZ);
-
-out_free:
- kfree(dent2);
- kfree(dent1);
- return err;
-}
-
-/**
- * dbg_check_znode - check if znode is all right.
- * @c: UBIFS file-system description object
- * @zbr: zbranch which points to this znode
- *
- * This function makes sure that znode referred to by @zbr is all right.
- * Returns zero if it is, and %-EINVAL if it is not.
- */
-static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr)
-{
- struct ubifs_znode *znode = zbr->znode;
- struct ubifs_znode *zp = znode->parent;
- int n, err, cmp;
-
- if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
- err = 1;
- goto out;
- }
- if (znode->level < 0) {
- err = 2;
- goto out;
- }
- if (znode->iip < 0 || znode->iip >= c->fanout) {
- err = 3;
- goto out;
- }
-
- if (zbr->len == 0)
- /* Only dirty zbranch may have no on-flash nodes */
- if (!ubifs_zn_dirty(znode)) {
- err = 4;
- goto out;
- }
-
- if (ubifs_zn_dirty(znode)) {
- /*
- * If znode is dirty, its parent has to be dirty as well. The
- * order of the operation is important, so we have to have
- * memory barriers.
- */
- smp_mb();
- if (zp && !ubifs_zn_dirty(zp)) {
- /*
- * The dirty flag is atomic and is cleared outside the
- * TNC mutex, so znode's dirty flag may now have
- * been cleared. The child is always cleared before the
- * parent, so we just need to check again.
- */
- smp_mb();
- if (ubifs_zn_dirty(znode)) {
- err = 5;
- goto out;
- }
- }
- }
-
- if (zp) {
- const union ubifs_key *min, *max;
-
- if (znode->level != zp->level - 1) {
- err = 6;
- goto out;
- }
-
- /* Make sure the 'parent' pointer in our znode is correct */
- err = ubifs_search_zbranch(c, zp, &zbr->key, &n);
- if (!err) {
- /* This zbranch does not exist in the parent */
- err = 7;
- goto out;
- }
-
- if (znode->iip >= zp->child_cnt) {
- err = 8;
- goto out;
- }
-
- if (znode->iip != n) {
- /* This may happen only in case of collisions */
- if (keys_cmp(c, &zp->zbranch[n].key,
- &zp->zbranch[znode->iip].key)) {
- err = 9;
- goto out;
- }
- n = znode->iip;
- }
-
- /*
- * Make sure that the first key in our znode is greater than or
- * equal to the key in the pointing zbranch.
- */
- min = &zbr->key;
- cmp = keys_cmp(c, min, &znode->zbranch[0].key);
- if (cmp == 1) {
- err = 10;
- goto out;
- }
-
- if (n + 1 < zp->child_cnt) {
- max = &zp->zbranch[n + 1].key;
-
- /*
- * Make sure the last key in our znode is less or
- * equivalent than the key in the zbranch which goes
- * after our pointing zbranch.
- */
- cmp = keys_cmp(c, max,
- &znode->zbranch[znode->child_cnt - 1].key);
- if (cmp == -1) {
- err = 11;
- goto out;
- }
- }
- } else {
- /* This may only be root znode */
- if (zbr != &c->zroot) {
- err = 12;
- goto out;
- }
- }
-
- /*
- * Make sure that next key is greater or equivalent then the previous
- * one.
- */
- for (n = 1; n < znode->child_cnt; n++) {
- cmp = keys_cmp(c, &znode->zbranch[n - 1].key,
- &znode->zbranch[n].key);
- if (cmp > 0) {
- err = 13;
- goto out;
- }
- if (cmp == 0) {
- /* This can only be keys with colliding hash */
- if (!is_hash_key(c, &znode->zbranch[n].key)) {
- err = 14;
- goto out;
- }
-
- if (znode->level != 0 || c->replaying)
- continue;
-
- /*
- * Colliding keys should follow binary order of
- * corresponding xentry/dentry names.
- */
- err = dbg_check_key_order(c, &znode->zbranch[n - 1],
- &znode->zbranch[n]);
- if (err < 0)
- return err;
- if (err) {
- err = 15;
- goto out;
- }
- }
- }
-
- for (n = 0; n < znode->child_cnt; n++) {
- if (!znode->zbranch[n].znode &&
- (znode->zbranch[n].lnum == 0 ||
- znode->zbranch[n].len == 0)) {
- err = 16;
- goto out;
- }
-
- if (znode->zbranch[n].lnum != 0 &&
- znode->zbranch[n].len == 0) {
- err = 17;
- goto out;
- }
-
- if (znode->zbranch[n].lnum == 0 &&
- znode->zbranch[n].len != 0) {
- err = 18;
- goto out;
- }
-
- if (znode->zbranch[n].lnum == 0 &&
- znode->zbranch[n].offs != 0) {
- err = 19;
- goto out;
- }
-
- if (znode->level != 0 && znode->zbranch[n].znode)
- if (znode->zbranch[n].znode->parent != znode) {
- err = 20;
- goto out;
- }
- }
-
- return 0;
-
-out:
- ubifs_err(c, "failed, error %d", err);
- ubifs_msg(c, "dump of the znode");
- ubifs_dump_znode(c, znode);
- if (zp) {
- ubifs_msg(c, "dump of the parent znode");
- ubifs_dump_znode(c, zp);
- }
- dump_stack();
- return -EINVAL;
-}
-
-/**
- * dbg_check_tnc - check TNC tree.
- * @c: UBIFS file-system description object
- * @extra: do extra checks that are possible at start commit
- *
- * This function traverses whole TNC tree and checks every znode. Returns zero
- * if everything is all right and %-EINVAL if something is wrong with TNC.
- */
-int dbg_check_tnc(struct ubifs_info *c, int extra)
-{
- struct ubifs_znode *znode;
- long clean_cnt = 0, dirty_cnt = 0;
- int err, last;
-
- if (!dbg_is_chk_index(c))
- return 0;
-
- ubifs_assert(c, mutex_is_locked(&c->tnc_mutex));
- if (!c->zroot.znode)
- return 0;
-
- znode = ubifs_tnc_postorder_first(c->zroot.znode);
- while (1) {
- struct ubifs_znode *prev;
- struct ubifs_zbranch *zbr;
-
- if (!znode->parent)
- zbr = &c->zroot;
- else
- zbr = &znode->parent->zbranch[znode->iip];
-
- err = dbg_check_znode(c, zbr);
- if (err)
- return err;
-
- if (extra) {
- if (ubifs_zn_dirty(znode))
- dirty_cnt += 1;
- else
- clean_cnt += 1;
- }
-
- prev = znode;
- znode = ubifs_tnc_postorder_next(c, znode);
- if (!znode)
- break;
-
- /*
- * If the last key of this znode is equivalent to the first key
- * of the next znode (collision), then check order of the keys.
- */
- last = prev->child_cnt - 1;
- if (prev->level == 0 && znode->level == 0 && !c->replaying &&
- !keys_cmp(c, &prev->zbranch[last].key,
- &znode->zbranch[0].key)) {
- err = dbg_check_key_order(c, &prev->zbranch[last],
- &znode->zbranch[0]);
- if (err < 0)
- return err;
- if (err) {
- ubifs_msg(c, "first znode");
- ubifs_dump_znode(c, prev);
- ubifs_msg(c, "second znode");
- ubifs_dump_znode(c, znode);
- return -EINVAL;
- }
- }
- }
-
- if (extra) {
- if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) {
- ubifs_err(c, "incorrect clean_zn_cnt %ld, calculated %ld",
- atomic_long_read(&c->clean_zn_cnt),
- clean_cnt);
- return -EINVAL;
- }
- if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) {
- ubifs_err(c, "incorrect dirty_zn_cnt %ld, calculated %ld",
- atomic_long_read(&c->dirty_zn_cnt),
- dirty_cnt);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
/**
* dbg_walk_index - walk the on-flash index.
* @c: UBIFS file-system description object
@@ -1711,7 +1003,7 @@ out_unlock:
* every indexing node and adds its size to the 'long long' variable pointed to
* by @priv.
*/
-static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
+int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
{
long long *idx_size = priv;
int add;
@@ -1722,1330 +1014,14 @@ static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
return 0;
}
-/**
- * dbg_check_idx_size - check index size.
- * @c: UBIFS file-system description object
- * @idx_size: size to check
- *
- * This function walks the UBIFS index, calculates its size and checks that the
- * size is equivalent to @idx_size. Returns zero in case of success and a
- * negative error code in case of failure.
- */
-int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
+void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
+ const char *file, int line)
{
- int err;
- long long calc = 0;
-
- if (!dbg_is_chk_index(c))
- return 0;
-
- err = dbg_walk_index(c, NULL, add_size, &calc);
- if (err) {
- ubifs_err(c, "error %d while walking the index", err);
- goto out_err;
- }
-
- if (calc != idx_size) {
- ubifs_err(c, "index size check failed: calculated size is %lld, should be %lld",
- calc, idx_size);
- dump_stack();
- err = -EINVAL;
- goto out_err;
- }
-
- return 0;
-
-out_err:
- ubifs_destroy_tnc_tree(c);
- return err;
-}
+ ubifs_err(c, "UBIFS assert failed: %s, in %s:%u", expr, file, line);
-/**
- * struct fsck_inode - information about an inode used when checking the file-system.
- * @rb: link in the RB-tree of inodes
- * @inum: inode number
- * @mode: inode type, permissions, etc
- * @nlink: inode link count
- * @xattr_cnt: count of extended attributes
- * @references: how many directory/xattr entries refer this inode (calculated
- * while walking the index)
- * @calc_cnt: for directory inode count of child directories
- * @size: inode size (read from on-flash inode)
- * @xattr_sz: summary size of all extended attributes (read from on-flash
- * inode)
- * @calc_sz: for directories calculated directory size
- * @calc_xcnt: count of extended attributes
- * @calc_xsz: calculated summary size of all extended attributes
- * @xattr_nms: sum of lengths of all extended attribute names belonging to this
- * inode (read from on-flash inode)
- * @calc_xnms: calculated sum of lengths of all extended attribute names
- */
-struct fsck_inode {
- struct rb_node rb;
- ino_t inum;
- umode_t mode;
- unsigned int nlink;
- unsigned int xattr_cnt;
- int references;
- int calc_cnt;
- long long size;
- unsigned int xattr_sz;
- long long calc_sz;
- long long calc_xcnt;
- long long calc_xsz;
- unsigned int xattr_nms;
- long long calc_xnms;
-};
-
-/**
- * struct fsck_data - private FS checking information.
- * @inodes: RB-tree of all inodes (contains @struct fsck_inode objects)
- */
-struct fsck_data {
- struct rb_root inodes;
-};
-
-/**
- * add_inode - add inode information to RB-tree of inodes.
- * @c: UBIFS file-system description object
- * @fsckd: FS checking information
- * @ino: raw UBIFS inode to add
- *
- * This is a helper function for 'check_leaf()' which adds information about
- * inode @ino to the RB-tree of inodes. Returns inode information pointer in
- * case of success and a negative error code in case of failure.
- */
-static struct fsck_inode *add_inode(struct ubifs_info *c,
- struct fsck_data *fsckd,
- struct ubifs_ino_node *ino)
-{
- struct rb_node **p, *parent = NULL;
- struct fsck_inode *fscki;
- ino_t inum = key_inum_flash(c, &ino->key);
- struct inode *inode;
- struct ubifs_inode *ui;
-
- p = &fsckd->inodes.rb_node;
- while (*p) {
- parent = *p;
- fscki = rb_entry(parent, struct fsck_inode, rb);
- if (inum < fscki->inum)
- p = &(*p)->rb_left;
- else if (inum > fscki->inum)
- p = &(*p)->rb_right;
- else
- return fscki;
- }
-
- if (inum > c->highest_inum) {
- ubifs_err(c, "too high inode number, max. is %lu",
- (unsigned long)c->highest_inum);
- return ERR_PTR(-EINVAL);
- }
-
- fscki = kzalloc(sizeof(struct fsck_inode), GFP_NOFS);
- if (!fscki)
- return ERR_PTR(-ENOMEM);
-
- inode = ilookup(c->vfs_sb, inum);
-
- fscki->inum = inum;
/*
- * If the inode is present in the VFS inode cache, use it instead of
- * the on-flash inode which might be out-of-date. E.g., the size might
- * be out-of-date. If we do not do this, the following may happen, for
- * example:
- * 1. A power cut happens
- * 2. We mount the file-system R/O, the replay process fixes up the
- * inode size in the VFS cache, but on on-flash.
- * 3. 'check_leaf()' fails because it hits a data node beyond inode
- * size.
+ * Different from linux kernel.
+ * There is only one action(readonly) when assertion is failed.
*/
- if (!inode) {
- fscki->nlink = le32_to_cpu(ino->nlink);
- fscki->size = le64_to_cpu(ino->size);
- fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
- fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
- fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
- fscki->mode = le32_to_cpu(ino->mode);
- } else {
- ui = ubifs_inode(inode);
- fscki->nlink = inode->i_nlink;
- fscki->size = inode->i_size;
- fscki->xattr_cnt = ui->xattr_cnt;
- fscki->xattr_sz = ui->xattr_size;
- fscki->xattr_nms = ui->xattr_names;
- fscki->mode = inode->i_mode;
- iput(inode);
- }
-
- if (S_ISDIR(fscki->mode)) {
- fscki->calc_sz = UBIFS_INO_NODE_SZ;
- fscki->calc_cnt = 2;
- }
-
- rb_link_node(&fscki->rb, parent, p);
- rb_insert_color(&fscki->rb, &fsckd->inodes);
-
- return fscki;
-}
-
-/**
- * search_inode - search inode in the RB-tree of inodes.
- * @fsckd: FS checking information
- * @inum: inode number to search
- *
- * This is a helper function for 'check_leaf()' which searches inode @inum in
- * the RB-tree of inodes and returns an inode information pointer or %NULL if
- * the inode was not found.
- */
-static struct fsck_inode *search_inode(struct fsck_data *fsckd, ino_t inum)
-{
- struct rb_node *p;
- struct fsck_inode *fscki;
-
- p = fsckd->inodes.rb_node;
- while (p) {
- fscki = rb_entry(p, struct fsck_inode, rb);
- if (inum < fscki->inum)
- p = p->rb_left;
- else if (inum > fscki->inum)
- p = p->rb_right;
- else
- return fscki;
- }
- return NULL;
-}
-
-/**
- * read_add_inode - read inode node and add it to RB-tree of inodes.
- * @c: UBIFS file-system description object
- * @fsckd: FS checking information
- * @inum: inode number to read
- *
- * This is a helper function for 'check_leaf()' which finds inode node @inum in
- * the index, reads it, and adds it to the RB-tree of inodes. Returns inode
- * information pointer in case of success and a negative error code in case of
- * failure.
- */
-static struct fsck_inode *read_add_inode(struct ubifs_info *c,
- struct fsck_data *fsckd, ino_t inum)
-{
- int n, err;
- union ubifs_key key;
- struct ubifs_znode *znode;
- struct ubifs_zbranch *zbr;
- struct ubifs_ino_node *ino;
- struct fsck_inode *fscki;
-
- fscki = search_inode(fsckd, inum);
- if (fscki)
- return fscki;
-
- ino_key_init(c, &key, inum);
- err = ubifs_lookup_level0(c, &key, &znode, &n);
- if (!err) {
- ubifs_err(c, "inode %lu not found in index", (unsigned long)inum);
- return ERR_PTR(-ENOENT);
- } else if (err < 0) {
- ubifs_err(c, "error %d while looking up inode %lu",
- err, (unsigned long)inum);
- return ERR_PTR(err);
- }
-
- zbr = &znode->zbranch[n];
- if (zbr->len < UBIFS_INO_NODE_SZ) {
- ubifs_err(c, "bad node %lu node length %d",
- (unsigned long)inum, zbr->len);
- return ERR_PTR(-EINVAL);
- }
-
- ino = kmalloc(zbr->len, GFP_NOFS);
- if (!ino)
- return ERR_PTR(-ENOMEM);
-
- err = ubifs_tnc_read_node(c, zbr, ino);
- if (err) {
- ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
- zbr->lnum, zbr->offs, err);
- kfree(ino);
- return ERR_PTR(err);
- }
-
- fscki = add_inode(c, fsckd, ino);
- kfree(ino);
- if (IS_ERR(fscki)) {
- ubifs_err(c, "error %ld while adding inode %lu node",
- PTR_ERR(fscki), (unsigned long)inum);
- return fscki;
- }
-
- return fscki;
-}
-
-/**
- * check_leaf - check leaf node.
- * @c: UBIFS file-system description object
- * @zbr: zbranch of the leaf node to check
- * @priv: FS checking information
- *
- * This is a helper function for 'dbg_check_filesystem()' which is called for
- * every single leaf node while walking the indexing tree. It checks that the
- * leaf node referred from the indexing tree exists, has correct CRC, and does
- * some other basic validation. This function is also responsible for building
- * an RB-tree of inodes - it adds all inodes into the RB-tree. It also
- * calculates reference count, size, etc for each inode in order to later
- * compare them to the information stored inside the inodes and detect possible
- * inconsistencies. Returns zero in case of success and a negative error code
- * in case of failure.
- */
-static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
- void *priv)
-{
- ino_t inum;
- void *node;
- struct ubifs_ch *ch;
- int err, type = key_type(c, &zbr->key);
- struct fsck_inode *fscki;
-
- if (zbr->len < UBIFS_CH_SZ) {
- ubifs_err(c, "bad leaf length %d (LEB %d:%d)",
- zbr->len, zbr->lnum, zbr->offs);
- return -EINVAL;
- }
-
- node = kmalloc(zbr->len, GFP_NOFS);
- if (!node)
- return -ENOMEM;
-
- err = ubifs_tnc_read_node(c, zbr, node);
- if (err) {
- ubifs_err(c, "cannot read leaf node at LEB %d:%d, error %d",
- zbr->lnum, zbr->offs, err);
- goto out_free;
- }
-
- /* If this is an inode node, add it to RB-tree of inodes */
- if (type == UBIFS_INO_KEY) {
- fscki = add_inode(c, priv, node);
- if (IS_ERR(fscki)) {
- err = PTR_ERR(fscki);
- ubifs_err(c, "error %d while adding inode node", err);
- goto out_dump;
- }
- goto out;
- }
-
- if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY &&
- type != UBIFS_DATA_KEY) {
- ubifs_err(c, "unexpected node type %d at LEB %d:%d",
- type, zbr->lnum, zbr->offs);
- err = -EINVAL;
- goto out_free;
- }
-
- ch = node;
- if (le64_to_cpu(ch->sqnum) > c->max_sqnum) {
- ubifs_err(c, "too high sequence number, max. is %llu",
- c->max_sqnum);
- err = -EINVAL;
- goto out_dump;
- }
-
- if (type == UBIFS_DATA_KEY) {
- long long blk_offs;
- struct ubifs_data_node *dn = node;
-
- ubifs_assert(c, zbr->len >= UBIFS_DATA_NODE_SZ);
-
- /*
- * Search the inode node this data node belongs to and insert
- * it to the RB-tree of inodes.
- */
- inum = key_inum_flash(c, &dn->key);
- fscki = read_add_inode(c, priv, inum);
- if (IS_ERR(fscki)) {
- err = PTR_ERR(fscki);
- ubifs_err(c, "error %d while processing data node and trying to find inode node %lu",
- err, (unsigned long)inum);
- goto out_dump;
- }
-
- /* Make sure the data node is within inode size */
- blk_offs = key_block_flash(c, &dn->key);
- blk_offs <<= UBIFS_BLOCK_SHIFT;
- blk_offs += le32_to_cpu(dn->size);
- if (blk_offs > fscki->size) {
- ubifs_err(c, "data node at LEB %d:%d is not within inode size %lld",
- zbr->lnum, zbr->offs, fscki->size);
- err = -EINVAL;
- goto out_dump;
- }
- } else {
- int nlen;
- struct ubifs_dent_node *dent = node;
- struct fsck_inode *fscki1;
-
- ubifs_assert(c, zbr->len >= UBIFS_DENT_NODE_SZ);
-
- err = ubifs_validate_entry(c, dent);
- if (err)
- goto out_dump;
-
- /*
- * Search the inode node this entry refers to and the parent
- * inode node and insert them to the RB-tree of inodes.
- */
- inum = le64_to_cpu(dent->inum);
- fscki = read_add_inode(c, priv, inum);
- if (IS_ERR(fscki)) {
- err = PTR_ERR(fscki);
- ubifs_err(c, "error %d while processing entry node and trying to find inode node %lu",
- err, (unsigned long)inum);
- goto out_dump;
- }
-
- /* Count how many direntries or xentries refers this inode */
- fscki->references += 1;
-
- inum = key_inum_flash(c, &dent->key);
- fscki1 = read_add_inode(c, priv, inum);
- if (IS_ERR(fscki1)) {
- err = PTR_ERR(fscki1);
- ubifs_err(c, "error %d while processing entry node and trying to find parent inode node %lu",
- err, (unsigned long)inum);
- goto out_dump;
- }
-
- nlen = le16_to_cpu(dent->nlen);
- if (type == UBIFS_XENT_KEY) {
- fscki1->calc_xcnt += 1;
- fscki1->calc_xsz += CALC_DENT_SIZE(nlen);
- fscki1->calc_xsz += CALC_XATTR_BYTES(fscki->size);
- fscki1->calc_xnms += nlen;
- } else {
- fscki1->calc_sz += CALC_DENT_SIZE(nlen);
- if (dent->type == UBIFS_ITYPE_DIR)
- fscki1->calc_cnt += 1;
- }
- }
-
-out:
- kfree(node);
- return 0;
-
-out_dump:
- ubifs_msg(c, "dump of node at LEB %d:%d", zbr->lnum, zbr->offs);
- ubifs_dump_node(c, node, zbr->len);
-out_free:
- kfree(node);
- return err;
-}
-
-/**
- * free_inodes - free RB-tree of inodes.
- * @fsckd: FS checking information
- */
-static void free_inodes(struct fsck_data *fsckd)
-{
- struct fsck_inode *fscki, *n;
-
- rbtree_postorder_for_each_entry_safe(fscki, n, &fsckd->inodes, rb)
- kfree(fscki);
-}
-
-/**
- * check_inodes - checks all inodes.
- * @c: UBIFS file-system description object
- * @fsckd: FS checking information
- *
- * This is a helper function for 'dbg_check_filesystem()' which walks the
- * RB-tree of inodes after the index scan has been finished, and checks that
- * inode nlink, size, etc are correct. Returns zero if inodes are fine,
- * %-EINVAL if not, and a negative error code in case of failure.
- */
-static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd)
-{
- int n, err;
- union ubifs_key key;
- struct ubifs_znode *znode;
- struct ubifs_zbranch *zbr;
- struct ubifs_ino_node *ino;
- struct fsck_inode *fscki;
- struct rb_node *this = rb_first(&fsckd->inodes);
-
- while (this) {
- fscki = rb_entry(this, struct fsck_inode, rb);
- this = rb_next(this);
-
- if (S_ISDIR(fscki->mode)) {
- /*
- * Directories have to have exactly one reference (they
- * cannot have hardlinks), although root inode is an
- * exception.
- */
- if (fscki->inum != UBIFS_ROOT_INO &&
- fscki->references != 1) {
- ubifs_err(c, "directory inode %lu has %d direntries which refer it, but should be 1",
- (unsigned long)fscki->inum,
- fscki->references);
- goto out_dump;
- }
- if (fscki->inum == UBIFS_ROOT_INO &&
- fscki->references != 0) {
- ubifs_err(c, "root inode %lu has non-zero (%d) direntries which refer it",
- (unsigned long)fscki->inum,
- fscki->references);
- goto out_dump;
- }
- if (fscki->calc_sz != fscki->size) {
- ubifs_err(c, "directory inode %lu size is %lld, but calculated size is %lld",
- (unsigned long)fscki->inum,
- fscki->size, fscki->calc_sz);
- goto out_dump;
- }
- if (fscki->calc_cnt != fscki->nlink) {
- ubifs_err(c, "directory inode %lu nlink is %d, but calculated nlink is %d",
- (unsigned long)fscki->inum,
- fscki->nlink, fscki->calc_cnt);
- goto out_dump;
- }
- } else {
- if (fscki->references != fscki->nlink) {
- ubifs_err(c, "inode %lu nlink is %d, but calculated nlink is %d",
- (unsigned long)fscki->inum,
- fscki->nlink, fscki->references);
- goto out_dump;
- }
- }
- if (fscki->xattr_sz != fscki->calc_xsz) {
- ubifs_err(c, "inode %lu has xattr size %u, but calculated size is %lld",
- (unsigned long)fscki->inum, fscki->xattr_sz,
- fscki->calc_xsz);
- goto out_dump;
- }
- if (fscki->xattr_cnt != fscki->calc_xcnt) {
- ubifs_err(c, "inode %lu has %u xattrs, but calculated count is %lld",
- (unsigned long)fscki->inum,
- fscki->xattr_cnt, fscki->calc_xcnt);
- goto out_dump;
- }
- if (fscki->xattr_nms != fscki->calc_xnms) {
- ubifs_err(c, "inode %lu has xattr names' size %u, but calculated names' size is %lld",
- (unsigned long)fscki->inum, fscki->xattr_nms,
- fscki->calc_xnms);
- goto out_dump;
- }
- }
-
- return 0;
-
-out_dump:
- /* Read the bad inode and dump it */
- ino_key_init(c, &key, fscki->inum);
- err = ubifs_lookup_level0(c, &key, &znode, &n);
- if (!err) {
- ubifs_err(c, "inode %lu not found in index",
- (unsigned long)fscki->inum);
- return -ENOENT;
- } else if (err < 0) {
- ubifs_err(c, "error %d while looking up inode %lu",
- err, (unsigned long)fscki->inum);
- return err;
- }
-
- zbr = &znode->zbranch[n];
- ino = kmalloc(zbr->len, GFP_NOFS);
- if (!ino)
- return -ENOMEM;
-
- err = ubifs_tnc_read_node(c, zbr, ino);
- if (err) {
- ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
- zbr->lnum, zbr->offs, err);
- kfree(ino);
- return err;
- }
-
- ubifs_msg(c, "dump of the inode %lu sitting in LEB %d:%d",
- (unsigned long)fscki->inum, zbr->lnum, zbr->offs);
- ubifs_dump_node(c, ino, zbr->len);
- kfree(ino);
- return -EINVAL;
-}
-
-/**
- * dbg_check_filesystem - check the file-system.
- * @c: UBIFS file-system description object
- *
- * This function checks the file system, namely:
- * o makes sure that all leaf nodes exist and their CRCs are correct;
- * o makes sure inode nlink, size, xattr size/count are correct (for all
- * inodes).
- *
- * The function reads whole indexing tree and all nodes, so it is pretty
- * heavy-weight. Returns zero if the file-system is consistent, %-EINVAL if
- * not, and a negative error code in case of failure.
- */
-int dbg_check_filesystem(struct ubifs_info *c)
-{
- int err;
- struct fsck_data fsckd;
-
- if (!dbg_is_chk_fs(c))
- return 0;
-
- fsckd.inodes = RB_ROOT;
- err = dbg_walk_index(c, check_leaf, NULL, &fsckd);
- if (err)
- goto out_free;
-
- err = check_inodes(c, &fsckd);
- if (err)
- goto out_free;
-
- free_inodes(&fsckd);
- return 0;
-
-out_free:
- ubifs_err(c, "file-system check failed with error %d", err);
- dump_stack();
- free_inodes(&fsckd);
- return err;
-}
-
-/**
- * dbg_check_data_nodes_order - check that list of data nodes is sorted.
- * @c: UBIFS file-system description object
- * @head: the list of nodes ('struct ubifs_scan_node' objects)
- *
- * This function returns zero if the list of data nodes is sorted correctly,
- * and %-EINVAL if not.
- */
-int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
-{
- struct list_head *cur;
- struct ubifs_scan_node *sa, *sb;
-
- if (!dbg_is_chk_gen(c))
- return 0;
-
- for (cur = head->next; cur->next != head; cur = cur->next) {
- ino_t inuma, inumb;
- uint32_t blka, blkb;
-
- cond_resched();
- sa = container_of(cur, struct ubifs_scan_node, list);
- sb = container_of(cur->next, struct ubifs_scan_node, list);
-
- if (sa->type != UBIFS_DATA_NODE) {
- ubifs_err(c, "bad node type %d", sa->type);
- ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
- return -EINVAL;
- }
- if (sb->type != UBIFS_DATA_NODE) {
- ubifs_err(c, "bad node type %d", sb->type);
- ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
- return -EINVAL;
- }
-
- inuma = key_inum(c, &sa->key);
- inumb = key_inum(c, &sb->key);
-
- if (inuma < inumb)
- continue;
- if (inuma > inumb) {
- ubifs_err(c, "larger inum %lu goes before inum %lu",
- (unsigned long)inuma, (unsigned long)inumb);
- goto error_dump;
- }
-
- blka = key_block(c, &sa->key);
- blkb = key_block(c, &sb->key);
-
- if (blka > blkb) {
- ubifs_err(c, "larger block %u goes before %u", blka, blkb);
- goto error_dump;
- }
- if (blka == blkb) {
- ubifs_err(c, "two data nodes for the same block");
- goto error_dump;
- }
- }
-
- return 0;
-
-error_dump:
- ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
- ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
- return -EINVAL;
-}
-
-/**
- * dbg_check_nondata_nodes_order - check that list of data nodes is sorted.
- * @c: UBIFS file-system description object
- * @head: the list of nodes ('struct ubifs_scan_node' objects)
- *
- * This function returns zero if the list of non-data nodes is sorted correctly,
- * and %-EINVAL if not.
- */
-int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
-{
- struct list_head *cur;
- struct ubifs_scan_node *sa, *sb;
-
- if (!dbg_is_chk_gen(c))
- return 0;
-
- for (cur = head->next; cur->next != head; cur = cur->next) {
- ino_t inuma, inumb;
- uint32_t hasha, hashb;
-
- cond_resched();
- sa = container_of(cur, struct ubifs_scan_node, list);
- sb = container_of(cur->next, struct ubifs_scan_node, list);
-
- if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
- sa->type != UBIFS_XENT_NODE) {
- ubifs_err(c, "bad node type %d", sa->type);
- ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
- return -EINVAL;
- }
- if (sb->type != UBIFS_INO_NODE && sb->type != UBIFS_DENT_NODE &&
- sb->type != UBIFS_XENT_NODE) {
- ubifs_err(c, "bad node type %d", sb->type);
- ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
- return -EINVAL;
- }
-
- if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
- ubifs_err(c, "non-inode node goes before inode node");
- goto error_dump;
- }
-
- if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE)
- continue;
-
- if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
- /* Inode nodes are sorted in descending size order */
- if (sa->len < sb->len) {
- ubifs_err(c, "smaller inode node goes first");
- goto error_dump;
- }
- continue;
- }
-
- /*
- * This is either a dentry or xentry, which should be sorted in
- * ascending (parent ino, hash) order.
- */
- inuma = key_inum(c, &sa->key);
- inumb = key_inum(c, &sb->key);
-
- if (inuma < inumb)
- continue;
- if (inuma > inumb) {
- ubifs_err(c, "larger inum %lu goes before inum %lu",
- (unsigned long)inuma, (unsigned long)inumb);
- goto error_dump;
- }
-
- hasha = key_block(c, &sa->key);
- hashb = key_block(c, &sb->key);
-
- if (hasha > hashb) {
- ubifs_err(c, "larger hash %u goes before %u",
- hasha, hashb);
- goto error_dump;
- }
- }
-
- return 0;
-
-error_dump:
- ubifs_msg(c, "dumping first node");
- ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
- ubifs_msg(c, "dumping second node");
- ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
- return -EINVAL;
-}
-
-static inline int chance(unsigned int n, unsigned int out_of)
-{
- return !!(get_random_u32_below(out_of) + 1 <= n);
-
-}
-
-static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
-{
- struct ubifs_debug_info *d = c->dbg;
-
- ubifs_assert(c, dbg_is_tst_rcvry(c));
-
- if (!d->pc_cnt) {
- /* First call - decide delay to the power cut */
- if (chance(1, 2)) {
- unsigned long delay;
-
- if (chance(1, 2)) {
- d->pc_delay = 1;
- /* Fail within 1 minute */
- delay = get_random_u32_below(60000);
- d->pc_timeout = jiffies;
- d->pc_timeout += msecs_to_jiffies(delay);
- ubifs_warn(c, "failing after %lums", delay);
- } else {
- d->pc_delay = 2;
- delay = get_random_u32_below(10000);
- /* Fail within 10000 operations */
- d->pc_cnt_max = delay;
- ubifs_warn(c, "failing after %lu calls", delay);
- }
- }
-
- d->pc_cnt += 1;
- }
-
- /* Determine if failure delay has expired */
- if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
- return 0;
- if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
- return 0;
-
- if (lnum == UBIFS_SB_LNUM) {
- if (write && chance(1, 2))
- return 0;
- if (chance(19, 20))
- return 0;
- ubifs_warn(c, "failing in super block LEB %d", lnum);
- } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
- if (chance(19, 20))
- return 0;
- ubifs_warn(c, "failing in master LEB %d", lnum);
- } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
- if (write && chance(99, 100))
- return 0;
- if (chance(399, 400))
- return 0;
- ubifs_warn(c, "failing in log LEB %d", lnum);
- } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
- if (write && chance(7, 8))
- return 0;
- if (chance(19, 20))
- return 0;
- ubifs_warn(c, "failing in LPT LEB %d", lnum);
- } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
- if (write && chance(1, 2))
- return 0;
- if (chance(9, 10))
- return 0;
- ubifs_warn(c, "failing in orphan LEB %d", lnum);
- } else if (lnum == c->ihead_lnum) {
- if (chance(99, 100))
- return 0;
- ubifs_warn(c, "failing in index head LEB %d", lnum);
- } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
- if (chance(9, 10))
- return 0;
- ubifs_warn(c, "failing in GC head LEB %d", lnum);
- } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
- !ubifs_search_bud(c, lnum)) {
- if (chance(19, 20))
- return 0;
- ubifs_warn(c, "failing in non-bud LEB %d", lnum);
- } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
- c->cmt_state == COMMIT_RUNNING_REQUIRED) {
- if (chance(999, 1000))
- return 0;
- ubifs_warn(c, "failing in bud LEB %d commit running", lnum);
- } else {
- if (chance(9999, 10000))
- return 0;
- ubifs_warn(c, "failing in bud LEB %d commit not running", lnum);
- }
-
- d->pc_happened = 1;
- ubifs_warn(c, "========== Power cut emulated ==========");
- dump_stack();
- return 1;
-}
-
-static int corrupt_data(const struct ubifs_info *c, const void *buf,
- unsigned int len)
-{
- unsigned int from, to, ffs = chance(1, 2);
- unsigned char *p = (void *)buf;
-
- from = get_random_u32_below(len);
- /* Corruption span max to end of write unit */
- to = min(len, ALIGN(from + 1, c->max_write_size));
-
- ubifs_warn(c, "filled bytes %u-%u with %s", from, to - 1,
- ffs ? "0xFFs" : "random data");
-
- if (ffs)
- memset(p + from, 0xFF, to - from);
- else
- get_random_bytes(p + from, to - from);
-
- return to;
-}
-
-int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
- int offs, int len)
-{
- int err, failing;
-
- if (dbg_is_power_cut(c))
- return -EROFS;
-
- failing = power_cut_emulated(c, lnum, 1);
- if (failing) {
- len = corrupt_data(c, buf, len);
- ubifs_warn(c, "actually write %d bytes to LEB %d:%d (the buffer was corrupted)",
- len, lnum, offs);
- }
- err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
- if (err)
- return err;
- if (failing)
- return -EROFS;
- return 0;
-}
-
-int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
- int len)
-{
- int err;
-
- if (dbg_is_power_cut(c))
- return -EROFS;
- if (power_cut_emulated(c, lnum, 1))
- return -EROFS;
- err = ubi_leb_change(c->ubi, lnum, buf, len);
- if (err)
- return err;
- if (power_cut_emulated(c, lnum, 1))
- return -EROFS;
- return 0;
-}
-
-int dbg_leb_unmap(struct ubifs_info *c, int lnum)
-{
- int err;
-
- if (dbg_is_power_cut(c))
- return -EROFS;
- if (power_cut_emulated(c, lnum, 0))
- return -EROFS;
- err = ubi_leb_unmap(c->ubi, lnum);
- if (err)
- return err;
- if (power_cut_emulated(c, lnum, 0))
- return -EROFS;
- return 0;
-}
-
-int dbg_leb_map(struct ubifs_info *c, int lnum)
-{
- int err;
-
- if (dbg_is_power_cut(c))
- return -EROFS;
- if (power_cut_emulated(c, lnum, 0))
- return -EROFS;
- err = ubi_leb_map(c->ubi, lnum);
- if (err)
- return err;
- if (power_cut_emulated(c, lnum, 0))
- return -EROFS;
- return 0;
-}
-
-/*
- * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
- * contain the stuff specific to particular file-system mounts.
- */
-static struct dentry *dfs_rootdir;
-
-static int dfs_file_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return nonseekable_open(inode, file);
-}
-
-/**
- * provide_user_output - provide output to the user reading a debugfs file.
- * @val: boolean value for the answer
- * @u: the buffer to store the answer at
- * @count: size of the buffer
- * @ppos: position in the @u output buffer
- *
- * This is a simple helper function which stores @val boolean value in the user
- * buffer when the user reads one of UBIFS debugfs files. Returns amount of
- * bytes written to @u in case of success and a negative error code in case of
- * failure.
- */
-static int provide_user_output(int val, char __user *u, size_t count,
- loff_t *ppos)
-{
- char buf[3];
-
- if (val)
- buf[0] = '1';
- else
- buf[0] = '0';
- buf[1] = '\n';
- buf[2] = 0x00;
-
- return simple_read_from_buffer(u, count, ppos, buf, 2);
-}
-
-static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
- loff_t *ppos)
-{
- struct dentry *dent = file->f_path.dentry;
- struct ubifs_info *c = file->private_data;
- struct ubifs_debug_info *d = c->dbg;
- int val;
-
- if (dent == d->dfs_chk_gen)
- val = d->chk_gen;
- else if (dent == d->dfs_chk_index)
- val = d->chk_index;
- else if (dent == d->dfs_chk_orph)
- val = d->chk_orph;
- else if (dent == d->dfs_chk_lprops)
- val = d->chk_lprops;
- else if (dent == d->dfs_chk_fs)
- val = d->chk_fs;
- else if (dent == d->dfs_tst_rcvry)
- val = d->tst_rcvry;
- else if (dent == d->dfs_ro_error)
- val = c->ro_error;
- else
- return -EINVAL;
-
- return provide_user_output(val, u, count, ppos);
-}
-
-/**
- * interpret_user_input - interpret user debugfs file input.
- * @u: user-provided buffer with the input
- * @count: buffer size
- *
- * This is a helper function which interpret user input to a boolean UBIFS
- * debugfs file. Returns %0 or %1 in case of success and a negative error code
- * in case of failure.
- */
-static int interpret_user_input(const char __user *u, size_t count)
-{
- size_t buf_size;
- char buf[8];
-
- buf_size = min_t(size_t, count, (sizeof(buf) - 1));
- if (copy_from_user(buf, u, buf_size))
- return -EFAULT;
-
- if (buf[0] == '1')
- return 1;
- else if (buf[0] == '0')
- return 0;
-
- return -EINVAL;
-}
-
-static ssize_t dfs_file_write(struct file *file, const char __user *u,
- size_t count, loff_t *ppos)
-{
- struct ubifs_info *c = file->private_data;
- struct ubifs_debug_info *d = c->dbg;
- struct dentry *dent = file->f_path.dentry;
- int val;
-
- if (file->f_path.dentry == d->dfs_dump_lprops) {
- ubifs_dump_lprops(c);
- return count;
- }
- if (file->f_path.dentry == d->dfs_dump_budg) {
- ubifs_dump_budg(c, &c->bi);
- return count;
- }
- if (file->f_path.dentry == d->dfs_dump_tnc) {
- mutex_lock(&c->tnc_mutex);
- ubifs_dump_tnc(c);
- mutex_unlock(&c->tnc_mutex);
- return count;
- }
-
- val = interpret_user_input(u, count);
- if (val < 0)
- return val;
-
- if (dent == d->dfs_chk_gen)
- d->chk_gen = val;
- else if (dent == d->dfs_chk_index)
- d->chk_index = val;
- else if (dent == d->dfs_chk_orph)
- d->chk_orph = val;
- else if (dent == d->dfs_chk_lprops)
- d->chk_lprops = val;
- else if (dent == d->dfs_chk_fs)
- d->chk_fs = val;
- else if (dent == d->dfs_tst_rcvry)
- d->tst_rcvry = val;
- else if (dent == d->dfs_ro_error)
- c->ro_error = !!val;
- else
- return -EINVAL;
-
- return count;
-}
-
-static const struct file_operations dfs_fops = {
- .open = dfs_file_open,
- .read = dfs_file_read,
- .write = dfs_file_write,
- .owner = THIS_MODULE,
- .llseek = no_llseek,
-};
-
-/**
- * dbg_debugfs_init_fs - initialize debugfs for UBIFS instance.
- * @c: UBIFS file-system description object
- *
- * This function creates all debugfs files for this instance of UBIFS.
- *
- * Note, the only reason we have not merged this function with the
- * 'ubifs_debugging_init()' function is because it is better to initialize
- * debugfs interfaces at the very end of the mount process, and remove them at
- * the very beginning of the mount process.
- */
-void dbg_debugfs_init_fs(struct ubifs_info *c)
-{
- int n;
- const char *fname;
- struct ubifs_debug_info *d = c->dbg;
-
- n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
- c->vi.ubi_num, c->vi.vol_id);
- if (n > UBIFS_DFS_DIR_LEN) {
- /* The array size is too small */
- return;
- }
-
- fname = d->dfs_dir_name;
- d->dfs_dir = debugfs_create_dir(fname, dfs_rootdir);
-
- fname = "dump_lprops";
- d->dfs_dump_lprops = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
- &dfs_fops);
-
- fname = "dump_budg";
- d->dfs_dump_budg = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
- &dfs_fops);
-
- fname = "dump_tnc";
- d->dfs_dump_tnc = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
- &dfs_fops);
-
- fname = "chk_general";
- d->dfs_chk_gen = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "chk_index";
- d->dfs_chk_index = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "chk_orphans";
- d->dfs_chk_orph = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "chk_lprops";
- d->dfs_chk_lprops = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "chk_fs";
- d->dfs_chk_fs = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "tst_recovery";
- d->dfs_tst_rcvry = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-
- fname = "ro_error";
- d->dfs_ro_error = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- d->dfs_dir, c, &dfs_fops);
-}
-
-/**
- * dbg_debugfs_exit_fs - remove all debugfs files.
- * @c: UBIFS file-system description object
- */
-void dbg_debugfs_exit_fs(struct ubifs_info *c)
-{
- debugfs_remove_recursive(c->dbg->dfs_dir);
-}
-
-struct ubifs_global_debug_info ubifs_dbg;
-
-static struct dentry *dfs_chk_gen;
-static struct dentry *dfs_chk_index;
-static struct dentry *dfs_chk_orph;
-static struct dentry *dfs_chk_lprops;
-static struct dentry *dfs_chk_fs;
-static struct dentry *dfs_tst_rcvry;
-
-static ssize_t dfs_global_file_read(struct file *file, char __user *u,
- size_t count, loff_t *ppos)
-{
- struct dentry *dent = file->f_path.dentry;
- int val;
-
- if (dent == dfs_chk_gen)
- val = ubifs_dbg.chk_gen;
- else if (dent == dfs_chk_index)
- val = ubifs_dbg.chk_index;
- else if (dent == dfs_chk_orph)
- val = ubifs_dbg.chk_orph;
- else if (dent == dfs_chk_lprops)
- val = ubifs_dbg.chk_lprops;
- else if (dent == dfs_chk_fs)
- val = ubifs_dbg.chk_fs;
- else if (dent == dfs_tst_rcvry)
- val = ubifs_dbg.tst_rcvry;
- else
- return -EINVAL;
-
- return provide_user_output(val, u, count, ppos);
-}
-
-static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
- size_t count, loff_t *ppos)
-{
- struct dentry *dent = file->f_path.dentry;
- int val;
-
- val = interpret_user_input(u, count);
- if (val < 0)
- return val;
-
- if (dent == dfs_chk_gen)
- ubifs_dbg.chk_gen = val;
- else if (dent == dfs_chk_index)
- ubifs_dbg.chk_index = val;
- else if (dent == dfs_chk_orph)
- ubifs_dbg.chk_orph = val;
- else if (dent == dfs_chk_lprops)
- ubifs_dbg.chk_lprops = val;
- else if (dent == dfs_chk_fs)
- ubifs_dbg.chk_fs = val;
- else if (dent == dfs_tst_rcvry)
- ubifs_dbg.tst_rcvry = val;
- else
- return -EINVAL;
-
- return count;
-}
-
-static const struct file_operations dfs_global_fops = {
- .read = dfs_global_file_read,
- .write = dfs_global_file_write,
- .owner = THIS_MODULE,
- .llseek = no_llseek,
-};
-
-/**
- * dbg_debugfs_init - initialize debugfs file-system.
- *
- * UBIFS uses debugfs file-system to expose various debugging knobs to
- * user-space. This function creates "ubifs" directory in the debugfs
- * file-system.
- */
-void dbg_debugfs_init(void)
-{
- const char *fname;
-
- fname = "ubifs";
- dfs_rootdir = debugfs_create_dir(fname, NULL);
-
- fname = "chk_general";
- dfs_chk_gen = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir,
- NULL, &dfs_global_fops);
-
- fname = "chk_index";
- dfs_chk_index = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- dfs_rootdir, NULL, &dfs_global_fops);
-
- fname = "chk_orphans";
- dfs_chk_orph = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- dfs_rootdir, NULL, &dfs_global_fops);
-
- fname = "chk_lprops";
- dfs_chk_lprops = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- dfs_rootdir, NULL, &dfs_global_fops);
-
- fname = "chk_fs";
- dfs_chk_fs = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir,
- NULL, &dfs_global_fops);
-
- fname = "tst_recovery";
- dfs_tst_rcvry = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
- dfs_rootdir, NULL, &dfs_global_fops);
-}
-
-/**
- * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
- */
-void dbg_debugfs_exit(void)
-{
- debugfs_remove_recursive(dfs_rootdir);
-}
-
-void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
- const char *file, int line)
-{
- ubifs_err(c, "UBIFS assert failed: %s, in %s:%u", expr, file, line);
-
- switch (c->assert_action) {
- case ASSACT_PANIC:
- BUG();
- break;
-
- case ASSACT_RO:
- ubifs_ro_mode(c, -EINVAL);
- break;
-
- case ASSACT_REPORT:
- default:
- dump_stack();
- break;
-
- }
-}
-
-/**
- * ubifs_debugging_init - initialize UBIFS debugging.
- * @c: UBIFS file-system description object
- *
- * This function initializes debugging-related data for the file system.
- * Returns zero in case of success and a negative error code in case of
- * failure.
- */
-int ubifs_debugging_init(struct ubifs_info *c)
-{
- c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
- if (!c->dbg)
- return -ENOMEM;
-
- return 0;
-}
-
-/**
- * ubifs_debugging_exit - free debugging data.
- * @c: UBIFS file-system description object
- */
-void ubifs_debugging_exit(struct ubifs_info *c)
-{
- kfree(c->dbg);
+ ubifs_ro_mode(c, -EINVAL);
}
diff --git a/ubifs-utils/libubifs/debug.h b/ubifs-utils/libubifs/debug.h
index ed966108..3a553627 100644
--- a/ubifs-utils/libubifs/debug.h
+++ b/ubifs-utils/libubifs/debug.h
@@ -17,125 +17,6 @@ typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
typedef int (*dbg_znode_callback)(struct ubifs_info *c,
struct ubifs_znode *znode, void *priv);
-/*
- * The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi"
- * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
- */
-#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
-#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
-
-/**
- * ubifs_debug_info - per-FS debugging information.
- * @old_zroot: old index root - used by 'dbg_check_old_index()'
- * @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
- * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
- *
- * @pc_happened: non-zero if an emulated power cut happened
- * @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
- * @pc_timeout: time in jiffies when delay of failure mode expires
- * @pc_cnt: current number of calls to failure mode I/O functions
- * @pc_cnt_max: number of calls by which to delay failure mode
- *
- * @chk_lpt_sz: used by LPT tree size checker
- * @chk_lpt_sz2: used by LPT tree size checker
- * @chk_lpt_wastage: used by LPT tree size checker
- * @chk_lpt_lebs: used by LPT tree size checker
- * @new_nhead_offs: used by LPT tree size checker
- * @new_ihead_lnum: used by debugging to check @c->ihead_lnum
- * @new_ihead_offs: used by debugging to check @c->ihead_offs
- *
- * @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
- * @saved_bi: saved budgeting information
- * @saved_free: saved amount of free space
- * @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
- *
- * @chk_gen: if general extra checks are enabled
- * @chk_index: if index xtra checks are enabled
- * @chk_orph: if orphans extra checks are enabled
- * @chk_lprops: if lprops extra checks are enabled
- * @chk_fs: if UBIFS contents extra checks are enabled
- * @tst_rcvry: if UBIFS recovery testing mode enabled
- *
- * @dfs_dir_name: name of debugfs directory containing this file-system's files
- * @dfs_dir: direntry object of the file-system debugfs directory
- * @dfs_dump_lprops: "dump lprops" debugfs knob
- * @dfs_dump_budg: "dump budgeting information" debugfs knob
- * @dfs_dump_tnc: "dump TNC" debugfs knob
- * @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks
- * @dfs_chk_index: debugfs knob to enable UBIFS index extra checks
- * @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks
- * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
- * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
- * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
- * @dfs_ro_error: debugfs knob to switch UBIFS to R/O mode (different to
- * re-mounting to R/O mode because it does not flush any buffers
- * and UBIFS just starts returning -EROFS on all write
- * operations)
- */
-struct ubifs_debug_info {
- struct ubifs_zbranch old_zroot;
- int old_zroot_level;
- unsigned long long old_zroot_sqnum;
-
- int pc_happened;
- int pc_delay;
- unsigned long pc_timeout;
- unsigned int pc_cnt;
- unsigned int pc_cnt_max;
-
- long long chk_lpt_sz;
- long long chk_lpt_sz2;
- long long chk_lpt_wastage;
- int chk_lpt_lebs;
- int new_nhead_offs;
- int new_ihead_lnum;
- int new_ihead_offs;
-
- struct ubifs_lp_stats saved_lst;
- struct ubifs_budg_info saved_bi;
- long long saved_free;
- int saved_idx_gc_cnt;
-
- unsigned int chk_gen:1;
- unsigned int chk_index:1;
- unsigned int chk_orph:1;
- unsigned int chk_lprops:1;
- unsigned int chk_fs:1;
- unsigned int tst_rcvry:1;
-
- char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1];
- struct dentry *dfs_dir;
- struct dentry *dfs_dump_lprops;
- struct dentry *dfs_dump_budg;
- struct dentry *dfs_dump_tnc;
- struct dentry *dfs_chk_gen;
- struct dentry *dfs_chk_index;
- struct dentry *dfs_chk_orph;
- struct dentry *dfs_chk_lprops;
- struct dentry *dfs_chk_fs;
- struct dentry *dfs_tst_rcvry;
- struct dentry *dfs_ro_error;
-};
-
-/**
- * ubifs_global_debug_info - global (not per-FS) UBIFS debugging information.
- *
- * @chk_gen: if general extra checks are enabled
- * @chk_index: if index xtra checks are enabled
- * @chk_orph: if orphans extra checks are enabled
- * @chk_lprops: if lprops extra checks are enabled
- * @chk_fs: if UBIFS contents extra checks are enabled
- * @tst_rcvry: if UBIFS recovery testing mode enabled
- */
-struct ubifs_global_debug_info {
- unsigned int chk_gen:1;
- unsigned int chk_index:1;
- unsigned int chk_orph:1;
- unsigned int chk_lprops:1;
- unsigned int chk_fs:1;
- unsigned int tst_rcvry:1;
-};
-
void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
const char *file, int line);
@@ -154,16 +35,15 @@ void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
} \
} while (0)
-#define ubifs_dbg_msg(type, fmt, ...) \
- pr_debug("UBIFS DBG " type " (pid %d): " fmt "\n", current->pid, \
- ##__VA_ARGS__)
+#define ubifs_dbg_msg(type, fmt, ...) do { \
+ pr_debug("UBIFS DBG " type " " fmt "\n",##__VA_ARGS__); \
+} while (0)
#define DBG_KEY_BUF_LEN 48
-#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
- char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
- pr_debug("UBIFS DBG " type " (pid %d): " fmt "%s\n", current->pid, \
- ##__VA_ARGS__, \
- dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
+#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
+ char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
+ pr_debug("UBIFS DBG " type " " fmt "%s\n", ##__VA_ARGS__, \
+ dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
} while (0)
/* General messages */
@@ -199,39 +79,8 @@ void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
/* Additional recovery messages */
#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
-extern struct ubifs_global_debug_info ubifs_dbg;
-
-static inline int dbg_is_chk_gen(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen);
-}
-static inline int dbg_is_chk_index(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.chk_index || c->dbg->chk_index);
-}
-static inline int dbg_is_chk_orph(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph);
-}
-static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops);
-}
-static inline int dbg_is_chk_fs(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs);
-}
-static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
-{
- return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry);
-}
-static inline int dbg_is_power_cut(const struct ubifs_info *c)
-{
- return !!c->dbg->pc_happened;
-}
-
-int ubifs_debugging_init(struct ubifs_info *c);
-void ubifs_debugging_exit(struct ubifs_info *c);
+static inline int dbg_is_chk_index(__unused const struct ubifs_info *c)
+{ return 0; }
/* Dump functions */
const char *dbg_ntype(int type);
@@ -241,10 +90,8 @@ const char *dbg_get_key_dump(const struct ubifs_info *c,
const union ubifs_key *key);
const char *dbg_snprintf_key(const struct ubifs_info *c,
const union ubifs_key *key, char *buffer, int len);
-void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode);
void ubifs_dump_node(const struct ubifs_info *c, const void *node,
int node_len);
-void ubifs_dump_budget_req(const struct ubifs_budget_req *req);
void ubifs_dump_lstats(const struct ubifs_lp_stats *lst);
void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi);
void ubifs_dump_lprop(const struct ubifs_info *c,
@@ -258,47 +105,65 @@ void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
int cat);
void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
struct ubifs_nnode *parent, int iip);
-void ubifs_dump_tnc(struct ubifs_info *c);
void ubifs_dump_index(struct ubifs_info *c);
void ubifs_dump_lpt_lebs(const struct ubifs_info *c);
int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
dbg_znode_callback znode_cb, void *priv);
+int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv);
/* Checking functions */
-void dbg_save_space_info(struct ubifs_info *c);
-int dbg_check_space_info(struct ubifs_info *c);
-int dbg_check_lprops(struct ubifs_info *c);
-int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
-int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
-int dbg_check_cats(struct ubifs_info *c);
-int dbg_check_ltab(struct ubifs_info *c);
-int dbg_chk_lpt_free_spc(struct ubifs_info *c);
-int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
-int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode);
-int dbg_check_dir(struct ubifs_info *c, const struct inode *dir);
-int dbg_check_tnc(struct ubifs_info *c, int extra);
-int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
-int dbg_check_filesystem(struct ubifs_info *c);
-void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
- int add_pos);
-int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
- int row, int col);
-int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
- loff_t size);
-int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
-int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
-
-int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
- int len);
-int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
-int dbg_leb_unmap(struct ubifs_info *c, int lnum);
-int dbg_leb_map(struct ubifs_info *c, int lnum);
-
-/* Debugfs-related stuff */
-void dbg_debugfs_init(void);
-void dbg_debugfs_exit(void);
-void dbg_debugfs_init_fs(struct ubifs_info *c);
-void dbg_debugfs_exit_fs(struct ubifs_info *c);
+static inline void dbg_save_space_info(__unused struct ubifs_info *c) {}
+static inline int dbg_check_space_info(__unused struct ubifs_info *c)
+{ return 0; }
+static inline int dbg_check_lprops(__unused struct ubifs_info *c) { return 0; }
+static inline int dbg_old_index_check_init(__unused struct ubifs_info *c,
+ __unused struct ubifs_zbranch *zroot)
+{ return 0; }
+static inline int dbg_check_old_index(__unused struct ubifs_info *c,
+ __unused struct ubifs_zbranch *zroot)
+{ return 0; }
+static inline int dbg_check_cats(__unused struct ubifs_info *c) { return 0; }
+static inline int dbg_check_ltab(__unused struct ubifs_info *c) { return 0; }
+static inline int dbg_chk_lpt_free_spc(__unused struct ubifs_info *c)
+{ return 0; }
+static inline int dbg_chk_lpt_sz(__unused struct ubifs_info *c,
+ __unused int action, __unused int len)
+{ return 0; }
+static inline int dbg_check_tnc(__unused struct ubifs_info *c,
+ __unused int extra) { return 0; }
+static inline int dbg_check_idx_size(__unused struct ubifs_info *c,
+ __unused long long idx_size) { return 0; }
+static inline int dbg_check_filesystem(__unused struct ubifs_info *c)
+{ return 0; }
+static inline void dbg_check_heap(__unused struct ubifs_info *c,
+ __unused struct ubifs_lpt_heap *heap,
+ __unused int cat,
+ __unused int add_pos) {}
+static inline int dbg_check_lpt_nodes(__unused struct ubifs_info *c,
+ __unused struct ubifs_cnode *cnode,
+ __unused int row,
+ __unused int col) { return 0; }
+static inline int dbg_check_data_nodes_order(__unused struct ubifs_info *c,
+ __unused struct list_head *head)
+{ return 0; }
+static inline int dbg_check_nondata_nodes_order(__unused struct ubifs_info *c,
+ __unused struct list_head *head)
+{ return 0; }
+static inline int dbg_leb_write(__unused struct ubifs_info *c,
+ __unused int lnum, __unused const void *buf,
+ __unused int offs, __unused int len)
+{ return 0; }
+static inline int dbg_leb_change(__unused struct ubifs_info *c,
+ __unused int lnum, __unused const void *buf,
+ __unused int len) { return 0; }
+static inline int dbg_leb_unmap(__unused struct ubifs_info *c,
+ __unused int lnum) { return 0; }
+static inline int dbg_leb_map(__unused struct ubifs_info *c, __unused int lnum)
+{ return 0; }
+
+extern void print_hex_dump(const char *prefix_str,
+ int prefix_type, int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii);
#endif /* !__UBIFS_DEBUG_H__ */
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index f32818dc..2af9d87b 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -786,8 +786,6 @@ struct ubifs_budg_info {
int dent_budget;
};
-struct ubifs_debug_info;
-
/**
* struct ubifs_info - UBIFS file-system description data structure
* (per-superblock).
--
2.13.6
Adapt gc subsystem(find.c, gc.c, scan.c) in libubifs, compared with
linux kernel implementations:
1. Adapt print_hex_dump based on implementations in hexdump.c.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/find.c | 9 ++++++++-
ubifs-utils/libubifs/gc.c | 10 +++++++---
ubifs-utils/libubifs/scan.c | 7 ++++++-
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/ubifs-utils/libubifs/find.c b/ubifs-utils/libubifs/find.c
index 873e6e1c..ecf689c4 100644
--- a/ubifs-utils/libubifs/find.c
+++ b/ubifs-utils/libubifs/find.c
@@ -14,8 +14,15 @@
* for fast access, falling back on scanning the LPT as a last resort.
*/
-#include <linux/sort.h>
+#include <sys/types.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "sort.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
/**
* struct scan_data - data provided to scan callback functions
diff --git a/ubifs-utils/libubifs/gc.c b/ubifs-utils/libubifs/gc.c
index 3134d070..c3595358 100644
--- a/ubifs-utils/libubifs/gc.c
+++ b/ubifs-utils/libubifs/gc.c
@@ -41,10 +41,14 @@
* good, and GC takes extra care when moving them.
*/
-#include <linux/slab.h>
-#include <linux/pagemap.h>
-#include <linux/list_sort.h>
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/*
* GC may need to move more than one LEB to make progress. The below constants
diff --git a/ubifs-utils/libubifs/scan.c b/ubifs-utils/libubifs/scan.c
index 84a9157d..74509fd0 100644
--- a/ubifs-utils/libubifs/scan.c
+++ b/ubifs-utils/libubifs/scan.c
@@ -15,7 +15,12 @@
* debugging functions.
*/
+#include "linux_err.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
/**
* scan_padding_bytes - scan for padding bytes.
@@ -232,7 +237,7 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
if (len > 8192)
len = 8192;
ubifs_err(c, "first %d bytes from LEB %d:%d", len, lnum, offs);
- print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
}
/**
--
2.13.6
There are four dimensions to define the type of inconsistent problems:
1. fixable: Some inconsistent problems can't be fixed, for example
corrupted superblock. Un-fixable problem will abort program.
2. must fix: Some inconsistent problems can be ignored(eg. incorrect
isize), but some are not(eg. corrupted TNC), which will affect the
subsequent fsck steps.
3. drop data: Some fixing methods will drop user data, which is
unacceptable for safe mode. If it happens, fsck will be aborted.
4. need rebuild: Some inconsistent problems depends on rebuilding
filesystem to be fixed(eg. corrupted master node, corrupted TNC).
Define an asking function to handle above kinds of inconsistent problems.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 +
ubifs-utils/fsck.ubifs/problem.c | 111 ++++++++++++++++++++++++++++++++++++
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 ubifs-utils/fsck.ubifs/problem.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 0ed91dff..faff911f 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -79,7 +79,8 @@ fsck_ubifs_SOURCES = \
$(common_SOURCES) \
$(libubifs_SOURCES) \
ubifs-utils/fsck.ubifs/fsck.ubifs.h \
- ubifs-utils/fsck.ubifs/fsck.ubifs.c
+ ubifs-utils/fsck.ubifs/fsck.ubifs.c \
+ ubifs-utils/fsck.ubifs/problem.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index d267dd24..b9783c15 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -88,4 +88,7 @@ static inline const char *mode_name(const struct ubifs_info *c)
/* Exit code for fsck program. */
extern int exit_code;
+/* problem.c */
+bool fix_problem(const struct ubifs_info *c, int problem_type);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
new file mode 100644
index 00000000..9a8c2e06
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "fsck.ubifs.h"
+
+/*
+ * problem flags.
+ *
+ * PROBLEM_FIXABLE: problem is fixable, unsolvable problem such as corrupted
+ * super block will abort the fsck program
+ * PROBLEM_MUST_FIX: problem must be fixed because it will affect the subsequent
+ * fsck process, otherwise aborting the fsck program
+ * PROBLEM_DROP_DATA: user data could be dropped after fixing the problem
+ * PROBLEM_NEED_REBUILD: rebuilding filesystem is needed to fix the problem
+ */
+#define PROBLEM_FIXABLE (1<<0)
+#define PROBLEM_MUST_FIX (1<<1)
+#define PROBLEM_DROP_DATA (1<<2)
+#define PROBLEM_NEED_REBUILD (1<<3)
+
+struct fsck_problem {
+ unsigned int flags;
+ const char *desc;
+};
+
+static const struct fsck_problem problem_table[] = {};
+
+static void print_problem(const struct ubifs_info *c,
+ const struct fsck_problem *problem)
+{
+ log_out(c, "problem: %s", problem->desc);
+}
+
+static void fatal_error(const struct ubifs_info *c,
+ const struct fsck_problem *problem)
+{
+ if (!(problem->flags & PROBLEM_FIXABLE))
+ log_out(c, "inconsistent problem cannot be fixed");
+ else
+ log_out(c, "inconsistent problem must be fixed");
+ exit(exit_code);
+}
+
+/**
+ * fix_problem - whether fixing the inconsistent problem
+ * @c: UBIFS file-system description object
+ * @problem_type: the type of inconsistent problem
+ *
+ * This function decides to fix/skip the inconsistent problem or abort the
+ * program according to @problem_type, returns %true if the problem should
+ * be fixed, returns %false if the problem will be skipped.
+ */
+bool fix_problem(const struct ubifs_info *c, int problem_type)
+{
+ bool ans, ask = true, def_y = true;
+ const struct fsck_problem *problem = &problem_table[problem_type];
+ const char *question = (problem->flags & PROBLEM_NEED_REBUILD) ?
+ "Rebuild filesystem?" : "Fix it?";
+
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (!(problem->flags & PROBLEM_FIXABLE)) {
+ exit_code |= FSCK_UNCORRECTED;
+ fatal_error(c, problem);
+ }
+
+ if (FSCK(c)->mode == CHECK_MODE ||
+ ((problem->flags & PROBLEM_DROP_DATA) && FSCK(c)->mode == SAFE_MODE) ||
+ ((problem->flags & PROBLEM_NEED_REBUILD) &&
+ (FSCK(c)->mode == SAFE_MODE || FSCK(c)->mode == DANGER_MODE0)))
+ def_y = false;
+
+ if ((problem->flags & PROBLEM_NEED_REBUILD) &&
+ (FSCK(c)->mode == DANGER_MODE0 || FSCK(c)->mode == DANGER_MODE1))
+ ask = false;
+
+ print_problem(c, problem);
+ ans = def_y;
+ if (FSCK(c)->mode == NORMAL_MODE) {
+ printf("%s[%d] (%s%s)", c->program_name, getpid(),
+ c->dev_name ? : "-", mode_name(c));
+ if (prompt(question, def_y))
+ ans = true;
+ else
+ ans = false;
+ } else {
+ if (ask)
+ log_out(c, "%s %c\n", question, def_y ? 'y' : 'n');
+ }
+
+ if (!ans) {
+ exit_code |= FSCK_UNCORRECTED;
+ if (problem->flags & PROBLEM_MUST_FIX)
+ fatal_error(c, problem);
+ } else {
+ exit_code |= FSCK_NONDESTRUCT;
+ }
+
+ return ans;
+}
--
2.13.6
Since ubifs-media.h is only used for ubifs-utils, move it under
ubifs-utils/libubifs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 4 ++--
ubifs-utils/common/ubifs.h | 2 +-
{include/mtd => ubifs-utils/libubifs}/ubifs-media.h | 0
3 files changed, 3 insertions(+), 3 deletions(-)
rename {include/mtd => ubifs-utils/libubifs}/ubifs-media.h (100%)
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index cb4e1cf1..d297f7a2 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -31,7 +31,7 @@ common_SOURCES = \
ubifs-utils/common/lpt.c \
ubifs-utils/common/super.c \
ubifs-utils/common/sign.h \
- include/mtd/ubifs-media.h
+ ubifs-utils/libubifs/ubifs-media.h
if WITH_CRYPTO
common_SOURCES += ubifs-utils/common/crypto.c \
@@ -47,7 +47,7 @@ mkfs_ubifs_SOURCES = \
mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
- -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -rdynamic
+ -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs -rdynamic
EXTRA_DIST += ubifs-utils/common/README
diff --git a/ubifs-utils/common/ubifs.h b/ubifs-utils/common/ubifs.h
index 5a909f63..ed297cc7 100644
--- a/ubifs-utils/common/ubifs.h
+++ b/ubifs-utils/common/ubifs.h
@@ -25,7 +25,7 @@
#ifndef __UBIFS_H__
#define __UBIFS_H__
-#include <mtd/ubifs-media.h>
+#include "ubifs-media.h"
#include "libubi.h"
/* Maximum logical eraseblock size in bytes */
diff --git a/include/mtd/ubifs-media.h b/ubifs-utils/libubifs/ubifs-media.h
similarity index 100%
rename from include/mtd/ubifs-media.h
rename to ubifs-utils/libubifs/ubifs-media.h
--
2.13.6
Load filesystem information from UBI volume (Similar to UBIFS mounting
process), initialize kinds of buffers and read superblock. This is the
base step for both fsck and rebuild_fs. Subsequent pacthes will complete
this step by adding more steps(eg. read master, replay journal, etc.)
which are only used in fsck.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 7 ++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 7 ++
ubifs-utils/fsck.ubifs/load_fs.c | 127 ++++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/problem.c | 4 +-
5 files changed, 146 insertions(+), 2 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/load_fs.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index faff911f..864271ab 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -80,7 +80,8 @@ fsck_ubifs_SOURCES = \
$(libubifs_SOURCES) \
ubifs-utils/fsck.ubifs/fsck.ubifs.h \
ubifs-utils/fsck.ubifs/fsck.ubifs.c \
- ubifs-utils/fsck.ubifs/problem.c
+ ubifs-utils/fsck.ubifs/problem.c \
+ ubifs-utils/fsck.ubifs/load_fs.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 36a8e061..9bc9c259 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -423,8 +423,15 @@ int main(int argc, char *argv[])
goto out_destroy_fsck;
}
+ /* Init: Read superblock */
+ err = ubifs_load_filesystem(c);
+ if (err)
+ goto out_close;
+
err = do_fsck();
+ ubifs_destroy_filesystem(c);
+out_close:
ubifs_close_volume(c);
out_destroy_fsck:
destroy_fsck_info(c);
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 53dc5ff0..eb365b1a 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -36,6 +36,9 @@
enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
DANGER_MODE1, REBUILD_MODE, CHECK_MODE };
+/* Types of inconsistent problems */
+enum { SB_CORRUPTED = 0 };
+
/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
@@ -96,4 +99,8 @@ extern int exit_code;
/* problem.c */
bool fix_problem(const struct ubifs_info *c, int problem_type);
+/* load_fs.c */
+int ubifs_load_filesystem(struct ubifs_info *c);
+void ubifs_destroy_filesystem(struct ubifs_info *c);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
new file mode 100644
index 00000000..4a06b4c2
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+int ubifs_load_filesystem(struct ubifs_info *c)
+{
+ int err;
+ size_t sz;
+
+ err = init_constants_early(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ return err;
+ }
+
+ err = check_volume_empty(c);
+ if (err <= 0) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, 0, "%s UBI volume!", err < 0 ? "bad" : "empty");
+ return -EINVAL;
+ }
+
+ if (c->ro_media && !c->ro_mount) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, 0, "cannot read-write on read-only media");
+ return -EROFS;
+ }
+
+ err = -ENOMEM;
+ c->bottom_up_buf = kmalloc_array(BOTTOM_UP_HEIGHT, sizeof(int),
+ GFP_KERNEL);
+ if (!c->bottom_up_buf) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, errno, "cannot allocate bottom_up_buf");
+ goto out_free;
+ }
+
+ c->sbuf = vmalloc(c->leb_size);
+ if (!c->sbuf) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, errno, "cannot allocate sbuf");
+ goto out_free;
+ }
+
+ if (!c->ro_mount) {
+ c->ileb_buf = vmalloc(c->leb_size);
+ if (!c->ileb_buf) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, errno, "cannot allocate ileb_buf");
+ goto out_free;
+ }
+ }
+
+ c->mounting = 1;
+
+ log_out(c, "Read superblock");
+ err = ubifs_read_superblock(c);
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED))
+ fix_problem(c, SB_CORRUPTED);
+ exit_code |= FSCK_ERROR;
+ goto out_mounting;
+ }
+
+ err = init_constants_sb(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_mounting;
+ }
+
+ sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2;
+ c->cbuf = kmalloc(sz, GFP_NOFS);
+ if (!c->cbuf) {
+ err = -ENOMEM;
+ exit_code |= FSCK_ERROR;
+ log_err(c, errno, "cannot allocate cbuf");
+ goto out_mounting;
+ }
+
+ err = alloc_wbufs(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ log_err(c, 0, "cannot allocate wbuf");
+ goto out_mounting;
+ }
+
+ c->mounting = 0;
+
+ return 0;
+
+out_mounting:
+ c->mounting = 0;
+out_free:
+ kfree(c->cbuf);
+ kfree(c->ileb_buf);
+ kfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ kfree(c->sup_node);
+
+ return err;
+}
+
+void ubifs_destroy_filesystem(struct ubifs_info *c)
+{
+ free_wbufs(c);
+
+ kfree(c->cbuf);
+ kfree(c->ileb_buf);
+ kfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ kfree(c->sup_node);
+}
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 9a8c2e06..acb9e45e 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -34,7 +34,9 @@ struct fsck_problem {
const char *desc;
};
-static const struct fsck_problem problem_table[] = {};
+static const struct fsck_problem problem_table[] = {
+ {0, "Corrupted superblock"}, // SB_CORRUPTED
+};
static void print_problem(const struct ubifs_info *c,
const struct fsck_problem *problem)
--
2.13.6
Set errno if the target is not char device. It will be useful for
fsck to print error message if open_ubi failed.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/super.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/libubifs/super.c b/ubifs-utils/libubifs/super.c
index 1cbbfcac..9fa366f3 100644
--- a/ubifs-utils/libubifs/super.c
+++ b/ubifs-utils/libubifs/super.c
@@ -43,9 +43,14 @@ int open_ubi(struct ubifs_info *c, const char *node)
{
struct stat st;
- if (stat(node, &st) || !S_ISCHR(st.st_mode))
+ if (stat(node, &st))
return -1;
+ if (!S_ISCHR(st.st_mode)) {
+ errno = ENODEV;
+ return -1;
+ }
+
c->libubi = libubi_open();
if (!c->libubi)
return -1;
--
2.13.6
This is the 6/12 step of rebuilding. Correct the file information.
Traverse all files and calculate information (nlink, size, xattr_cnt,
etc.) for each file just like check_leaf() does, correct inode node
based on the calculated information.
Now, all files are consistent, and UBIFS will pass chk_fs after mounting.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 270 +++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 1 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 6 +
ubifs-utils/libubifs/debug.c | 2 +-
ubifs-utils/libubifs/debug.h | 1 +
5 files changed, 279 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index dd5cb310..b8777f6c 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -13,6 +13,7 @@
#include "linux_err.h"
#include "bitops.h"
#include "kmem.h"
+#include "crc32.h"
#include "ubifs.h"
#include "defs.h"
#include "debug.h"
@@ -982,3 +983,272 @@ reachable:
dbg_fsck("file %lu is reachable, in %s", file->inum, c->dev_name);
return true;
}
+
+/**
+ * calculate_file_info - calculate the information of file
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @file_tree: tree of all scanned files
+ *
+ * This function calculates file information according to dentry nodes,
+ * data nodes and truncation node. The calculated informaion will be used
+ * to correct inode node.
+ */
+static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree)
+{
+ int nlink = 0;
+ bool corrupted_truncation = false;
+ unsigned long long ino_sqnum, trun_size = 0, new_size = 0, trun_sqnum = 0;
+ struct rb_node *node;
+ struct scanned_file *parent_file, *xattr_file;
+ struct scanned_dent_node *dent_node;
+ struct scanned_data_node *data_node;
+ LIST_HEAD(drop_list);
+
+ for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
+ xattr_file = rb_entry(node, struct scanned_file, rb);
+
+ ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
+ calculate_file_info(c, xattr_file, file_tree);
+ }
+
+ if (file->inum == UBIFS_ROOT_INO) {
+ file->calc_nlink += 2;
+ file->calc_size += UBIFS_INO_NODE_SZ;
+ return;
+ }
+
+ if (S_ISDIR(file->ino.mode)) {
+ file->calc_nlink += 2;
+ file->calc_size += UBIFS_INO_NODE_SZ;
+
+ dent_node = rb_entry(rb_first(&file->dent_nodes),
+ struct scanned_dent_node, rb);
+ parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
+ if (!parent_file) {
+ ubifs_assert(c, 0);
+ return;
+ }
+ parent_file->calc_nlink += 1;
+ parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
+ return;
+ }
+
+ if (file->ino.is_xattr) {
+ file->calc_nlink = 1;
+ file->calc_size = file->ino.size;
+
+ dent_node = rb_entry(rb_first(&file->dent_nodes),
+ struct scanned_dent_node, rb);
+ parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
+ if (!parent_file) {
+ ubifs_assert(c, 0);
+ return;
+ }
+ parent_file->calc_xcnt += 1;
+ parent_file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
+ parent_file->calc_xsz += CALC_XATTR_BYTES(file->ino.size);
+ parent_file->calc_xnms += dent_node->nlen;
+ return;
+ }
+
+ for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
+ nlink++;
+
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+
+ parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
+ if (!parent_file) {
+ ubifs_assert(c, 0);
+ return;
+ }
+ parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
+ }
+ file->calc_nlink = nlink;
+
+ if (!S_ISREG(file->ino.mode)) {
+ /* No need to verify i_size for symlink/sock/block/char/fifo. */
+ file->calc_size = file->ino.size;
+ return;
+ }
+
+ /*
+ * Process i_size and data content, following situations should
+ * be considered:
+ * 1. Sequential writing or overwriting, i_size should be
+ * max(i_size, data node size), pick larger sqnum one from
+ * data nodes with same block index.
+ * 2. Mixed truncation and writing, i_size depends on the latest
+ * truncation node or inode node or last data node, pick data
+ * nodes which are not truncated.
+ * 3. Setting bigger i_size attr, pick inode size or biggest
+ * i_size calculated by data nodes.
+ */
+ if (file->trun.header.exist) {
+ trun_size = file->trun.new_size;
+ trun_sqnum = file->trun.header.sqnum;
+ }
+ ino_sqnum = file->ino.header.sqnum;
+ for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) {
+ unsigned long long d_sz, d_sqnum;
+ unsigned int block_no;
+
+ data_node = rb_entry(node, struct scanned_data_node, rb);
+
+ d_sqnum = data_node->header.sqnum;
+ block_no = key_block(c, &data_node->key);
+ d_sz = data_node->size + block_no * UBIFS_BLOCK_SIZE;
+ if ((trun_sqnum > d_sqnum && trun_size < d_sz) ||
+ (ino_sqnum > d_sqnum && file->ino.size < d_sz)) {
+ /*
+ * The truncated data nodes are not gced after
+ * truncating, just remove them.
+ */
+ list_add(&data_node->list, &drop_list);
+ } else {
+ new_size = max_t(unsigned long long, new_size, d_sz);
+ }
+ }
+ /*
+ * Truncation node is written successful, but inode node is not. It
+ * won't happen because inode node is written before truncation node
+ * according to ubifs_jnl_truncate(), unless only inode is corrupted.
+ * In this case, data nodes could have been removed in history mounting
+ * recovery, so i_size needs to be updated.
+ */
+ if (trun_sqnum > ino_sqnum && trun_size < file->ino.size) {
+ if (trun_size < new_size) {
+ corrupted_truncation = true;
+ /*
+ * Appendant writing after truncation and newest inode
+ * is not fell on disk.
+ */
+ goto update_isize;
+ }
+
+ /*
+ * Overwriting happens after truncation and newest inode is
+ * not fell on disk.
+ */
+ file->calc_size = trun_size;
+ goto drop_data;
+ }
+update_isize:
+ /*
+ * The file cannot use 'new_size' directly when the file may have ever
+ * been set i_size. For example:
+ * 1. echo 123 > file # i_size = 4
+ * 2. truncate -s 100 file # i_size = 100
+ * After scanning, new_size is 4. Apperantly the size of 'file' should
+ * be 100. So, the calculated new_size according to data nodes should
+ * only be used for extending i_size, like ubifs_recover_size() does.
+ */
+ if (new_size > file->ino.size || corrupted_truncation)
+ file->calc_size = new_size;
+ else
+ file->calc_size = file->ino.size;
+
+drop_data:
+ while (!list_empty(&drop_list)) {
+ data_node = list_entry(drop_list.next, struct scanned_data_node,
+ list);
+
+ list_del(&data_node->list);
+ rb_erase(&data_node->rb, &file->data_nodes);
+ kfree(data_node);
+ }
+}
+
+/**
+ * correct_file_info - correct the information of file
+ * @c: UBIFS file-system description object
+ * @file: file object
+ *
+ * This function corrects file information according to calculated fields,
+ * eg. 'calc_nlink', 'calc_xcnt', 'calc_xsz', 'calc_xnms' and 'calc_size'.
+ * Corrected inode node will be re-written.
+ */
+static int correct_file_info(struct ubifs_info *c, struct scanned_file *file)
+{
+ uint32_t crc;
+ int err, lnum, len;
+ struct rb_node *node;
+ struct ubifs_ino_node *ino;
+ struct scanned_file *xattr_file;
+
+ for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
+ xattr_file = rb_entry(node, struct scanned_file, rb);
+
+ err = correct_file_info(c, xattr_file);
+ if (err)
+ return err;
+ }
+
+ if (file->calc_nlink == file->ino.nlink &&
+ file->calc_xcnt == file->ino.xcnt &&
+ file->calc_xsz == file->ino.xsz &&
+ file->calc_xnms == file->ino.xnms &&
+ file->calc_size == file->ino.size)
+ return 0;
+
+ lnum = file->ino.header.lnum;
+ dbg_fsck("correct file(inum:%lu type:%s), nlink %u->%u, xattr cnt %u->%u, xattr size %u->%u, xattr names %u->%u, size %llu->%llu, at %d:%d, in %s",
+ file->inum, file->ino.is_xattr ? "xattr" :
+ ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
+ file->ino.nlink, file->calc_nlink,
+ file->ino.xcnt, file->calc_xcnt,
+ file->ino.xsz, file->calc_xsz,
+ file->ino.xnms, file->calc_xnms,
+ file->ino.size, file->calc_size,
+ lnum, file->ino.header.offs, c->dev_name);
+
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ ino = c->sbuf + file->ino.header.offs;
+ ino->nlink = cpu_to_le32(file->calc_nlink);
+ ino->xattr_cnt = cpu_to_le32(file->calc_xcnt);
+ ino->xattr_size = cpu_to_le32(file->calc_xsz);
+ ino->xattr_names = cpu_to_le32(file->calc_xnms);
+ ino->size = cpu_to_le64(file->calc_size);
+ len = le32_to_cpu(ino->ch.len);
+ crc = crc32(UBIFS_CRC32_INIT, (void *)ino + 8, len - 8);
+ ino->ch.crc = cpu_to_le32(crc);
+
+ /* Atomically write the fixed LEB back again */
+ return ubifs_leb_change(c, lnum, c->sbuf, c->leb_size);
+}
+
+/**
+ * check_and_correct_files - check and correct information of files.
+ * @c: UBIFS file-system description object
+ *
+ * This function does similar things with dbg_check_filesystem(), besides,
+ * it also corrects file information if the calculated information is not
+ * consistent with information from flash.
+ */
+int check_and_correct_files(struct ubifs_info *c)
+{
+ int err;
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ calculate_file_info(c, file, tree);
+ }
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ err = correct_file_info(c, file);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index d378a06c..124c8f12 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -272,6 +272,7 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
struct rb_root *file_tree);
bool file_is_reachable(struct ubifs_info *c, struct scanned_file *file,
struct rb_root *file_tree);
+int check_and_correct_files(struct ubifs_info *c);
/* rebuild_fs.c */
int ubifs_rebuild_filesystem(struct ubifs_info *c);
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 669b61d1..ecf6b0c6 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -677,6 +677,12 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
log_out(c, "Extract reachable files");
extract_dentry_tree(c);
+ /* Step 6: Check & correct files' information. */
+ log_out(c, "Check & correct file information");
+ err = check_and_correct_files(c);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
out:
destroy_scanned_info(c, &si);
destroy_rebuild_info(c);
diff --git a/ubifs-utils/libubifs/debug.c b/ubifs-utils/libubifs/debug.c
index 94928da3..eaf403f9 100644
--- a/ubifs-utils/libubifs/debug.c
+++ b/ubifs-utils/libubifs/debug.c
@@ -69,7 +69,7 @@ static const char *get_key_type(int type)
}
}
-static const char *get_dent_type(int type)
+const char *ubifs_get_type_name(int type)
{
switch (type) {
case UBIFS_ITYPE_REG:
diff --git a/ubifs-utils/libubifs/debug.h b/ubifs-utils/libubifs/debug.h
index 8da79f80..400b4759 100644
--- a/ubifs-utils/libubifs/debug.h
+++ b/ubifs-utils/libubifs/debug.h
@@ -85,6 +85,7 @@ static inline int dbg_is_chk_index(__unused const struct ubifs_info *c)
{ return 0; }
/* Dump functions */
+const char *ubifs_get_type_name(int type);
const char *dbg_ntype(int type);
const char *dbg_cstate(int cmt_state);
const char *dbg_jhead(int jhead);
--
2.13.6
Add basic process code for fsck.ubifs. There are following modes for fsck:
1. normal mode: Check the filesystem, ask user whether or not to fix
the problem as long as inconsistent data is found during fs checking.
2. safe mode: Check and safely repair the filesystem, if there are any
data dropping operations needed by fixing, fsck will fail.
3. danger mode: Answer 'yes' to all questions. There two sub modes:
a) Check and repair the filesystem according to TNC, data dropping
will be reported. If TNC/master/log is corrupted, fsck will fail.
b) Check and forcedly repair the filesystem according to TNC, turns
to rebuild filesystem if TNC/master/log is corrupted. Always make
fsck succeed.
4. check mode: Make no changes to the filesystem, only check the
filesystem.
5. rebuild mode: Scan entire UBI volume to find all nodes, and rebuild
filesystem, always make fsck success.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
ubifs-utils/Makemodule.am | 12 ++
ubifs-utils/common/defs.h | 3 +-
ubifs-utils/fsck.ubifs/.gitignore | 1 +
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 259 ++++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 91 +++++++++++++
ubifs-utils/libubifs/debug.c | 8 +-
ubifs-utils/libubifs/super.c | 52 ++++++++
ubifs-utils/libubifs/ubifs.h | 8 ++
9 files changed, 432 insertions(+), 3 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/.gitignore
create mode 100644 ubifs-utils/fsck.ubifs/fsck.ubifs.c
create mode 100644 ubifs-utils/fsck.ubifs/fsck.ubifs.h
diff --git a/.gitignore b/.gitignore
index 8c51ee5f..3eb66c14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ flash_otp_lock
flash_otp_write
flash_unlock
flashcp
+fsck.ubifs
ftl_check
ftl_format
jffs2dump
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 936b49fc..0ed91dff 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -75,8 +75,20 @@ mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUI
mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
-I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs -rdynamic
+fsck_ubifs_SOURCES = \
+ $(common_SOURCES) \
+ $(libubifs_SOURCES) \
+ ubifs-utils/fsck.ubifs/fsck.ubifs.h \
+ ubifs-utils/fsck.ubifs/fsck.ubifs.c
+
+fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
+fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
+ -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs \
+ -I$(top_srcdir)/ubifs-utils/fsck.ubifs -rdynamic
+
EXTRA_DIST += ubifs-utils/common/README ubifs-utils/libubifs/README
dist_sbin_SCRIPTS = ubifs-utils/mount.ubifs
sbin_PROGRAMS += mkfs.ubifs
+sbin_PROGRAMS += fsck.ubifs
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index 79451c40..8c8347a9 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -23,8 +23,9 @@ extern struct ubifs_info info_;
#include "common.h"
#define MKFS_PROGRAM_NAME "mkfs.ubifs"
+#define FSCK_PROGRAM_NAME "fsck.ubifs"
-enum { MKFS_PROGRAM_TYPE = 0 };
+enum { MKFS_PROGRAM_TYPE = 0, FSCK_PROGRAM_TYPE };
enum {
DUMP_PREFIX_NONE,
diff --git a/ubifs-utils/fsck.ubifs/.gitignore b/ubifs-utils/fsck.ubifs/.gitignore
new file mode 100644
index 00000000..09d664aa
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/.gitignore
@@ -0,0 +1 @@
+/fsck.ubifs
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
new file mode 100644
index 00000000..b1286150
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+/*
+ * Because we copy functions from the kernel, we use a subset of the UBIFS
+ * file-system description object struct ubifs_info.
+ */
+struct ubifs_info info_;
+static struct ubifs_info *c = &info_;
+
+int exit_code = FSCK_OK;
+
+static const char *optstring = "Vrg:abyn";
+
+static const struct option longopts[] = {
+ {"version", 0, NULL, 'V'},
+ {"reserve", 1, NULL, 'r'},
+ {"debug", 1, NULL, 'g'},
+ {"auto", 1, NULL, 'a'},
+ {"rebuild", 1, NULL, 'b'},
+ {"yes", 1, NULL, 'y'},
+ {"nochange", 1, NULL, 'n'},
+ {NULL, 0, NULL, 0}
+};
+
+static const char *helptext =
+"Usage: fsck.ubifs [OPTIONS] ubi_volume\n"
+"Check & repair UBIFS filesystem on a given UBI volume\n\n"
+"Options:\n"
+"-V, --version Display version information\n"
+"-g, --debug=LEVEL Display debug information (0 - none, 1 - error message,\n"
+" 2 - warning message[default], 3 - notice message, 4 - debug message)\n"
+"-a, --auto Automatic safely repair without droping data (No questions).\n"
+" Can not be specified at the same time as the -y or -n options\n"
+"-y, --yes Assume \"yes\" to all questions. Automatic repair and report dropping data (No questions).\n"
+" There are two submodes for this working mode:\n"
+" a. default - Fail if TNC/master/log is corrupted. Only -y option is specified\n"
+" b. rebuild fs - Turn to rebuild fs if TNC/master/log is corrupted. Specify -b option to make effect\n"
+" Can not be specified at the same time as the -a or -n options\n"
+"-b, --rebuild Forcedly repair the filesystem even by rebuilding filesystem.\n"
+" Depends on -y option\n"
+"-n, --nochange Make no changes to the filesystem, only check filesystem.\n"
+" Can not be specified at the same time as the -a or -y options\n"
+"Examples:\n"
+"\t1. Check and repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t fsck.ubifs /dev/ubi0_0\n"
+"\t2. Only check without modifying filesystem from UBI volume /dev/ubi0_0\n"
+"\t fsck.ubifs -n /dev/ubi0_0\n"
+"\t3. Check and safely repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t fsck.ubifs -a /dev/ubi0_0\n"
+"\t4. Check and forcedly repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t fsck.ubifs -y -b /dev/ubi0_0\n\n";
+
+static inline void usage(void)
+{
+ printf("%s", helptext);
+ exit_code |= FSCK_USAGE;
+ exit(exit_code);
+}
+
+static void get_options(int argc, char* argv[], int *mode)
+{
+ int opt, i, submode = 0;
+ char *endp;
+
+ while (1) {
+ opt = getopt_long(argc, argv, optstring, longopts, &i);
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'V':
+ common_print_version();
+ exit(FSCK_OK);
+ case 'g':
+ c->debug_level = strtol(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ c->debug_level < 0 || c->debug_level > DEBUG_LEVEL) {
+ log_err(c, 0, "bad debugging level '%s'", optarg);
+ usage();
+ }
+ break;
+ case 'a':
+ if (*mode != NORMAL_MODE) {
+ conflict_opt:
+ log_err(c, 0, "Only one of the options -a, -n or -y may be specified");
+ usage();
+ }
+ *mode = SAFE_MODE;
+ break;
+ case 'y':
+ if (*mode != NORMAL_MODE)
+ goto conflict_opt;
+ *mode = DANGER_MODE0;
+ break;
+ case 'b':
+ submode = 1;
+ break;
+ case 'n':
+ if (*mode != NORMAL_MODE)
+ goto conflict_opt;
+ *mode = CHECK_MODE;
+ c->ro_mount = 1;
+ break;
+ case 'r':
+ /* Compatible with FSCK(8). */
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (submode) {
+ if (*mode != DANGER_MODE0) {
+ log_err(c, 0, "Option -y is not specified when -b is used");
+ usage();
+ } else
+ *mode = DANGER_MODE1;
+ }
+
+ if (optind != argc) {
+ c->dev_name = strdup(argv[optind]);
+ if (!c->dev_name) {
+ log_err(c, errno, "can not allocate dev_name");
+ exit_code |= FSCK_ERROR;
+ exit(exit_code);
+ }
+ }
+
+ if (!c->dev_name) {
+ log_err(c, 0, "no ubi_volume specified");
+ usage();
+ }
+}
+
+static void exit_callback(void)
+{
+ if (exit_code & FSCK_NONDESTRUCT)
+ log_out(c, "********** Filesystem was modified **********");
+ if (exit_code & FSCK_UNCORRECTED)
+ log_out(c, "********** WARNING: Filesystem still has errors **********");
+ if (exit_code & ~(FSCK_OK|FSCK_NONDESTRUCT))
+ log_out(c, "FSCK failed, exit code %d", exit_code);
+ else
+ log_out(c, "FSCK success!");
+}
+
+static void fsck_assert_failed(__unused const struct ubifs_info *c)
+{
+ exit_code |= FSCK_ERROR;
+ exit(exit_code);
+}
+
+static void signal_cancel(int sig)
+{
+ ubifs_warn(c, "killed by signo %d", sig);
+ exit_code |= FSCK_CANCELED;
+ exit(exit_code);
+}
+
+static int init_fsck_info(struct ubifs_info *c, int argc, char *argv[])
+{
+ int err = 0, mode = NORMAL_MODE;
+ struct sigaction sa;
+ struct ubifs_fsck_info *fsck = NULL;
+
+ if (atexit(exit_callback)) {
+ log_err(c, errno, "can not set exit callback");
+ return -errno;
+ }
+
+ init_ubifs_info(c, FSCK_PROGRAM_TYPE);
+ get_options(argc, argv, &mode);
+
+ fsck = calloc(1, sizeof(struct ubifs_fsck_info));
+ if (!fsck) {
+ err = -errno;
+ log_err(c, errno, "can not allocate fsck info");
+ goto out_err;
+ }
+
+ c->private = fsck;
+ FSCK(c)->mode = mode;
+ c->assert_failed_cb = fsck_assert_failed;
+
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) {
+ err = -errno;
+ log_err(c, errno, "can not set signal handler");
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ free(fsck);
+ free(c->dev_name);
+ c->dev_name = NULL;
+ return err;
+}
+
+static void destroy_fsck_info(struct ubifs_info *c)
+{
+ free(c->private);
+ c->private = NULL;
+ free(c->dev_name);
+ c->dev_name = NULL;
+}
+
+/*
+ * do_fsck - Check & repair the filesystem.
+ */
+static int do_fsck(void)
+{
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+
+ err = init_fsck_info(c, argc, argv);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_exit;
+ }
+
+ err = ubifs_open_volume(c, c->dev_name);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_destroy_fsck;
+ }
+
+ err = do_fsck();
+
+ ubifs_close_volume(c);
+out_destroy_fsck:
+ destroy_fsck_info(c);
+out_exit:
+ return exit_code;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
new file mode 100644
index 00000000..d267dd24
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#ifndef __FSCK_UBIFS_H__
+#define __FSCK_UBIFS_H__
+
+/* Exit codes used by fsck-type programs */
+#define FSCK_OK 0 /* No errors */
+#define FSCK_NONDESTRUCT 1 /* File system errors corrected */
+#define FSCK_REBOOT 2 /* System should be rebooted */
+#define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */
+#define FSCK_ERROR 8 /* Operational error */
+#define FSCK_USAGE 16 /* Usage or syntax error */
+#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
+#define FSCK_LIBRARY 128 /* Shared library error */
+
+/*
+ * There are 6 working modes for fsck:
+ * NORMAL_MODE: Check the filesystem, ask user whether or not to fix the
+ * problem as long as inconsistent data is found during checking.
+ * SAFE_MODE: Check and safely repair the filesystem, if there are any
+ * data dropping operations needed by fixing, fsck will fail.
+ * DANGER_MODE0:Check and repair the filesystem according to TNC, data dropping
+ * will be reported. If TNC/master/log is corrupted, fsck will fail.
+ * DANGER_MODE1:Check and forcedly repair the filesystem according to TNC,
+ * turns to @REBUILD_MODE mode automatically if TNC/master/log is
+ * corrupted.
+ * REBUILD_MODE:Scan entire UBI volume to find all nodes, and rebuild the
+ * filesystem, always make fsck success.
+ * CHECK_MODE: Make no changes to the filesystem, only check the filesystem.
+ */
+enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
+ DANGER_MODE1, REBUILD_MODE, CHECK_MODE };
+
+/**
+ * struct ubifs_fsck_info - UBIFS fsck information.
+ * @mode: working mode
+ */
+struct ubifs_fsck_info {
+ int mode;
+};
+
+#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
+
+static inline const char *mode_name(const struct ubifs_info *c)
+{
+ if (!c->private)
+ return "";
+
+ switch (FSCK(c)->mode) {
+ case NORMAL_MODE:
+ return ",normal mode";
+ case SAFE_MODE:
+ return ",safe mode";
+ case DANGER_MODE0:
+ return ",danger mode";
+ case DANGER_MODE1:
+ return ",danger + rebuild mode";
+ case REBUILD_MODE:
+ return ",rebuild mode";
+ case CHECK_MODE:
+ return ",check mode";
+ default:
+ return "";
+ }
+}
+
+#define log_out(c, fmt, ...) do { \
+ printf("%s[%d] (%s%s): " fmt "\n", c->program_name ? : "noprog",\
+ getpid(), c->dev_name ? : "-", mode_name(c), \
+ ##__VA_ARGS__); \
+} while (0)
+
+#define log_err(c, err, fmt, ...) do { \
+ printf("%s[%d][ERROR] (%s%s): %s: " fmt, \
+ c->program_name ? : "noprog", getpid(), \
+ c->dev_name ? : "-", mode_name(c), \
+ __FUNCTION__, ##__VA_ARGS__); \
+ if (err) \
+ printf(" - %s", strerror(err)); \
+ printf("\n"); \
+} while (0)
+
+/* Exit code for fsck program. */
+extern int exit_code;
+
+#endif
diff --git a/ubifs-utils/libubifs/debug.c b/ubifs-utils/libubifs/debug.c
index a2109906..94928da3 100644
--- a/ubifs-utils/libubifs/debug.c
+++ b/ubifs-utils/libubifs/debug.c
@@ -1021,7 +1021,11 @@ void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
/*
* Different from linux kernel.
- * There is only one action(readonly) when assertion is failed.
+ * Invoke callback function if there is one, otherwise make filesystem
+ * readonly when assertion is failed.
*/
- ubifs_ro_mode(c, -EINVAL);
+ if (c->assert_failed_cb)
+ c->assert_failed_cb(c);
+ else
+ ubifs_ro_mode(c, -EINVAL);
}
diff --git a/ubifs-utils/libubifs/super.c b/ubifs-utils/libubifs/super.c
index 9fa366f3..155489d9 100644
--- a/ubifs-utils/libubifs/super.c
+++ b/ubifs-utils/libubifs/super.c
@@ -138,6 +138,53 @@ int close_target(struct ubifs_info *c)
}
/**
+ * ubifs_open_volume - open UBI volume.
+ * @c: the UBIFS file-system description object
+ * @volume_name: the UBI volume name
+ *
+ * Open ubi volume. This function is implemented by open_ubi + open_target.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
+ */
+int ubifs_open_volume(struct ubifs_info *c, const char *volume_name)
+{
+ int err;
+
+ err = open_ubi(c, volume_name);
+ if (err) {
+ ubifs_err(c, "cannot open libubi. %s", strerror(errno));
+ return err;
+ }
+
+ err = open_target(c);
+ if (err)
+ close_ubi(c);
+
+ return err;
+}
+
+/**
+ * ubifs_close_volume - close UBI volume.
+ * @c: the UBIFS file-system description object
+ *
+ * Close ubi volume. This function is implemented by close_target + close_ubi.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
+ */
+int ubifs_close_volume(struct ubifs_info *c)
+{
+ int err;
+
+ err = close_target(c);
+ if (err)
+ return err;
+
+ close_ubi(c);
+
+ return 0;
+}
+
+/**
* check_volume_empty - check if the UBI volume is empty.
* @c: the UBIFS file-system description object
*
@@ -197,6 +244,11 @@ void init_ubifs_info(struct ubifs_info *c, int program_type)
case MKFS_PROGRAM_TYPE:
c->program_name = MKFS_PROGRAM_NAME;
break;
+ case FSCK_PROGRAM_TYPE:
+ c->program_name = FSCK_PROGRAM_NAME;
+ /* Always check crc for data node. */
+ c->no_chk_data_crc = 0;
+ break;
default:
assert(0);
break;
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 2af9d87b..babaae8f 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1026,6 +1026,9 @@ struct ubifs_budg_info {
*
* @new_ihead_lnum: used by debugging to check @c->ihead_lnum
* @new_ihead_offs: used by debugging to check @c->ihead_offs
+ *
+ * @private: private information related to specific situation, eg. fsck.
+ * @assert_failed_cb: callback function to handle assertion failure
*/
struct ubifs_info {
struct ubifs_sb_node *sup_node;
@@ -1248,6 +1251,9 @@ struct ubifs_info {
int new_ihead_lnum;
int new_ihead_offs;
+
+ void *private;
+ void (*assert_failed_cb)(const struct ubifs_info *c);
};
extern atomic_long_t ubifs_clean_zn_cnt;
@@ -1684,6 +1690,8 @@ int open_ubi(struct ubifs_info *c, const char *node);
void close_ubi(struct ubifs_info *c);
int open_target(struct ubifs_info *c);
int close_target(struct ubifs_info *c);
+int ubifs_open_volume(struct ubifs_info *c, const char *volume_name);
+int ubifs_close_volume(struct ubifs_info *c);
int check_volume_empty(struct ubifs_info *c);
void init_ubifs_info(struct ubifs_info *c, int program_type);
int init_constants_early(struct ubifs_info *c);
--
2.13.6
This is the 3/12 step of rebuilding. Generate file according to left valid
inode nodes and dentry nodes. Based on the results from step 2, it is easy
to understand:
Step 2 has done:
valid_inos - del_inos = left_inos
valid_dents - del_dents = left_dents
Step 3 should do:
Traverse left_inos and left_dents, insert inode/dentry nodes into
corresponding file.
After that, all files are generated by scanning, the next thing to do is
dropping invalid files(eg. nonconsistent file type between inode node and
dentry nodes, file has no dentry nodes(excepts '/'), encrypted file has
no xattr information, etc.), which will be done in next step.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/rebuild_fs.c | 61 +++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index dbb0f3bc..a86430d0 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -476,6 +476,61 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
}
/**
+ * add_valid_nodes_into_file - add valid nodes into file.
+ * @c: UBIFS file-system description object
+ * @si: records nodes and files information during scanning
+ *
+ * This function adds valid nodes into corresponding file, all valid ino/dent
+ * nodes will be removed from @si->valid_inos/@si->valid_dents if the function
+ * is executed successfully.
+ */
+static int add_valid_nodes_into_file(struct ubifs_info *c,
+ struct scanned_info *si)
+{
+ int err, type;
+ ino_t inum;
+ struct scanned_node *sn;
+ struct scanned_ino_node *ino_node;
+ struct scanned_dent_node *dent_node;
+ struct rb_node *this;
+ struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+
+ this = rb_first(&si->valid_inos);
+ while (this) {
+ ino_node = rb_entry(this, struct scanned_ino_node, rb);
+ this = rb_next(this);
+
+ sn = (struct scanned_node *)ino_node;
+ type = key_type(c, &ino_node->key);
+ inum = key_inum(c, &ino_node->key);
+ err = insert_or_update_file(c, tree, sn, type, inum);
+ if (err)
+ return err;
+
+ rb_erase(&ino_node->rb, &si->valid_inos);
+ kfree(ino_node);
+ }
+
+ this = rb_first(&si->valid_dents);
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ sn = (struct scanned_node *)dent_node;
+ inum = dent_node->inum;
+ type = key_type(c, &dent_node->key);
+ err = insert_or_update_file(c, tree, sn, type, inum);
+ if (err)
+ return err;
+
+ rb_erase(&dent_node->rb, &si->valid_dents);
+ kfree(dent_node);
+ }
+
+ return 0;
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -509,6 +564,12 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
log_out(c, "Remove deleted nodes");
remove_del_nodes(c, &si);
+ /* Step 3: Add valid nodes into file. */
+ log_out(c, "Add valid nodes into file");
+ err = add_valid_nodes_into_file(c, &si);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
out:
destroy_scanned_info(c, &si);
destroy_rebuild_info(c);
--
2.13.6
This is the 7/12 step of rebuilding. Record used LEBs which may hold
useful nodes, then left unused LEBs could be taken for storing new index
tree. Notice, LEB that contains effective nodes on deleted trees in step
1 is regarded as used.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 115 +++++++++++++++++++++++++++++++++++-
2 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 124c8f12..0b065935 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -180,9 +180,11 @@ struct scanned_file {
/**
* ubifs_rebuild_info - UBIFS rebuilding information.
+ * @used_lebs: a bitmap used for recording used lebs
* @scanned_files: tree of all scanned files
*/
struct ubifs_rebuild_info {
+ unsigned long *used_lebs;
struct rb_root scanned_files;
};
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index ecf6b0c6..f5412f08 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -48,19 +48,29 @@ static int init_rebuild_info(struct ubifs_info *c)
if (!FSCK(c)->rebuild) {
err = -ENOMEM;
log_err(c, errno, "can not allocate rebuild info");
- goto out;
+ goto free_sbuf;
}
FSCK(c)->rebuild->scanned_files = RB_ROOT;
+ FSCK(c)->rebuild->used_lebs = kcalloc(BITS_TO_LONGS(c->main_lebs),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!FSCK(c)->rebuild->used_lebs) {
+ err = -ENOMEM;
+ log_err(c, errno, "can not allocate bitmap of used lebs");
+ goto free_rebuild;
+ }
return 0;
-out:
+free_rebuild:
+ kfree(FSCK(c)->rebuild);
+free_sbuf:
vfree(c->sbuf);
return err;
}
static void destroy_rebuild_info(struct ubifs_info *c)
{
+ kfree(FSCK(c)->rebuild->used_lebs);
kfree(FSCK(c)->rebuild);
vfree(c->sbuf);
}
@@ -451,6 +461,9 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_ino_node = lookup_valid_ino_node(c, si, del_ino_node);
if (valid_ino_node) {
+ int lnum = del_ino_node->header.lnum;
+
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
rb_erase(&valid_ino_node->rb, &si->valid_inos);
kfree(valid_ino_node);
}
@@ -466,6 +479,9 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_dent_node = lookup_valid_dent_node(c, si, del_dent_node);
if (valid_dent_node) {
+ int lnum = del_dent_node->header.lnum;
+
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
rb_erase(&valid_dent_node->rb, &si->valid_dents);
kfree(valid_dent_node);
}
@@ -627,6 +643,93 @@ static void extract_dentry_tree(struct ubifs_info *c)
}
}
+static const char *get_file_name(struct ubifs_info *c, struct scanned_file *file)
+{
+ static char name[UBIFS_MAX_NLEN + 1];
+ struct rb_node *node;
+ struct scanned_dent_node *dent_node;
+
+ node = rb_first(&file->dent_nodes);
+ if (!node) {
+ ubifs_assert(c, file->inum == UBIFS_ROOT_INO);
+ return "/";
+ }
+
+ if (c->encrypted && !file->ino.is_xattr)
+ /* Encrypted file name. */
+ return "<encrypted>";
+
+ /* Get name from any one dentry. */
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+ memcpy(name, dent_node->name, dent_node->nlen);
+ /* @dent->name could be non '\0' terminated. */
+ name[dent_node->nlen] = '\0';
+ return name;
+}
+
+static void record_file_used_lebs(struct ubifs_info *c,
+ struct scanned_file *file)
+{
+ int lnum;
+ struct rb_node *node;
+ struct scanned_file *xattr_file;
+ struct scanned_dent_node *dent_node;
+ struct scanned_data_node *data_node;
+
+ dbg_fsck("recovered file(inum:%lu name:%s type:%s), in %s",
+ file->inum, get_file_name(c, file),
+ file->ino.is_xattr ? "xattr" :
+ ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
+ c->dev_name);
+
+ lnum = file->ino.header.lnum;
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+
+ if (file->trun.header.exist) {
+ lnum = file->trun.header.lnum;
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ }
+
+ for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) {
+ data_node = rb_entry(node, struct scanned_data_node, rb);
+
+ lnum = data_node->header.lnum;
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ }
+
+ for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+
+ lnum = dent_node->header.lnum;
+ set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ }
+
+ for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
+ xattr_file = rb_entry(node, struct scanned_file, rb);
+
+ record_file_used_lebs(c, xattr_file);
+ }
+}
+
+/**
+ * record_used_lebs - record used LEBs.
+ * @c: UBIFS file-system description object
+ *
+ * This function records all used LEBs which may hold useful nodes, then left
+ * unused LEBs could be taken for storing new index tree.
+ */
+static void record_used_lebs(struct ubifs_info *c)
+{
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ record_file_used_lebs(c, file);
+ }
+}
+
/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
@@ -680,8 +783,14 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 6: Check & correct files' information. */
log_out(c, "Check & correct file information");
err = check_and_correct_files(c);
- if (err)
+ if (err) {
exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 7: Record used LEBs. */
+ log_out(c, "Record used LEBs");
+ record_used_lebs(c);
out:
destroy_scanned_info(c, &si);
--
2.13.6
This is the 5/12 step of rebuilding. Extract reachable directory entries
tree. Make sure that all files can be searched from '/', unreachable
file is deleted. So, all files can be accessible in userspace after
reparing.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 94 ++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 6 +++
ubifs-utils/fsck.ubifs/rebuild_fs.c | 44 ++++++++++++++++
3 files changed, 144 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index 772e3003..dd5cb310 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -525,6 +525,7 @@ static int update_file(struct ubifs_info *c, struct scanned_file *file,
{
struct scanned_dent_node *dent = (struct scanned_dent_node *)sn;
+ dent->file = file;
err = insert_file_dentry(file, dent);
break;
}
@@ -888,3 +889,96 @@ check_dent_node:
return true;
}
+
+static bool dentry_is_reachable(struct ubifs_info *c,
+ struct scanned_dent_node *dent_node,
+ struct list_head *path_list,
+ struct rb_root *file_tree)
+{
+ struct scanned_file *parent_file = NULL;
+ struct scanned_dent_node *dn, *parent_dent;
+ struct rb_node *p;
+
+ /* Check whether the path is cyclical. */
+ list_for_each_entry(dn, path_list, list) {
+ if (dn == dent_node)
+ return false;
+ }
+
+ /* Quick path, dentry has already been checked as reachable. */
+ if (dent_node->can_be_found)
+ return true;
+
+ dent_node->can_be_found = true;
+ list_add(&dent_node->list, path_list);
+
+ parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
+ /* Parent dentry is not found, unreachable. */
+ if (!parent_file)
+ return false;
+
+ /* Parent dentry is '/', reachable. */
+ if (parent_file->inum == UBIFS_ROOT_INO)
+ return true;
+
+ p = rb_first(&parent_file->dent_nodes);
+ if (!p)
+ return false;
+ parent_dent = rb_entry(p, struct scanned_dent_node, rb);
+
+ return dentry_is_reachable(c, parent_dent, path_list, file_tree);
+}
+
+/**
+ * file_is_reachable - whether the file can be found from '/'.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @file_tree: tree of all scanned files
+ *
+ * This function iterates all directory entries in given @file and checks
+ * whether each dentry is reachable. All unreachable directory entries will
+ * be removed.
+ */
+bool file_is_reachable(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree)
+{
+ struct rb_node *node;
+ struct scanned_dent_node *dent_node;
+
+ if (file->inum == UBIFS_ROOT_INO)
+ goto reachable;
+
+retry:
+ for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
+ LIST_HEAD(path_list);
+
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+
+ if (dentry_is_reachable(c, dent_node, &path_list, file_tree))
+ continue;
+
+ while (!list_empty(&path_list)) {
+ dent_node = list_entry(path_list.next,
+ struct scanned_dent_node, list);
+
+ dbg_fsck("remove unreachable dentry %s, in %s",
+ c->encrypted && !file->ino.is_xattr ?
+ "<encrypted>" : dent_node->name, c->dev_name);
+ list_del(&dent_node->list);
+ rb_erase(&dent_node->rb, &dent_node->file->dent_nodes);
+ kfree(dent_node);
+ }
+
+ /* Since dentry node is removed from rb-tree, rescan rb-tree. */
+ goto retry;
+ }
+
+ if (!rb_first(&file->dent_nodes)) {
+ dbg_fsck("file %lu is unreachable, in %s", file->inum, c->dev_name);
+ return false;
+ }
+
+reachable:
+ dbg_fsck("file %lu is reachable, in %s", file->inum, c->dev_name);
+ return true;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 3885d138..d378a06c 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -39,6 +39,8 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/* Types of inconsistent problems */
enum { SB_CORRUPTED = 0 };
+struct scanned_file;
+
/**
* scanned_node - common header node.
* @exist: whether the node is found by scanning
@@ -92,6 +94,7 @@ struct scanned_ino_node {
* @nlen: name length
* @name: dentry name
* @inum: target inode number
+ * @file: corresponding file
* @rb: link in the trees of:
* 1) valid dentry nodes or deleted dentry node
* 2) all scanned dentry nodes from same file
@@ -105,6 +108,7 @@ struct scanned_dent_node {
unsigned int nlen;
char name[UBIFS_MAX_NLEN + 1];
ino_t inum;
+ struct scanned_file *file;
struct rb_node rb;
struct list_head list;
};
@@ -266,6 +270,8 @@ void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum);
bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
struct rb_root *file_tree);
+bool file_is_reachable(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree);
/* rebuild_fs.c */
int ubifs_rebuild_filesystem(struct ubifs_info *c);
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 31f1b3ba..669b61d1 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -588,6 +588,46 @@ static void filter_invalid_files(struct ubifs_info *c)
}
/**
+ * extract_dentry_tree - extract reachable directory entries.
+ * @c: UBIFS file-system description object
+ *
+ * This function iterates all directory entries and remove those
+ * unreachable ones. 'Unreachable' means that a directory entry can
+ * not be searched from '/'.
+ */
+static void extract_dentry_tree(struct ubifs_info *c)
+{
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ LIST_HEAD(unreachable);
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ /*
+ * Since all xattr files are already attached to corresponding
+ * host file, there are only non-xattr files in the file tree.
+ */
+ ubifs_assert(c, !file->ino.is_xattr);
+ if (!file_is_reachable(c, file, tree))
+ list_add(&file->list, &unreachable);
+ }
+
+ /* Remove unreachable files. */
+ while (!list_empty(&unreachable)) {
+ file = list_entry(unreachable.next, struct scanned_file, list);
+
+ dbg_fsck("remove unreachable file %lu, in %s",
+ file->inum, c->dev_name);
+ list_del(&file->list);
+ destroy_file_content(c, file);
+ rb_erase(&file->rb, tree);
+ kfree(file);
+ }
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -633,6 +673,10 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
log_out(c, "Filter invalid files");
filter_invalid_files(c);
+ /* Step 5: Extract reachable directory entries. */
+ log_out(c, "Extract reachable files");
+ extract_dentry_tree(c);
+
out:
destroy_scanned_info(c, &si);
destroy_rebuild_info(c);
--
2.13.6
This is a preparation for building TNC, there must at least one file
in filesystem, if not, just create new root dir.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 6 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 213 +++++++++++++++++++++++++++++++++++-
2 files changed, 217 insertions(+), 2 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index f027ec3f..bc1d7503 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -183,11 +183,17 @@ struct scanned_file {
* @used_lebs: a bitmap used for recording used lebs
* @lpts: lprops table
* @scanned_files: tree of all scanned files
+ * @write_buf: write buffer for LEB @head_lnum
+ * @head_lnum: current writing LEB number
+ * @head_offs: current writing position in LEB @head_lnum
*/
struct ubifs_rebuild_info {
unsigned long *used_lebs;
struct ubifs_lprops *lpts;
struct rb_root scanned_files;
+ void *write_buf;
+ int head_lnum;
+ int head_offs;
};
/**
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 1b03d6c0..62bd4128 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -65,9 +65,17 @@ static int init_rebuild_info(struct ubifs_info *c)
log_err(c, errno, "can not allocate lpts");
goto free_used_lebs;
}
+ FSCK(c)->rebuild->write_buf = vmalloc(c->leb_size);
+ if (!FSCK(c)->rebuild->write_buf) {
+ err = -ENOMEM;
+ goto free_lpts;
+ }
+ FSCK(c)->rebuild->head_lnum = -1;
return 0;
+free_lpts:
+ kfree(FSCK(c)->rebuild->lpts);
free_used_lebs:
kfree(FSCK(c)->rebuild->used_lebs);
free_rebuild:
@@ -79,6 +87,7 @@ free_sbuf:
static void destroy_rebuild_info(struct ubifs_info *c)
{
+ vfree(FSCK(c)->rebuild->write_buf);
kfree(FSCK(c)->rebuild->lpts);
kfree(FSCK(c)->rebuild->used_lebs);
kfree(FSCK(c)->rebuild);
@@ -661,6 +670,197 @@ static void extract_dentry_tree(struct ubifs_info *c)
}
}
+static void init_root_ino(struct ubifs_info *c, struct ubifs_ino_node *ino)
+{
+#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+ __le64 tmp_le64;
+
+ /* Create default root inode */
+ ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
+ ino->ch.node_type = UBIFS_INO_NODE;
+ ino->creat_sqnum = cpu_to_le64(++c->max_sqnum);
+ ino->nlink = cpu_to_le32(2);
+ tmp_le64 = cpu_to_le64(time(NULL));
+ ino->atime_sec = tmp_le64;
+ ino->ctime_sec = tmp_le64;
+ ino->mtime_sec = tmp_le64;
+ ino->atime_nsec = 0;
+ ino->ctime_nsec = 0;
+ ino->mtime_nsec = 0;
+ ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
+ ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ);
+ /* Set compression enabled by default */
+ ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
+}
+
+/**
+ * get_free_leb - get a free LEB according to @FSCK(c)->rebuild->used_lebs.
+ * @c: UBIFS file-system description object
+ *
+ * This function tries to find a free LEB, %0 is returned if found, otherwise
+ * %ENOSPC is returned.
+ */
+static int get_free_leb(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ lnum = find_next_zero_bit(FSCK(c)->rebuild->used_lebs, c->main_lebs, 0);
+ if (lnum >= c->main_lebs) {
+ ubifs_err(c, "No space left.");
+ return -ENOSPC;
+ }
+ set_bit(lnum, FSCK(c)->rebuild->used_lebs);
+ lnum += c->main_first;
+
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+
+ FSCK(c)->rebuild->head_lnum = lnum;
+ FSCK(c)->rebuild->head_offs = 0;
+
+ return 0;
+}
+
+/**
+ * flush_write_buf - flush write buffer.
+ * @c: UBIFS file-system description object
+ *
+ * This function flush write buffer to LEB @FSCK(c)->rebuild->head_lnum, then
+ * set @FSCK(c)->rebuild->head_lnum to '-1'.
+ */
+static int flush_write_buf(struct ubifs_info *c)
+{
+ int len, pad, err;
+
+ if (!FSCK(c)->rebuild->head_offs)
+ return 0;
+
+ len = ALIGN(FSCK(c)->rebuild->head_offs, c->min_io_size);
+ pad = len - FSCK(c)->rebuild->head_offs;
+ if (pad)
+ ubifs_pad(c, FSCK(c)->rebuild->write_buf +
+ FSCK(c)->rebuild->head_offs, pad);
+
+ err = ubifs_leb_write(c, FSCK(c)->rebuild->head_lnum,
+ FSCK(c)->rebuild->write_buf, 0, len);
+ if (err)
+ return err;
+
+ FSCK(c)->rebuild->head_lnum = -1;
+
+ return 0;
+}
+
+/**
+ * reserve_space - reserve enough space to write data.
+ * @c: UBIFS file-system description object
+ * @len: the length of written data
+ * @lnum: the write LEB number is returned here
+ * @offs: the write pos in LEB is returned here
+ *
+ * This function finds target position <@lnum, @offs> to write data with
+ * length of @len.
+ */
+static int reserve_space(struct ubifs_info *c, int len, int *lnum, int *offs)
+{
+ int err;
+
+ if (FSCK(c)->rebuild->head_lnum == -1) {
+get_new:
+ err = get_free_leb(c);
+ if (err)
+ return err;
+ }
+
+ if (len > c->leb_size - FSCK(c)->rebuild->head_offs) {
+ err = flush_write_buf(c);
+ if (err)
+ return err;
+
+ goto get_new;
+ }
+
+ *lnum = FSCK(c)->rebuild->head_lnum;
+ *offs = FSCK(c)->rebuild->head_offs;
+ FSCK(c)->rebuild->head_offs += ALIGN(len, 8);
+
+ return 0;
+}
+
+static void copy_node_data(struct ubifs_info *c, void *node, int offs, int len)
+{
+ memcpy(FSCK(c)->rebuild->write_buf + offs, node, len);
+ memset(FSCK(c)->rebuild->write_buf + offs + len, 0xff, ALIGN(len, 8) - len);
+}
+
+/**
+ * create_root - create root dir.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates root dir.
+ */
+static int create_root(struct ubifs_info *c)
+{
+ int err, lnum, offs;
+ struct ubifs_ino_node *ino;
+ struct scanned_file *file;
+
+ ino = kzalloc(ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size), GFP_KERNEL);
+ if (!ino)
+ return -ENOMEM;
+
+ c->max_sqnum = 0;
+ c->highest_inum = UBIFS_FIRST_INO;
+ init_root_ino(c, ino);
+ err = ubifs_prepare_node_hmac(c, ino, UBIFS_INO_NODE_SZ, -1, 1);
+ if (err)
+ goto out;
+
+ err = reserve_space(c, UBIFS_INO_NODE_SZ, &lnum, &offs);
+ if (err)
+ goto out;
+
+ copy_node_data(c, ino, offs, UBIFS_INO_NODE_SZ);
+
+ err = flush_write_buf(c);
+ if (err)
+ goto out;
+
+ file = kzalloc(sizeof(struct scanned_file), GFP_KERNEL);
+ if (!file) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ file->inum = UBIFS_ROOT_INO;
+ file->dent_nodes = RB_ROOT;
+ file->data_nodes = RB_ROOT;
+ INIT_LIST_HEAD(&file->list);
+
+ file->ino.header.exist = true;
+ file->ino.header.lnum = lnum;
+ file->ino.header.offs = offs;
+ file->ino.header.len = UBIFS_INO_NODE_SZ;
+ file->ino.header.sqnum = le64_to_cpu(ino->ch.sqnum);
+ ino_key_init(c, &file->ino.key, UBIFS_ROOT_INO);
+ file->ino.is_xattr = le32_to_cpu(ino->flags) & UBIFS_XATTR_FL;
+ file->ino.mode = le32_to_cpu(ino->mode);
+ file->calc_nlink = file->ino.nlink = le32_to_cpu(ino->nlink);
+ file->calc_xcnt = file->ino.xcnt = le32_to_cpu(ino->xattr_cnt);
+ file->calc_xsz = file->ino.xsz = le32_to_cpu(ino->xattr_size);
+ file->calc_xnms = file->ino.xnms = le32_to_cpu(ino->xattr_names);
+ file->calc_size = file->ino.size = le64_to_cpu(ino->size);
+
+ rb_link_node(&file->rb, NULL, &FSCK(c)->rebuild->scanned_files.rb_node);
+ rb_insert_color(&file->rb, &FSCK(c)->rebuild->scanned_files);
+
+out:
+ kfree(ino);
+ return err;
+}
+
static const char *get_file_name(struct ubifs_info *c, struct scanned_file *file)
{
static char name[UBIFS_MAX_NLEN + 1];
@@ -741,9 +941,10 @@ static void record_file_used_lebs(struct ubifs_info *c,
*
* This function traverses all nodes from valid files and does following
* things:
- * 1. Record all used LEBs which may hold useful nodes, then left unused
+ * 1. If there are no scanned files, create default empty filesystem.
+ * 2. Record all used LEBs which may hold useful nodes, then left unused
* LEBs could be taken for storing new index tree.
- * 2. Re-write data to prevent failed gc scanning in the subsequent mounting
+ * 3. Re-write data to prevent failed gc scanning in the subsequent mounting
* process caused by corrupted data.
*/
static int traverse_files_and_nodes(struct ubifs_info *c)
@@ -753,6 +954,14 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
struct scanned_file *file;
struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ if (rb_first(tree) == NULL) {
+ /* No scanned files. Create root dir. */
+ log_out(c, "No files found, create empty filesystem");
+ err = create_root(c);
+ if (err)
+ return err;
+ }
+
log_out(c, "Record used LEBs");
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
--
2.13.6
This is the 4/12 step of rebuilding. Filter out invalid files and drop
them, for example:
1. File has no inode node or inode nlink is zero
2. Nonconsistent file type between inode node and dentry nodes
3. File has no dentry nodes(excepts '/')
4. Encrypted file has no xattr information
5. Non regular file has data nodes
6. Directory/xattr file has more than one dentries
7. Xattr file has no host inode, or the host inode is a xattr
...
Valid xattr file will be inserted into corresponding host file's subtree.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 210 +++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 65 +++++++++-
ubifs-utils/libubifs/journal.c | 4 +-
ubifs-utils/libubifs/ubifs.h | 3 +
5 files changed, 282 insertions(+), 3 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index 58aa9db8..772e3003 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -678,3 +678,213 @@ void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
kfree(file);
}
}
+
+/**
+ * lookup_file - lookup file according to inode number.
+ * @file_tree: tree of all scanned files
+ * @inum: inode number
+ *
+ * This function lookups target file from @file_tree according to @inum.
+ */
+struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum)
+{
+ struct scanned_file *file;
+ struct rb_node *p;
+
+ p = file_tree->rb_node;
+ while (p) {
+ file = rb_entry(p, struct scanned_file, rb);
+
+ if (inum < file->inum)
+ p = p->rb_left;
+ else if (inum > file->inum)
+ p = p->rb_right;
+ else
+ return file;
+ }
+
+ return NULL;
+}
+
+/**
+ * insert_xattr_file - insert xattr file into file's subtree.
+ * @c: UBIFS file-system description object
+ * @xattr_file: xattr file
+ * @host_file: host file
+ *
+ * This inserts xattr file into its' host file's subtree.
+ */
+static void insert_xattr_file(struct ubifs_info *c,
+ struct scanned_file *xattr_file,
+ struct scanned_file *host_file)
+{
+ struct scanned_file *tmp_xattr_file;
+ struct rb_node **p, *parent = NULL;
+
+ p = &host_file->xattr_files.rb_node;
+ while (*p) {
+ parent = *p;
+ tmp_xattr_file = rb_entry(parent, struct scanned_file, rb);
+ if (xattr_file->inum < tmp_xattr_file->inum) {
+ p = &(*p)->rb_left;
+ } else if (xattr_file->inum > tmp_xattr_file->inum) {
+ p = &(*p)->rb_right;
+ } else {
+ /* Impossible: Same xattr file is inserted twice. */
+ ubifs_assert(c, 0);
+ }
+ }
+
+ rb_link_node(&xattr_file->rb, parent, p);
+ rb_insert_color(&xattr_file->rb, &host_file->xattr_files);
+}
+
+/**
+ * file_is_valid - check whether the file is valid.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @file_tree: tree of all scanned files
+ *
+ * This function checks whether given @file is valid, following checks will
+ * be performed:
+ * 1. All files have none-zero nlink inode, otherwise they are invalid.
+ * 2. The file type comes from inode and dentries should be consistent,
+ * inconsistent dentries will be deleted.
+ * 3. Directory type or xattr type files only have one dentry. Superfluous
+ * dentries with lower sequence number will be deleted.
+ * 4. Non-regular file doesn't have data nodes. Data nodes are deleted for
+ * non-regular file.
+ * 5. All files must have at least one dentries, except '/', '/' doesn't
+ * have dentries. Non '/' file is invalid if it doesn't have dentries.
+ * 6. Xattr files should have host inode, and host inode cannot be a xattr,
+ * otherwise they are invalid.
+ * 7. Encrypted files should have corresponding xattrs, otherwise they are
+ * invalid.
+ * Xattr file will be inserted into corresponding host file's subtree.
+ *
+ * Returns %true is @file is valid, otherwise %false is returned.
+ * Notice: All xattr files should be traversed before non-xattr files, because
+ * checking item 7 depends on it.
+ */
+bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree)
+{
+ int type;
+ struct rb_node *node;
+ struct scanned_file *parent_file = NULL;
+ struct scanned_dent_node *dent_node;
+ struct scanned_data_node *data_node;
+ LIST_HEAD(drop_list);
+
+ if (!file->ino.header.exist || !file->ino.nlink)
+ return false;
+
+ type = ubifs_get_dent_type(file->ino.mode);
+
+ /* Drop dentry nodes with inconsistent type. */
+ for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
+ int is_xattr = 0;
+
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+
+ if (key_type(c, &dent_node->key) == UBIFS_XENT_KEY)
+ is_xattr = 1;
+ if (is_xattr != file->ino.is_xattr || type != dent_node->type)
+ list_add(&dent_node->list, &drop_list);
+ }
+
+ while (!list_empty(&drop_list)) {
+ dent_node = list_entry(drop_list.next, struct scanned_dent_node,
+ list);
+
+ list_del(&dent_node->list);
+ rb_erase(&dent_node->rb, &file->dent_nodes);
+ kfree(dent_node);
+ }
+
+ if (type != UBIFS_ITYPE_DIR && !file->ino.is_xattr)
+ goto check_data_nodes;
+
+ /*
+ * Make sure that directory/xattr type files only have one dentry.
+ * This work should be done in step 3, but file type could be unknown
+ * for lacking inode information at that time, so do it here.
+ */
+ node = rb_first(&file->dent_nodes);
+ while (node) {
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+ node = rb_next(node);
+ if (!node)
+ break;
+
+ rb_erase(&dent_node->rb, &file->dent_nodes);
+ kfree(dent_node);
+ }
+
+check_data_nodes:
+ if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr)
+ goto check_dent_node;
+
+ /*
+ * Make sure that non regular type files not have data/trun nodes.
+ * This work should be done in step 3, but file type could be unknown
+ * for lacking inode information at that time, so do it here.
+ */
+ file->trun.header.exist = 0;
+ node = rb_first(&file->data_nodes);
+ while (node) {
+ data_node = rb_entry(node, struct scanned_data_node, rb);
+ node = rb_next(node);
+
+ rb_erase(&data_node->rb, &file->data_nodes);
+ kfree(data_node);
+ }
+
+check_dent_node:
+ if (rb_first(&file->dent_nodes)) {
+ if (file->inum == UBIFS_ROOT_INO)
+ /* '/' has no dentries. */
+ return false;
+
+ node = rb_first(&file->dent_nodes);
+ dent_node = rb_entry(node, struct scanned_dent_node, rb);
+ parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
+ } else {
+ /* Non-root files must have dentries. */
+ if (file->inum != UBIFS_ROOT_INO)
+ return false;
+ }
+
+ if (file->ino.is_xattr) {
+ if (!parent_file)
+ /* Host inode is not found. */
+ return false;
+ if (parent_file->ino.is_xattr)
+ /* Host cannot be a xattr file. */
+ return false;
+
+ insert_xattr_file(c, file, parent_file);
+ if (parent_file->ino.is_encrypted) {
+ int nlen = min(dent_node->nlen,
+ strlen(UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT));
+
+ if (!strncmp(dent_node->name,
+ UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, nlen))
+ parent_file->has_encrypted_info = true;
+ }
+ } else {
+ if (parent_file && !S_ISDIR(parent_file->ino.mode))
+ /* Parent file should be directory. */
+ return false;
+
+ /*
+ * Since xattr files are checked in first round, so all
+ * non-xattr files's @has_encrypted_info fields have been
+ * initialized.
+ */
+ if (file->ino.is_encrypted && !file->has_encrypted_info)
+ return false;
+ }
+
+ return true;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index c6c21e99..3885d138 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -263,6 +263,9 @@ int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
struct scanned_node *sn, int key_type, ino_t inum);
void destroy_file_content(struct ubifs_info *c, struct scanned_file *file);
void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
+struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum);
+bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree);
/* rebuild_fs.c */
int ubifs_rebuild_filesystem(struct ubifs_info *c);
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index a86430d0..31f1b3ba 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -531,6 +531,63 @@ static int add_valid_nodes_into_file(struct ubifs_info *c,
}
/**
+ * filter_invalid_files - filter out invalid files.
+ * @c: UBIFS file-system description object
+ *
+ * This function filters out invalid files(eg. inconsistent types between
+ * inode and dentry node, or missing inode/dentry node, or encrypted inode
+ * has no encryption related xattrs, etc.).
+ */
+static void filter_invalid_files(struct ubifs_info *c)
+{
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ LIST_HEAD(tmp_list);
+
+ /* Add all xattr files into a list. */
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ if (file->ino.is_xattr)
+ list_add(&file->list, &tmp_list);
+ }
+
+ /*
+ * Round 1: Traverse xattr files, check whether the xattr file is
+ * valid, move valid xattr file into corresponding host file's subtree.
+ */
+ while (!list_empty(&tmp_list)) {
+ file = list_entry(tmp_list.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ rb_erase(&file->rb, tree);
+ if (!file_is_valid(c, file, tree)) {
+ destroy_file_content(c, file);
+ kfree(file);
+ }
+ }
+
+ /* Round 2: Traverse non-xattr files. */
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ if (!file_is_valid(c, file, tree))
+ list_add(&file->list, &tmp_list);
+ }
+
+ /* Remove invalid files. */
+ while (!list_empty(&tmp_list)) {
+ file = list_entry(tmp_list.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ destroy_file_content(c, file);
+ rb_erase(&file->rb, tree);
+ kfree(file);
+ }
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -567,8 +624,14 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 3: Add valid nodes into file. */
log_out(c, "Add valid nodes into file");
err = add_valid_nodes_into_file(c, &si);
- if (err)
+ if (err) {
exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 4: Drop invalid files. */
+ log_out(c, "Filter invalid files");
+ filter_invalid_files(c);
out:
destroy_scanned_info(c, &si);
diff --git a/ubifs-utils/libubifs/journal.c b/ubifs-utils/libubifs/journal.c
index 37dc3f0e..d3fdb76a 100644
--- a/ubifs-utils/libubifs/journal.c
+++ b/ubifs-utils/libubifs/journal.c
@@ -404,10 +404,10 @@ static void finish_reservation(struct ubifs_info *c)
}
/**
- * get_dent_type - translate VFS inode mode to UBIFS directory entry type.
+ * ubifs_get_dent_type - translate VFS inode mode to UBIFS directory entry type.
* @mode: inode mode
*/
-static int get_dent_type(int mode)
+int ubifs_get_dent_type(int mode)
{
switch (mode & S_IFMT) {
case S_IFREG:
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 21b0ce0a..e6de7cea 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1611,6 +1611,9 @@ int ubifs_log_end_commit(struct ubifs_info *c, int new_ltail_lnum);
int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum);
int ubifs_consolidate_log(struct ubifs_info *c);
+/* journal.c */
+int ubifs_get_dent_type(int mode);
+
/* budget.c */
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req);
void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req);
--
2.13.6
Add parsing functions for each type of nodes, which will be used for
checking the validity of raw node data while reading from TNC or
scanning from UBIFS logical erase block.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/extract_files.c | 405 +++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 106 +++++++++
ubifs-utils/libubifs/debug.h | 2 +
4 files changed, 515 insertions(+), 1 deletion(-)
create mode 100644 ubifs-utils/fsck.ubifs/extract_files.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index 864271ab..a0676a70 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -81,7 +81,8 @@ fsck_ubifs_SOURCES = \
ubifs-utils/fsck.ubifs/fsck.ubifs.h \
ubifs-utils/fsck.ubifs/fsck.ubifs.c \
ubifs-utils/fsck.ubifs/problem.c \
- ubifs-utils/fsck.ubifs/load_fs.c
+ ubifs-utils/fsck.ubifs/load_fs.c \
+ ubifs-utils/fsck.ubifs/extract_files.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
new file mode 100644
index 00000000..dd52ef84
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+static void parse_node_header(int lnum, int offs, int len,
+ unsigned long long sqnum,
+ struct scanned_node *header)
+{
+ header->exist = true;
+ header->lnum = lnum;
+ header->offs = offs;
+ header->len = len;
+ header->sqnum = sqnum;
+}
+
+static inline bool inode_can_be_encrypted(struct ubifs_info *c,
+ struct scanned_ino_node *ino_node)
+{
+ if (!c->encrypted)
+ return false;
+
+ if (ino_node->is_xattr)
+ return false;
+
+ /* Only regular files, directories, and symlinks can be encrypted. */
+ if (S_ISREG(ino_node->mode) || S_ISDIR(ino_node->mode) ||
+ S_ISLNK(ino_node->mode))
+ return true;
+
+ return false;
+}
+
+/**
+ * parse_ino_node - parse inode node and check it's validity.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: the offset in LEB of the raw inode node
+ * @node: raw node
+ * @key: key of node scanned (if it has one)
+ * @ino_node: node used to store raw inode information
+ *
+ * This function checks the raw inode information, and stores inode
+ * information into @ino_node. Returns %true if the inode is valid,
+ * otherwise %false is returned.
+ */
+bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_ino_node *ino_node)
+{
+ bool valid = false;
+ int data_len, node_len;
+ unsigned int flags;
+ unsigned long long sqnum;
+ struct ubifs_ch *ch = (struct ubifs_ch *)node;
+ struct ubifs_ino_node *ino = (struct ubifs_ino_node *)node;
+ ino_t inum = key_inum(c, key);
+
+ if (!inum || inum > INUM_WATERMARK) {
+ dbg_fsck("bad inode node(bad inum %lu) at %d:%d, in %s",
+ inum, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (ch->node_type != key_type(c, key)) {
+ dbg_fsck("bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
+ inum, ch->node_type, key_type(c, key),
+ lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ node_len = le32_to_cpu(ch->len);
+ sqnum = le64_to_cpu(ch->sqnum);
+ key_copy(c, key, &ino_node->key);
+ flags = le32_to_cpu(ino->flags);
+ data_len = le32_to_cpu(ino->data_len);
+ ino_node->is_xattr = !!(flags & UBIFS_XATTR_FL) ? 1 : 0;
+ ino_node->is_encrypted = !!(flags & UBIFS_CRYPT_FL) ? 1 : 0;
+ ino_node->mode = le32_to_cpu(ino->mode);
+ ino_node->nlink = le32_to_cpu(ino->nlink);
+ ino_node->xcnt = le32_to_cpu(ino->xattr_cnt);
+ ino_node->xsz = le32_to_cpu(ino->xattr_size);
+ ino_node->xnms = le32_to_cpu(ino->xattr_names);
+ ino_node->size = le64_to_cpu(ino->size);
+
+ if (inum == UBIFS_ROOT_INO && !S_ISDIR(ino_node->mode)) {
+ dbg_fsck("bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (ino_node->size > c->max_inode_sz) {
+ dbg_fsck("bad inode node %lu(size %llu is too large) at %d:%d, in %s",
+ inum, ino_node->size, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (le16_to_cpu(ino->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
+ dbg_fsck("bad inode node %lu(unknown compression type %d) at %d:%d, in %s",
+ inum, le16_to_cpu(ino->compr_type), lnum, offs,
+ c->dev_name);
+ goto out;
+ }
+
+ if (ino_node->xnms + ino_node->xcnt > XATTR_LIST_MAX) {
+ dbg_fsck("bad inode node %lu(too big xnames %u xcount %u) at %d:%d, in %s",
+ inum, ino_node->xnms, ino_node->xcnt,
+ lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (data_len < 0 || data_len > UBIFS_MAX_INO_DATA) {
+ dbg_fsck("bad inode node %lu(invalid data len %d) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (UBIFS_INO_NODE_SZ + data_len != node_len) {
+ dbg_fsck("bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d, in %s",
+ inum, data_len, node_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (ino_node->is_xattr) {
+ if (!S_ISREG(ino_node->mode)) {
+ dbg_fsck("bad inode node %lu(bad type %u for xattr) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT,
+ lnum, offs, c->dev_name);
+ goto out;
+ }
+ if (data_len != ino_node->size) {
+ dbg_fsck("bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d, in %s",
+ inum, data_len, ino_node->size,
+ lnum, offs, c->dev_name);
+ goto out;
+ }
+ if (ino_node->xcnt || ino_node->xsz || ino_node->xnms) {
+ dbg_fsck("bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d, in %s",
+ inum, ino_node->xcnt, ino_node->xsz,
+ ino_node->xnms, lnum, offs, c->dev_name);
+ goto out;
+ }
+ }
+
+ switch (ino_node->mode & S_IFMT) {
+ case S_IFREG:
+ if (!ino_node->is_xattr && data_len != 0) {
+ dbg_fsck("bad inode node %lu(bad data len %d for reg file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+ break;
+ case S_IFDIR:
+ if (data_len != 0) {
+ dbg_fsck("bad inode node %lu(bad data len %d for dir file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+ break;
+ case S_IFLNK:
+ if (data_len == 0) {
+ /*
+ * For encryption enabled or selinux enabled situation,
+ * uninitialized inode with xattrs could be written
+ * before ubifs_jnl_update(). If the dent node is
+ * written successfully but the initialized inode is
+ * not written, ubifs_iget() will get bad symlink inode
+ * with 'ui->data_len = 0'. Similar phenomenon can also
+ * occur for block/char dev creation.
+ * Just drop the inode node when above class of
+ * exceptions are found.
+ */
+ dbg_fsck("bad symlink inode node %lu(bad data len %d) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+ break;
+ case S_IFBLK:
+ fallthrough;
+ case S_IFCHR:
+ {
+ union ubifs_dev_desc *dev = (union ubifs_dev_desc *)ino->data;
+ int sz_new = sizeof(dev->new), sz_huge = sizeof(dev->huge);
+
+ if (data_len != sz_new && data_len != sz_huge) {
+ dbg_fsck("bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d, in %s",
+ inum, data_len, sz_new, sz_huge, lnum,
+ offs, c->dev_name);
+ goto out;
+ }
+ break;
+ }
+ case S_IFSOCK:
+ fallthrough;
+ case S_IFIFO:
+ if (data_len != 0) {
+ dbg_fsck("bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ goto out;
+ }
+ break;
+ default:
+ /* invalid file type. */
+ dbg_fsck("bad inode node %lu(unknown type %u) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (ino_node->is_encrypted && !inode_can_be_encrypted(c, ino_node)) {
+ dbg_fsck("bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
+ c->encrypted, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ valid = true;
+ parse_node_header(lnum, offs, node_len, sqnum, &ino_node->header);
+
+out:
+ return valid;
+}
+
+/**
+ * parse_dent_node - parse dentry node and check it's validity.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: the offset in LEB of the raw inode node
+ * @node: raw node
+ * @key: key of node scanned (if it has one)
+ * @dent_node: node used to store raw dentry information
+ *
+ * This function checks the raw dentry/(xattr entry) information, and
+ * stores dentry/(xattr entry) information into @dent_node. Returns
+ * %true if the entry is valid, otherwise %false is returned.
+ */
+bool parse_dent_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_dent_node *dent_node)
+{
+ bool valid = false;
+ int node_len, nlen;
+ unsigned long long sqnum;
+ struct ubifs_ch *ch = (struct ubifs_ch *)node;
+ struct ubifs_dent_node *dent = (struct ubifs_dent_node *)node;
+ int key_type = key_type_flash(c, dent->key);
+ ino_t inum;
+
+ nlen = le16_to_cpu(dent->nlen);
+ node_len = le32_to_cpu(ch->len);
+ sqnum = le64_to_cpu(ch->sqnum);
+ inum = le64_to_cpu(dent->inum);
+
+ if (node_len != nlen + UBIFS_DENT_NODE_SZ + 1 ||
+ dent->type >= UBIFS_ITYPES_CNT ||
+ nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
+ (key_type == UBIFS_XENT_KEY &&
+ strnlen((const char *)dent->name, nlen) != nlen) ||
+ inum > INUM_WATERMARK || key_type != ch->node_type) {
+ dbg_fsck("bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d, in %s",
+ ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
+ node_len, nlen, dent->type, inum, key_type,
+ ch->node_type, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ key_copy(c, key, &dent_node->key);
+ dent_node->can_be_found = false;
+ dent_node->type = dent->type;
+ dent_node->nlen = nlen;
+ memcpy(dent_node->name, dent->name, nlen);
+ dent_node->name[nlen] = '\0';
+ dent_node->inum = inum;
+
+ valid = true;
+ parse_node_header(lnum, offs, node_len, sqnum, &dent_node->header);
+
+out:
+ return valid;
+}
+
+/**
+ * parse_data_node - parse data node and check it's validity.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: the offset in LEB of the raw data node
+ * @node: raw node
+ * @key: key of node scanned (if it has one)
+ * @ino_node: node used to store raw data information
+ *
+ * This function checks the raw data node information, and stores
+ * data node information into @data_node. Returns %true if the data
+ * node is valid, otherwise %false is returned.
+ */
+bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_data_node *data_node)
+{
+ bool valid = false;
+ int node_len;
+ unsigned long long sqnum;
+ struct ubifs_ch *ch = (struct ubifs_ch *)node;
+ struct ubifs_data_node *dn = (struct ubifs_data_node *)node;
+ ino_t inum = key_inum(c, key);
+
+ if (ch->node_type != key_type(c, key)) {
+ dbg_fsck("bad data node(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
+ ch->node_type, key_type(c, key),
+ lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (!inum || inum > INUM_WATERMARK) {
+ dbg_fsck("bad data node(bad inum %lu) at %d:%d, in %s",
+ inum, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ node_len = le32_to_cpu(ch->len);
+ sqnum = le64_to_cpu(ch->sqnum);
+ key_copy(c, key, &data_node->key);
+ data_node->size = le32_to_cpu(dn->size);
+
+ if (!data_node->size || data_node->size > UBIFS_BLOCK_SIZE) {
+ dbg_fsck("bad data node(invalid size %u) at %d:%d, in %s",
+ data_node->size, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ if (le16_to_cpu(dn->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
+ dbg_fsck("bad data node(invalid compression type %d) at %d:%d, in %s",
+ le16_to_cpu(dn->compr_type), lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ valid = true;
+ parse_node_header(lnum, offs, node_len, sqnum, &data_node->header);
+
+out:
+ return valid;
+}
+
+/**
+ * parse_trun_node - parse truncation node and check it's validity.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: the offset in LEB of the raw truncation node
+ * @node: raw node
+ * @key: key of node scanned (if it has one)
+ * @trun_node: node used to store raw truncation information
+ *
+ * This function checks the raw truncation information, and stores
+ * truncation information into @trun_node. Returns %true if the
+ * truncation is valid, otherwise %false is returned.
+ */
+bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_trun_node *trun_node)
+{
+ bool valid = false;
+ int node_len;
+ unsigned long long sqnum;
+ struct ubifs_ch *ch = (struct ubifs_ch *)node;
+ struct ubifs_trun_node *trun = (struct ubifs_trun_node *)node;
+ loff_t old_size = le64_to_cpu(trun->old_size);
+ loff_t new_size = le64_to_cpu(trun->new_size);
+ ino_t inum = le32_to_cpu(trun->inum);
+
+ if (!inum || inum > INUM_WATERMARK) {
+ dbg_fsck("bad truncation node(bad inum %lu) at %d:%d, in %s",
+ inum, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ node_len = le32_to_cpu(ch->len);
+ sqnum = le64_to_cpu(ch->sqnum);
+ trun_node->new_size = new_size;
+
+ if (old_size < 0 || old_size > c->max_inode_sz ||
+ new_size < 0 || new_size > c->max_inode_sz ||
+ old_size <= new_size) {
+ dbg_fsck("bad truncation node(new size %ld old size %ld inum %lu) at %d:%d, in %s",
+ new_size, old_size, inum, lnum, offs, c->dev_name);
+ goto out;
+ }
+
+ trun_key_init(c, key, inum);
+ valid = true;
+ parse_node_header(lnum, offs, node_len, sqnum, &trun_node->header);
+
+out:
+ return valid;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index eb365b1a..3511d90e 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -40,6 +40,102 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
enum { SB_CORRUPTED = 0 };
/**
+ * scanned_node - common header node.
+ * @exist: whether the node is found by scanning
+ * @lnum: LEB number of the scanned node
+ * @offs: scanned node's offset within @lnum
+ * @len: length of scanned node
+ * @sqnum: sequence number
+ */
+struct scanned_node {
+ bool exist;
+ int lnum;
+ int offs;
+ int len;
+ unsigned long long sqnum;
+};
+
+/**
+ * scanned_ino_node - scanned inode node.
+ * @header: common header of scanned node
+ * @key: the key of inode node
+ * @is_xattr: %1 for xattr inode, otherwise %0
+ * @is_encrypted: %1 for encrypted inode, otherwise %0
+ * @mode: file mode
+ * @nlink: number of hard links
+ * @xcnt: count of extended attributes this inode has
+ * @xsz: summarized size of all extended attributes in bytes
+ * @xnms: sum of lengths of all extended attribute names
+ * @size: inode size in bytes
+ * @rb: link in the tree of valid inode nodes or deleted inode nodes
+ */
+struct scanned_ino_node {
+ struct scanned_node header;
+ union ubifs_key key;
+ unsigned int is_xattr:1;
+ unsigned int is_encrypted:1;
+ unsigned int mode;
+ unsigned int nlink;
+ unsigned int xcnt;
+ unsigned int xsz;
+ unsigned int xnms;
+ unsigned long long size;
+ struct rb_node rb;
+};
+
+/**
+ * scanned_dent_node - scanned dentry node.
+ * @header: common header of scanned node
+ * @key: the key of dentry node
+ * @can_be_found: whether this dentry can be found from '/'
+ * @type: file type, reg/dir/symlink/block/char/fifo/sock
+ * @nlen: name length
+ * @name: dentry name
+ * @inum: target inode number
+ * @rb: link in the trees of:
+ * 1) valid dentry nodes or deleted dentry node
+ * 2) all scanned dentry nodes from same file
+ * @list: link in the list dentries for looking up/deleting
+ */
+struct scanned_dent_node {
+ struct scanned_node header;
+ union ubifs_key key;
+ bool can_be_found;
+ unsigned int type;
+ unsigned int nlen;
+ char name[UBIFS_MAX_NLEN + 1];
+ ino_t inum;
+ struct rb_node rb;
+ struct list_head list;
+};
+
+/**
+ * scanned_data_node - scanned data node.
+ * @header: common header of scanned node
+ * @key: the key of data node
+ * @size: uncompressed data size in bytes
+ * @rb: link in the tree of all scanned data nodes from same file
+ * @list: link in the list for deleting
+ */
+struct scanned_data_node {
+ struct scanned_node header;
+ union ubifs_key key;
+ unsigned int size;
+ struct rb_node rb;
+ struct list_head list;
+};
+
+/**
+ * scanned_trun_node - scanned truncation node.
+ * @header: common header of scanned node
+ * @new_size: size after truncation
+ */
+struct scanned_trun_node {
+ struct scanned_node header;
+ unsigned long long new_size;
+};
+
+/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
* @failure_reason: reasons for failed operations
@@ -103,4 +199,14 @@ bool fix_problem(const struct ubifs_info *c, int problem_type);
int ubifs_load_filesystem(struct ubifs_info *c);
void ubifs_destroy_filesystem(struct ubifs_info *c);
+/* extract_files.c */
+bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_ino_node *ino_node);
+bool parse_dent_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_dent_node *dent_node);
+bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_data_node *data_node);
+bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
+ union ubifs_key *key, struct scanned_trun_node *trun_node);
+
#endif
diff --git a/ubifs-utils/libubifs/debug.h b/ubifs-utils/libubifs/debug.h
index 3a553627..8da79f80 100644
--- a/ubifs-utils/libubifs/debug.h
+++ b/ubifs-utils/libubifs/debug.h
@@ -78,6 +78,8 @@ void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__)
/* Additional recovery messages */
#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
+/* Additional fsck messages */
+#define dbg_fsck(fmt, ...) ubifs_dbg_msg("fsck", fmt, ##__VA_ARGS__)
static inline int dbg_is_chk_index(__unused const struct ubifs_info *c)
{ return 0; }
--
2.13.6
Read failure caused by scanning corrupted data or invalid data members
should be identified, beacuse fsck can handle it. Updating lp failure
caused by bad space statistics should be identified too, beacuse fsck
can handle it.
Add eight callback functions to implement it for fsck:
1. set_failure_reason_callback: Record failure reasons when reading or
parsing node failed, there are four reasons:
a. FR_DATA_CORRUPTED: scanning corrupted data or invalid nodes found
b. FR_TNC_CORRUPTED: invalid index nodes
c. FR_LPT_CORRUPTED: invalid pnode/nnode
d. FR_LPT_INCORRECT: invalid space statistics or invalid LEB properties
2. get_failure_reason_callback: get failure reasons
3. clear_failure_reason_callback: Clear the error which is caused by
above reasons.
4. test_and_clear_failure_reason_callback: Check and clear the error
which is caused by above reasons, if so, fsck will handle it
according to specific situation.
For example, fsck will drop data node rather than fails to return
when reading failure is caused by DATA_CORRUPTED.
For another example, journal replaying will continue rather than
fails to return if updating lpt failure is caused by LPT_CORRUPTED.
5. set_lpt_invalid_callback: Set the invalid lpt status
6. test_lpt_valid_callback: Check whether the lpt is corrupted/incorrect,
it should be invoked before updating lp, if lpt status is invalid,
returns false (which means that caller should skip updating lp, because
updating lp could trigger assertion failed in ubifs_change_lp).
7. can_ignore_failure_callback: Check whether the failure can be
ignored, some inconsistent errors won't affect the fsck process,
for example wrong space statistics can be fixed after traversing
TNC, so failures caused by incorrect space statistics can be ignored.
8. handle_failure_callback: Check whether the failure can be handled,
some inconsistent errors could be fixed by fsck, we have fix_problem
to do that, but UBIFS needs a callback function to invoke it in common
libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 174 ++++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 5 ++
ubifs-utils/libubifs/io.c | 4 +
ubifs-utils/libubifs/lpt.c | 66 ++++++++++----
ubifs-utils/libubifs/lpt_commit.c | 5 +-
ubifs-utils/libubifs/master.c | 18 +++-
ubifs-utils/libubifs/orphan.c | 4 +-
ubifs-utils/libubifs/recovery.c | 4 +
ubifs-utils/libubifs/replay.c | 1 +
ubifs-utils/libubifs/sb.c | 2 +
ubifs-utils/libubifs/scan.c | 1 +
ubifs-utils/libubifs/tnc_misc.c | 4 +
ubifs-utils/libubifs/ubifs.h | 98 ++++++++++++++++++++
13 files changed, 366 insertions(+), 20 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index b1286150..36a8e061 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -168,6 +168,172 @@ static void fsck_assert_failed(__unused const struct ubifs_info *c)
exit(exit_code);
}
+static void fsck_set_failure_reason(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (FSCK(c)->mode == REBUILD_MODE)
+ return;
+
+ FSCK(c)->failure_reason = reason;
+ if (reason & FR_LPT_CORRUPTED) {
+ log_out(c, "Found corrupted pnode/nnode, set lpt corrupted");
+ FSCK(c)->lpt_status |= FR_LPT_CORRUPTED;
+ }
+ if (reason & FR_LPT_INCORRECT) {
+ log_out(c, "Bad space statistics, set lpt incorrect");
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ }
+}
+
+static unsigned int fsck_get_failure_reason(const struct ubifs_info *c)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ return FSCK(c)->failure_reason;
+}
+
+static void fsck_clear_failure_reason(const struct ubifs_info *c)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ FSCK(c)->failure_reason = 0;
+}
+
+static bool fsck_test_and_clear_failure_reason(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ bool res = (FSCK(c)->failure_reason & reason) != 0;
+
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+ ubifs_assert(c, !(FSCK(c)->failure_reason & (~reason)));
+
+ FSCK(c)->failure_reason = 0;
+
+ return res;
+}
+
+static void fsck_set_lpt_invalid(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (reason & FR_LPT_CORRUPTED) {
+ log_out(c, "Found corrupted pnode/nnode, set lpt corrupted");
+ FSCK(c)->lpt_status |= FR_LPT_CORRUPTED;
+ }
+ if (reason & FR_LPT_INCORRECT) {
+ log_out(c, "Bad space statistics, set lpt incorrect");
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ }
+}
+
+static bool fsck_test_lpt_valid(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (c->cmt_state != COMMIT_RESTING)
+ /* Don't skip updating lpt when do commit. */
+ goto out;
+
+ if (FSCK(c)->lpt_status)
+ return false;
+
+ if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs) {
+ log_out(c, "Bad empty_lebs %d(main_lebs %d), set lpt incorrect",
+ c->lst.empty_lebs, c->main_lebs);
+ goto out_invalid;
+ }
+ if (c->freeable_cnt < 0 || c->freeable_cnt > c->main_lebs) {
+ log_out(c, "Bad freeable_cnt %d(main_lebs %d), set lpt incorrect",
+ c->freeable_cnt, c->main_lebs);
+ goto out_invalid;
+ }
+ if (c->lst.taken_empty_lebs < 0 ||
+ c->lst.taken_empty_lebs > c->lst.empty_lebs) {
+ log_out(c, "Bad taken_empty_lebs %d(empty_lebs %d), set lpt incorrect",
+ c->lst.taken_empty_lebs, c->lst.empty_lebs);
+ goto out_invalid;
+ }
+ if (c->lst.total_free & 7) {
+ log_out(c, "total_free(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_free);
+ goto out_invalid;
+ }
+ if (c->lst.total_dirty & 7) {
+ log_out(c, "total_dirty(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dirty);
+ goto out_invalid;
+ }
+ if (c->lst.total_dead & 7) {
+ log_out(c, "total_dead(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dead);
+ goto out_invalid;
+ }
+ if (c->lst.total_dark & 7) {
+ log_out(c, "total_dark(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dark);
+ goto out_invalid;
+ }
+ if (c->lst.total_used & 7) {
+ log_out(c, "total_used(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_used);
+ goto out_invalid;
+ }
+ if (old_free != LPROPS_NC && (old_free & 7)) {
+ log_out(c, "LEB %d old_free(%d) is not 8 bytes aligned, set lpt incorrect",
+ lnum, old_free);
+ goto out_invalid;
+ }
+ if (old_dirty != LPROPS_NC && (old_dirty & 7)) {
+ log_out(c, "LEB %d old_dirty(%d) is not 8 bytes aligned, set lpt incorrect",
+ lnum, old_dirty);
+ goto out_invalid;
+ }
+ if (free != LPROPS_NC && (free < 0 || free > c->leb_size)) {
+ log_out(c, "LEB %d bad free %d(leb_size %d), set lpt incorrect",
+ lnum, free, c->leb_size);
+ goto out_invalid;
+ }
+ if (dirty != LPROPS_NC && dirty < 0) {
+ /* Dirty may be more than c->leb_size before set_bud_lprops. */
+ log_out(c, "LEB %d bad dirty %d(leb_size %d), set lpt incorrect",
+ lnum, dirty, c->leb_size);
+ goto out_invalid;
+ }
+
+out:
+ return true;
+
+out_invalid:
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ return false;
+}
+
+static bool fsck_can_ignore_failure(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (c->cmt_state != COMMIT_RESTING)
+ /* Don't ignore failure when do commit. */
+ return false;
+ if (reason & (FR_LPT_CORRUPTED | FR_LPT_INCORRECT))
+ return true;
+
+ return false;
+}
+
+static const unsigned int reason_mapping_table[] = {};
+
+static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ return fix_problem(c, reason_mapping_table[reason]);
+}
+
static void signal_cancel(int sig)
{
ubifs_warn(c, "killed by signo %d", sig);
@@ -199,6 +365,14 @@ static int init_fsck_info(struct ubifs_info *c, int argc, char *argv[])
c->private = fsck;
FSCK(c)->mode = mode;
c->assert_failed_cb = fsck_assert_failed;
+ c->set_failure_reason_cb = fsck_set_failure_reason;
+ c->get_failure_reason_cb = fsck_get_failure_reason;
+ c->clear_failure_reason_cb = fsck_clear_failure_reason;
+ c->test_and_clear_failure_reason_cb = fsck_test_and_clear_failure_reason;
+ c->set_lpt_invalid_cb = fsck_set_lpt_invalid;
+ c->test_lpt_valid_cb = fsck_test_lpt_valid;
+ c->can_ignore_failure_cb = fsck_can_ignore_failure;
+ c->handle_failure_cb = fsck_handle_failure;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = signal_cancel;
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index b9783c15..53dc5ff0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -39,9 +39,14 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
+ * @failure_reason: reasons for failed operations
+ * @lpt_status: the status of lpt, could be: %0(OK), %FR_LPT_CORRUPTED or
+ * %FR_LPT_INCORRECT
*/
struct ubifs_fsck_info {
int mode;
+ unsigned int failure_reason;
+ unsigned int lpt_status;
};
#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
diff --git a/ubifs-utils/libubifs/io.c b/ubifs-utils/libubifs/io.c
index 6f170172..b6fb331b 100644
--- a/ubifs-utils/libubifs/io.c
+++ b/ubifs-utils/libubifs/io.c
@@ -964,6 +964,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
if (err) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "expected node type %d", type);
return err;
}
@@ -977,6 +978,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
ubifs_dump_node(c, buf, len);
dump_stack();
@@ -1020,6 +1022,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
if (err) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "expected node type %d", type);
return err;
}
@@ -1033,6 +1036,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
offs, ubi_is_mapped(c->dev_fd, lnum));
ubifs_dump_node(c, buf, len);
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index 92b3fec8..c0df7c7d 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -983,6 +983,7 @@ static int check_lpt_crc(const struct ubifs_info *c, void *buf, int len)
calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
len - UBIFS_LPT_CRC_BYTES);
if (crc != calc_crc) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx",
crc, calc_crc);
dump_stack();
@@ -1007,6 +1008,7 @@ static int check_lpt_type(const struct ubifs_info *c, uint8_t **addr,
node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS);
if (node_type != type) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid type (%d) in LPT node type %d",
node_type, type);
dump_stack();
@@ -1106,8 +1108,10 @@ static int unpack_ltab(const struct ubifs_info *c, void *buf)
int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits);
if (free < 0 || free > c->leb_size || dirty < 0 ||
- dirty > c->leb_size || free + dirty > c->leb_size)
+ dirty > c->leb_size || free + dirty > c->leb_size) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return -EINVAL;
+ }
c->ltab[i].free = free;
c->ltab[i].dirty = dirty;
@@ -1136,8 +1140,10 @@ static int unpack_lsave(const struct ubifs_info *c, void *buf)
for (i = 0; i < c->lsave_cnt; i++) {
int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits);
- if (lnum < c->main_first || lnum >= c->leb_cnt)
+ if (lnum < c->main_first || lnum >= c->leb_cnt) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return -EINVAL;
+ }
c->lsave[i] = lnum;
}
err = check_lpt_crc(c, buf, c->lsave_sz);
@@ -1162,11 +1168,11 @@ static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
int num = calc_nnode_num_from_parent(c, parent, iip);
if (nnode->num != num)
- return -EINVAL;
+ goto out_invalid;
}
lvl = parent ? parent->level - 1 : c->lpt_hght;
if (lvl < 1)
- return -EINVAL;
+ goto out_invalid;
if (lvl == 1)
max_offs = c->leb_size - c->pnode_sz;
else
@@ -1177,15 +1183,19 @@ static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
if (lnum == 0) {
if (offs != 0)
- return -EINVAL;
+ goto out_invalid;
continue;
}
if (lnum < c->lpt_first || lnum > c->lpt_last)
- return -EINVAL;
+ goto out_invalid;
if (offs < 0 || offs > max_offs)
- return -EINVAL;
+ goto out_invalid;
}
return 0;
+
+out_invalid:
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
}
/**
@@ -1206,7 +1216,7 @@ static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
int num = calc_pnode_num_from_parent(c, parent, iip);
if (pnode->num != num)
- return -EINVAL;
+ goto out_invalid;
}
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int free = pnode->lprops[i].free;
@@ -1214,13 +1224,17 @@ static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
if (free < 0 || free > c->leb_size || free % c->min_io_size ||
(free & 7))
- return -EINVAL;
+ goto out_invalid;
if (dirty < 0 || dirty > c->leb_size || (dirty & 7))
- return -EINVAL;
+ goto out_invalid;
if (dirty + free > c->leb_size)
- return -EINVAL;
+ goto out_invalid;
}
return 0;
+
+out_invalid:
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
}
/**
@@ -1283,8 +1297,11 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
goto out;
@@ -1352,8 +1369,11 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
}
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_pnode(c, buf, pnode);
if (err)
goto out;
@@ -1394,8 +1414,11 @@ static int read_ltab(struct ubifs_info *c)
if (!buf)
return -ENOMEM;
err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_ltab(c, buf);
out:
vfree(buf);
@@ -1418,8 +1441,11 @@ static int read_lsave(struct ubifs_info *c)
return -ENOMEM;
err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
c->lsave_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_lsave(c, buf);
if (err)
goto out;
@@ -2031,8 +2057,11 @@ static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c,
} else {
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->nnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return ERR_PTR(err);
+ }
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
return ERR_PTR(err);
@@ -2100,8 +2129,11 @@ static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c,
ubifs_assert(c, branch->offs >= 0 && branch->offs < c->leb_size);
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->pnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return ERR_PTR(err);
+ }
err = unpack_pnode(c, buf, pnode);
if (err)
return ERR_PTR(err);
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index b00f75f0..43eb7a6a 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -1607,8 +1607,11 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
dbg_lp("LEB %d", lnum);
err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
while (1) {
if (!is_a_node(c, p, len)) {
diff --git a/ubifs-utils/libubifs/master.c b/ubifs-utils/libubifs/master.c
index cce1a415..61ff4cec 100644
--- a/ubifs-utils/libubifs/master.c
+++ b/ubifs-utils/libubifs/master.c
@@ -146,10 +146,12 @@ static int scan_for_master(struct ubifs_info *c)
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_scan_destroy(sleb);
return -EUCLEAN;
out_dump:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "unexpected node type %d master LEB %d:%d",
snod->type, lnum, snod->offs);
ubifs_scan_destroy(sleb);
@@ -165,6 +167,7 @@ out_dump:
*/
static int validate_master(const struct ubifs_info *c)
{
+ unsigned int reason = FR_DATA_CORRUPTED;
long long main_sz;
int err;
@@ -254,39 +257,46 @@ static int validate_master(const struct ubifs_info *c)
}
if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
+ reason = FR_LPT_INCORRECT;
err = 15;
goto out;
}
if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
+ reason = FR_LPT_INCORRECT;
err = 16;
goto out;
}
if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
c->lst.total_free & 7) {
+ reason = FR_LPT_INCORRECT;
err = 17;
goto out;
}
if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
+ reason = FR_LPT_INCORRECT;
err = 18;
goto out;
}
if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
+ reason = FR_LPT_INCORRECT;
err = 19;
goto out;
}
if (c->lst.total_free + c->lst.total_dirty +
c->lst.total_used > main_sz) {
+ reason = FR_LPT_INCORRECT;
err = 20;
goto out;
}
if (c->lst.total_dead + c->lst.total_dark +
c->lst.total_used + c->bi.old_idx_sz > main_sz) {
+ reason = FR_LPT_INCORRECT;
err = 21;
goto out;
}
@@ -294,6 +304,7 @@ static int validate_master(const struct ubifs_info *c)
if (c->lst.total_dead < 0 ||
c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dead & 7) {
+ reason = FR_LPT_INCORRECT;
err = 22;
goto out;
}
@@ -301,6 +312,7 @@ static int validate_master(const struct ubifs_info *c)
if (c->lst.total_dark < 0 ||
c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dark & 7) {
+ reason = FR_LPT_INCORRECT;
err = 23;
goto out;
}
@@ -308,6 +320,7 @@ static int validate_master(const struct ubifs_info *c)
return 0;
out:
+ set_failure_reason_callback(c, reason);
ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
return -EINVAL;
@@ -331,8 +344,10 @@ int ubifs_read_master(struct ubifs_info *c)
err = scan_for_master(c);
if (err) {
- if (err == -EUCLEAN)
+ if (err == -EUCLEAN) {
+ clear_failure_reason_callback(c);
err = ubifs_recover_master_node(c);
+ }
if (err)
/*
* Note, we do not free 'c->mst_node' here because the
@@ -386,6 +401,7 @@ int ubifs_read_master(struct ubifs_info *c)
if (c->leb_cnt < old_leb_cnt ||
c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad leb_cnt on master node");
ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
return -EINVAL;
diff --git a/ubifs-utils/libubifs/orphan.c b/ubifs-utils/libubifs/orphan.c
index 2f318748..26668cbd 100644
--- a/ubifs-utils/libubifs/orphan.c
+++ b/ubifs-utils/libubifs/orphan.c
@@ -547,9 +547,11 @@ static int kill_orphans(struct ubifs_info *c)
dbg_rcvry("LEB %d", lnum);
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
if (IS_ERR(sleb)) {
- if (PTR_ERR(sleb) == -EUCLEAN)
+ if (PTR_ERR(sleb) == -EUCLEAN) {
+ clear_failure_reason_callback(c);
sleb = ubifs_recover_leb(c, lnum, 0,
c->sbuf, -1);
+ }
if (IS_ERR(sleb)) {
err = PTR_ERR(sleb);
break;
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
index 910414cb..9115b17a 100644
--- a/ubifs-utils/libubifs/recovery.c
+++ b/ubifs-utils/libubifs/recovery.c
@@ -182,6 +182,7 @@ static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf,
return 0;
out_err:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
err = -EINVAL;
out_free:
vfree(sbuf);
@@ -355,6 +356,7 @@ int ubifs_recover_master_node(struct ubifs_info *c)
return 0;
out_err:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
err = -EINVAL;
out_free:
ubifs_err(c, "failed to recover master node");
@@ -762,6 +764,7 @@ corrupted_rescan:
ubifs_err(c, "corruption %d", ret);
ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
corrupted:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_scanned_corruption(c, lnum, offs, buf);
err = -EUCLEAN;
error:
@@ -817,6 +820,7 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs,
out_err:
err = -EINVAL;
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
out_free:
ubifs_err(c, "failed to get CS sqnum");
kfree(cs_node);
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index ff9efaac..0ed12e3a 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -928,6 +928,7 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
if (IS_ERR(sleb)) {
if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery)
return PTR_ERR(sleb);
+ clear_failure_reason_callback(c);
/*
* Note, the below function will recover this log LEB only if
* it is the last, because unclean reboots can possibly corrupt
diff --git a/ubifs-utils/libubifs/sb.c b/ubifs-utils/libubifs/sb.c
index 312661d6..2147280e 100644
--- a/ubifs-utils/libubifs/sb.c
+++ b/ubifs-utils/libubifs/sb.c
@@ -360,6 +360,8 @@ int ubifs_read_superblock(struct ubifs_info *c)
err = validate_sb(c, sup);
out:
+ if (err)
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
return err;
}
diff --git a/ubifs-utils/libubifs/scan.c b/ubifs-utils/libubifs/scan.c
index 74509fd0..e9581a62 100644
--- a/ubifs-utils/libubifs/scan.c
+++ b/ubifs-utils/libubifs/scan.c
@@ -344,6 +344,7 @@ corrupted:
}
err = -EUCLEAN;
ubifs_scan_destroy(sleb);
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
return ERR_PTR(err);
error:
diff --git a/ubifs-utils/libubifs/tnc_misc.c b/ubifs-utils/libubifs/tnc_misc.c
index 8c38f153..0ffb4342 100644
--- a/ubifs-utils/libubifs/tnc_misc.c
+++ b/ubifs-utils/libubifs/tnc_misc.c
@@ -222,6 +222,8 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
if (err < 0) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED))
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
kfree(idx);
return err;
}
@@ -335,6 +337,7 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
return 0;
out_dump:
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
ubifs_err(c, "bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
ubifs_dump_node(c, idx, c->max_idx_node_sz);
kfree(idx);
@@ -430,6 +433,7 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
/* Make sure the key of the read node is correct */
key_read(c, node + UBIFS_KEY_OFFSET, &key1);
if (!keys_eq(c, key, &key1)) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad key in node at LEB %d:%d",
zbr->lnum, zbr->offs);
dbg_tnck(key, "looked for key ");
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index babaae8f..21b0ce0a 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1029,6 +1029,20 @@ struct ubifs_budg_info {
*
* @private: private information related to specific situation, eg. fsck.
* @assert_failed_cb: callback function to handle assertion failure
+ * @set_failure_reason_cb: record reasons while certain failure happens
+ * @get_failure_reason_cb: get failure reasons
+ * @clear_failure_reason_cb: callback function to clear the error which is
+ * caused by reading corrupted data or invalid lpt
+ * @test_and_clear_failure_reason_cb: callback function to check and clear the
+ * error which is caused by reading corrupted
+ * data or invalid lpt
+ * @set_lpt_invalid_cb: callback function to set the invalid lpt status
+ * @test_lpt_valid_cb: callback function to check whether lpt is corrupted or
+ * incorrect, should be called before updating lpt
+ * @can_ignore_failure_cb: callback function to decide whether the failure
+ * can be ignored
+ * @handle_failure_cb: callback function to decide whether the failure can be
+ * handled
*/
struct ubifs_info {
struct ubifs_sb_node *sup_node;
@@ -1254,6 +1268,21 @@ struct ubifs_info {
void *private;
void (*assert_failed_cb)(const struct ubifs_info *c);
+ void (*set_failure_reason_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ unsigned int (*get_failure_reason_cb)(const struct ubifs_info *c);
+ void (*clear_failure_reason_cb)(const struct ubifs_info *c);
+ bool (*test_and_clear_failure_reason_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ void (*set_lpt_invalid_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ bool (*test_lpt_valid_cb)(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty);
+ bool (*can_ignore_failure_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ bool (*handle_failure_cb)(const struct ubifs_info *c,
+ unsigned int reason);
};
extern atomic_long_t ubifs_clean_zn_cnt;
@@ -1502,6 +1531,75 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
void *buf);
+/* Failure reasons which are checked by fsck. */
+enum {
+ FR_DATA_CORRUPTED = 1, /* Data is corrupted(master/log/orphan/main) */
+ FR_TNC_CORRUPTED = 2, /* TNC is corrupted */
+ FR_LPT_CORRUPTED = 4, /* LPT is corrupted */
+ FR_LPT_INCORRECT = 8 /* Space statistics are wrong */
+};
+/* Callback functions for failure(which can be handled by fsck) happens. */
+static inline void set_failure_reason_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->set_failure_reason_cb)
+ c->set_failure_reason_cb(c, reason);
+}
+static inline unsigned int get_failure_reason_callback(
+ const struct ubifs_info *c)
+{
+ if (c->get_failure_reason_cb)
+ return c->get_failure_reason_cb(c);
+
+ return 0;
+}
+static inline void clear_failure_reason_callback(const struct ubifs_info *c)
+{
+ if (c->clear_failure_reason_cb)
+ c->clear_failure_reason_cb(c);
+}
+static inline bool test_and_clear_failure_reason_callback(
+ const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->test_and_clear_failure_reason_cb)
+ return c->test_and_clear_failure_reason_cb(c, reason);
+
+ return false;
+}
+static inline void set_lpt_invalid_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->set_lpt_invalid_cb)
+ c->set_lpt_invalid_cb(c, reason);
+}
+static inline bool test_lpt_valid_callback(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty)
+{
+ if (c->test_lpt_valid_cb)
+ return c->test_lpt_valid_cb(c, lnum,
+ old_free, old_dirty, free, dirty);
+
+ return false;
+}
+static inline bool can_ignore_failure_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->can_ignore_failure_cb)
+ return c->can_ignore_failure_cb(c, reason);
+
+ return false;
+}
+static inline bool handle_failure_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->handle_failure_cb)
+ return c->handle_failure_cb(c, reason);
+
+ return false;
+}
+
/* log.c */
void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud);
void ubifs_create_buds_lists(struct ubifs_info *c);
--
2.13.6
Adapt orphan.c in libubifs, compared with linux kernel implementations:
1. Keep the commit related implementations, because do_commit depends
on these functions which will be invoked in fsck.
2. Keep the orphan replaying implementations, because fsck needs them.
3. Other implementations are removed which won't be used in mkfs/fsck.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/orphan.c | 357 +-----------------------------------------
1 file changed, 8 insertions(+), 349 deletions(-)
diff --git a/ubifs-utils/libubifs/orphan.c b/ubifs-utils/libubifs/orphan.c
index fb957d96..2f318748 100644
--- a/ubifs-utils/libubifs/orphan.c
+++ b/ubifs-utils/libubifs/orphan.c
@@ -7,7 +7,14 @@
* Author: Adrian Hunter
*/
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
/*
* An orphan is an inode number whose inode node has been committed to the index
@@ -43,137 +50,6 @@
static int dbg_check_orphans(struct ubifs_info *c);
/**
- * ubifs_add_orphan - add an orphan.
- * @c: UBIFS file-system description object
- * @inum: orphan inode number
- *
- * Add an orphan. This function is called when an inodes link count drops to
- * zero.
- */
-int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
-{
- struct ubifs_orphan *orphan, *o;
- struct rb_node **p, *parent = NULL;
-
- orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
- if (!orphan)
- return -ENOMEM;
- orphan->inum = inum;
- orphan->new = 1;
-
- spin_lock(&c->orphan_lock);
- if (c->tot_orphans >= c->max_orphans) {
- spin_unlock(&c->orphan_lock);
- kfree(orphan);
- return -ENFILE;
- }
- p = &c->orph_tree.rb_node;
- while (*p) {
- parent = *p;
- o = rb_entry(parent, struct ubifs_orphan, rb);
- if (inum < o->inum)
- p = &(*p)->rb_left;
- else if (inum > o->inum)
- p = &(*p)->rb_right;
- else {
- ubifs_err(c, "orphaned twice");
- spin_unlock(&c->orphan_lock);
- kfree(orphan);
- return -EINVAL;
- }
- }
- c->tot_orphans += 1;
- c->new_orphans += 1;
- rb_link_node(&orphan->rb, parent, p);
- rb_insert_color(&orphan->rb, &c->orph_tree);
- list_add_tail(&orphan->list, &c->orph_list);
- list_add_tail(&orphan->new_list, &c->orph_new);
-
- spin_unlock(&c->orphan_lock);
- dbg_gen("ino %lu", (unsigned long)inum);
- return 0;
-}
-
-static struct ubifs_orphan *lookup_orphan(struct ubifs_info *c, ino_t inum)
-{
- struct ubifs_orphan *o;
- struct rb_node *p;
-
- p = c->orph_tree.rb_node;
- while (p) {
- o = rb_entry(p, struct ubifs_orphan, rb);
- if (inum < o->inum)
- p = p->rb_left;
- else if (inum > o->inum)
- p = p->rb_right;
- else {
- return o;
- }
- }
- return NULL;
-}
-
-static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o)
-{
- rb_erase(&o->rb, &c->orph_tree);
- list_del(&o->list);
- c->tot_orphans -= 1;
-
- if (o->new) {
- list_del(&o->new_list);
- c->new_orphans -= 1;
- }
-
- kfree(o);
-}
-
-static void orphan_delete(struct ubifs_info *c, struct ubifs_orphan *orph)
-{
- if (orph->del) {
- dbg_gen("deleted twice ino %lu", (unsigned long)orph->inum);
- return;
- }
-
- if (orph->cmt) {
- orph->del = 1;
- rb_erase(&orph->rb, &c->orph_tree);
- orph->dnext = c->orph_dnext;
- c->orph_dnext = orph;
- dbg_gen("delete later ino %lu", (unsigned long)orph->inum);
- return;
- }
-
- __orphan_drop(c, orph);
-}
-
-/**
- * ubifs_delete_orphan - delete an orphan.
- * @c: UBIFS file-system description object
- * @inum: orphan inode number
- *
- * Delete an orphan. This function is called when an inode is deleted.
- */
-void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
-{
- struct ubifs_orphan *orph;
-
- spin_lock(&c->orphan_lock);
-
- orph = lookup_orphan(c, inum);
- if (!orph) {
- spin_unlock(&c->orphan_lock);
- ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
- dump_stack();
-
- return;
- }
-
- orphan_delete(c, orph);
-
- spin_unlock(&c->orphan_lock);
-}
-
-/**
* ubifs_orphan_start_commit - start commit of orphans.
* @c: UBIFS file-system description object
*
@@ -724,224 +600,7 @@ int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only)
return err;
}
-/*
- * Everything below is related to debugging.
- */
-
-struct check_orphan {
- struct rb_node rb;
- ino_t inum;
-};
-
-struct check_info {
- unsigned long last_ino;
- unsigned long tot_inos;
- unsigned long missing;
- unsigned long long leaf_cnt;
- struct ubifs_ino_node *node;
- struct rb_root root;
-};
-
-static bool dbg_find_orphan(struct ubifs_info *c, ino_t inum)
-{
- bool found = false;
-
- spin_lock(&c->orphan_lock);
- found = !!lookup_orphan(c, inum);
- spin_unlock(&c->orphan_lock);
-
- return found;
-}
-
-static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)
-{
- struct check_orphan *orphan, *o;
- struct rb_node **p, *parent = NULL;
-
- orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS);
- if (!orphan)
- return -ENOMEM;
- orphan->inum = inum;
-
- p = &root->rb_node;
- while (*p) {
- parent = *p;
- o = rb_entry(parent, struct check_orphan, rb);
- if (inum < o->inum)
- p = &(*p)->rb_left;
- else if (inum > o->inum)
- p = &(*p)->rb_right;
- else {
- kfree(orphan);
- return 0;
- }
- }
- rb_link_node(&orphan->rb, parent, p);
- rb_insert_color(&orphan->rb, root);
- return 0;
-}
-
-static int dbg_find_check_orphan(struct rb_root *root, ino_t inum)
-{
- struct check_orphan *o;
- struct rb_node *p;
-
- p = root->rb_node;
- while (p) {
- o = rb_entry(p, struct check_orphan, rb);
- if (inum < o->inum)
- p = p->rb_left;
- else if (inum > o->inum)
- p = p->rb_right;
- else
- return 1;
- }
- return 0;
-}
-
-static void dbg_free_check_tree(struct rb_root *root)
-{
- struct check_orphan *o, *n;
-
- rbtree_postorder_for_each_entry_safe(o, n, root, rb)
- kfree(o);
-}
-
-static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr,
- void *priv)
+static int dbg_check_orphans(__unused struct ubifs_info *c)
{
- struct check_info *ci = priv;
- ino_t inum;
- int err;
-
- inum = key_inum(c, &zbr->key);
- if (inum != ci->last_ino) {
- /*
- * Lowest node type is the inode node or xattr entry(when
- * selinux/encryption is enabled), so it comes first
- */
- if (key_type(c, &zbr->key) != UBIFS_INO_KEY &&
- key_type(c, &zbr->key) != UBIFS_XENT_KEY)
- ubifs_err(c, "found orphan node ino %lu, type %d",
- (unsigned long)inum, key_type(c, &zbr->key));
- ci->last_ino = inum;
- ci->tot_inos += 1;
- err = ubifs_tnc_read_node(c, zbr, ci->node);
- if (err) {
- ubifs_err(c, "node read failed, error %d", err);
- return err;
- }
- if (ci->node->nlink == 0)
- /* Must be recorded as an orphan */
- if (!dbg_find_check_orphan(&ci->root, inum) &&
- !dbg_find_orphan(c, inum)) {
- ubifs_err(c, "missing orphan, ino %lu",
- (unsigned long)inum);
- ci->missing += 1;
- }
- }
- ci->leaf_cnt += 1;
return 0;
}
-
-static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb)
-{
- struct ubifs_scan_node *snod;
- struct ubifs_orph_node *orph;
- ino_t inum;
- int i, n, err;
-
- list_for_each_entry(snod, &sleb->nodes, list) {
- cond_resched();
- if (snod->type != UBIFS_ORPH_NODE)
- continue;
- orph = snod->node;
- n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
- for (i = 0; i < n; i++) {
- inum = le64_to_cpu(orph->inos[i]);
- err = dbg_ins_check_orphan(&ci->root, inum);
- if (err)
- return err;
- }
- }
- return 0;
-}
-
-static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci)
-{
- int lnum, err = 0;
- void *buf;
-
- /* Check no-orphans flag and skip this if no orphans */
- if (c->no_orphs)
- return 0;
-
- buf = __vmalloc(c->leb_size, GFP_NOFS);
- if (!buf) {
- ubifs_err(c, "cannot allocate memory to check orphans");
- return 0;
- }
-
- for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
- struct ubifs_scan_leb *sleb;
-
- sleb = ubifs_scan(c, lnum, 0, buf, 0);
- if (IS_ERR(sleb)) {
- err = PTR_ERR(sleb);
- break;
- }
-
- err = dbg_read_orphans(ci, sleb);
- ubifs_scan_destroy(sleb);
- if (err)
- break;
- }
-
- vfree(buf);
- return err;
-}
-
-static int dbg_check_orphans(struct ubifs_info *c)
-{
- struct check_info ci;
- int err;
-
- if (!dbg_is_chk_orph(c))
- return 0;
-
- ci.last_ino = 0;
- ci.tot_inos = 0;
- ci.missing = 0;
- ci.leaf_cnt = 0;
- ci.root = RB_ROOT;
- ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
- if (!ci.node) {
- ubifs_err(c, "out of memory");
- return -ENOMEM;
- }
-
- err = dbg_scan_orphans(c, &ci);
- if (err)
- goto out;
-
- err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci);
- if (err) {
- ubifs_err(c, "cannot scan TNC, error %d", err);
- goto out;
- }
-
- if (ci.missing) {
- ubifs_err(c, "%lu missing orphan(s)", ci.missing);
- err = -EINVAL;
- goto out;
- }
-
- dbg_cmt("last inode number is %lu", ci.last_ino);
- dbg_cmt("total number of inodes is %lu", ci.tot_inos);
- dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt);
-
-out:
- dbg_free_check_tree(&ci.root);
- kfree(ci.node);
- return err;
-}
--
2.13.6
Adapt io.c in libubifs, compared with linux kernel implementations:
1. Modify io related functions(eg. ubifs_leb_read/ubifs_leb_write,
etc.), adapt them with userspace io functions lseek/read/write.
2. Remove some functions(eg. record_magic_error, ubifs_bg_wbufs_sync)
which won't be used in fsck/mkfs.
3. Replce ubifs_errc with ubifs_err, because there will be no SB_SILENT
options in mkfs/fsck.
4. Initiate wbuf->size as c->max_write_size.
Signed-off-by: Zhihao Cheng <[email protected]>
---
include/crc32.h | 5 +
ubifs-utils/libubifs/io.c | 364 ++++++++++++----------------------------------
2 files changed, 95 insertions(+), 274 deletions(-)
diff --git a/include/crc32.h b/include/crc32.h
index 9c1f742c..f5271f37 100644
--- a/include/crc32.h
+++ b/include/crc32.h
@@ -10,4 +10,9 @@
/* Return a 32-bit CRC of the contents of the buffer */
extern uint32_t mtd_crc32(uint32_t val, const void *ss, int len);
+static inline uint32_t crc32(uint32_t val, const void *ss, int len)
+{
+ return mtd_crc32(val, ss, len);
+}
+
#endif /* __CRC32_H__ */
diff --git a/ubifs-utils/libubifs/io.c b/ubifs-utils/libubifs/io.c
index 01d8eb17..6f170172 100644
--- a/ubifs-utils/libubifs/io.c
+++ b/ubifs-utils/libubifs/io.c
@@ -58,9 +58,11 @@
* they are read from the flash media.
*/
-#include <linux/crc32.h>
-#include <linux/slab.h>
+#include "kmem.h"
+#include "crc32.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
/**
* ubifs_ro_mode - switch UBIFS to read read-only mode.
@@ -72,7 +74,6 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
if (!c->ro_error) {
c->ro_error = 1;
c->no_chk_data_crc = 0;
- c->vfs_sb->s_flags |= SB_RDONLY;
ubifs_warn(c, "switched to read-only mode, error %d", err);
dump_stack();
}
@@ -87,9 +88,26 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
int len, int even_ebadmsg)
{
- int err;
+ int err = 0;
+ off_t pos = (off_t)lnum * c->leb_size + offs;
+
+ if (!len)
+ return 0;
- err = ubi_read(c->ubi, lnum, buf, offs, len);
+ /*
+ * The %-EBADMSG may be ignored in some case, the buf may not be filled
+ * with data in some buggy mtd drivers. So we'd better to reset the buf
+ * content before reading.
+ */
+ memset(buf, 0, len);
+ if (lseek(c->dev_fd, pos, SEEK_SET) != pos) {
+ err = -errno;
+ goto out;
+ }
+
+ if (read(c->dev_fd, buf, len) != len)
+ err = -errno;
+out:
/*
* In case of %-EBADMSG print the error message only if the
* @even_ebadmsg is true.
@@ -105,15 +123,27 @@ int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
int len)
{
- int err;
+ int err = 0;
+ off_t pos = (off_t)lnum * c->leb_size + offs;
ubifs_assert(c, !c->ro_media && !c->ro_mount);
if (c->ro_error)
return -EROFS;
- if (!dbg_is_tst_rcvry(c))
- err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
- else
- err = dbg_leb_write(c, lnum, buf, offs, len);
+ if (!c->libubi) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (!len)
+ return 0;
+
+ if (lseek(c->dev_fd, pos, SEEK_SET) != pos) {
+ err = -errno;
+ goto out;
+ }
+ if (write(c->dev_fd, buf, len) != len)
+ err = -errno;
+out:
if (err) {
ubifs_err(c, "writing %d bytes to LEB %d:%d failed, error %d",
len, lnum, offs, err);
@@ -125,15 +155,31 @@ int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len)
{
- int err;
+ int err = 0;
+ off_t pos = (off_t)lnum * c->leb_size;
ubifs_assert(c, !c->ro_media && !c->ro_mount);
if (c->ro_error)
return -EROFS;
- if (!dbg_is_tst_rcvry(c))
- err = ubi_leb_change(c->ubi, lnum, buf, len);
- else
- err = dbg_leb_change(c, lnum, buf, len);
+ if (c->libubi) {
+ err = ubi_leb_change_start(c->libubi, c->dev_fd, lnum, len);
+ if (err) {
+ ubifs_err(c, "ubi_leb_change_start failed");
+ err = -errno;
+ goto out;
+ }
+ }
+
+ if (!len)
+ return 0;
+
+ if (lseek(c->dev_fd, pos, SEEK_SET) != pos) {
+ err = -errno;
+ goto out;
+ }
+ if (write(c->dev_fd, buf, len) != len)
+ err = -errno;
+out:
if (err) {
ubifs_err(c, "changing %d bytes in LEB %d failed, error %d",
len, lnum, err);
@@ -145,15 +191,15 @@ int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len)
int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
{
- int err;
+ int err = 0;
ubifs_assert(c, !c->ro_media && !c->ro_mount);
if (c->ro_error)
return -EROFS;
- if (!dbg_is_tst_rcvry(c))
- err = ubi_leb_unmap(c->ubi, lnum);
- else
- err = dbg_leb_unmap(c, lnum);
+ if (!c->libubi)
+ return -ENODEV;
+ if (ubi_leb_unmap(c->dev_fd, lnum))
+ err = -errno;
if (err) {
ubifs_err(c, "unmap LEB %d failed, error %d", lnum, err);
ubifs_ro_mode(c, err);
@@ -164,15 +210,15 @@ int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
int ubifs_leb_map(struct ubifs_info *c, int lnum)
{
- int err;
+ int err = 0;
ubifs_assert(c, !c->ro_media && !c->ro_mount);
if (c->ro_error)
return -EROFS;
- if (!dbg_is_tst_rcvry(c))
- err = ubi_leb_map(c->ubi, lnum);
- else
- err = dbg_leb_map(c, lnum);
+ if (!c->libubi)
+ return -ENODEV;
+ if (ubi_leb_map(c->dev_fd, lnum))
+ err = -errno;
if (err) {
ubifs_err(c, "mapping LEB %d failed, error %d", lnum, err);
ubifs_ro_mode(c, err);
@@ -183,9 +229,12 @@ int ubifs_leb_map(struct ubifs_info *c, int lnum)
int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
{
- int err;
+ int err = 0;
- err = ubi_is_mapped(c->ubi, lnum);
+ if (!c->libubi)
+ return -ENODEV;
+ if (ubi_is_mapped(c->dev_fd, lnum))
+ err = -errno;
if (err < 0) {
ubifs_err(c, "ubi_is_mapped failed for LEB %d, error %d",
lnum, err);
@@ -194,24 +243,6 @@ int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
return err;
}
-static void record_magic_error(struct ubifs_stats_info *stats)
-{
- if (stats)
- stats->magic_errors++;
-}
-
-static void record_node_error(struct ubifs_stats_info *stats)
-{
- if (stats)
- stats->node_errors++;
-}
-
-static void record_crc_error(struct ubifs_stats_info *stats)
-{
- if (stats)
- stats->crc_errors++;
-}
-
/**
* ubifs_check_node - check node.
* @c: UBIFS file-system description object
@@ -256,7 +287,6 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
if (!quiet)
ubifs_err(c, "bad magic %#08x, expected %#08x",
magic, UBIFS_NODE_MAGIC);
- record_magic_error(c->stats);
err = -EUCLEAN;
goto out;
}
@@ -265,7 +295,6 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
if (!quiet)
ubifs_err(c, "bad node type %d", type);
- record_node_error(c->stats);
goto out;
}
@@ -290,7 +319,6 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
if (!quiet)
ubifs_err(c, "bad CRC: calculated %#08x, read %#08x",
crc, node_crc);
- record_crc_error(c->stats);
err = -EUCLEAN;
goto out;
}
@@ -395,7 +423,7 @@ void ubifs_init_node(struct ubifs_info *c, void *node, int len, int pad)
}
}
-void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
+void ubifs_crc_node(__unused struct ubifs_info *c, void *node, int len)
{
struct ubifs_ch *ch = node;
uint32_t crc;
@@ -488,61 +516,6 @@ void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last)
}
/**
- * wbuf_timer_callback_nolock - write-buffer timer callback function.
- * @timer: timer data (write-buffer descriptor)
- *
- * This function is called when the write-buffer timer expires.
- */
-static enum hrtimer_restart wbuf_timer_callback_nolock(struct hrtimer *timer)
-{
- struct ubifs_wbuf *wbuf = container_of(timer, struct ubifs_wbuf, timer);
-
- dbg_io("jhead %s", dbg_jhead(wbuf->jhead));
- wbuf->need_sync = 1;
- wbuf->c->need_wbuf_sync = 1;
- ubifs_wake_up_bgt(wbuf->c);
- return HRTIMER_NORESTART;
-}
-
-/**
- * new_wbuf_timer_nolock - start new write-buffer timer.
- * @c: UBIFS file-system description object
- * @wbuf: write-buffer descriptor
- */
-static void new_wbuf_timer_nolock(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
-{
- ktime_t softlimit = ms_to_ktime(dirty_writeback_interval * 10);
- unsigned long long delta = dirty_writeback_interval;
-
- /* centi to milli, milli to nano, then 10% */
- delta *= 10ULL * NSEC_PER_MSEC / 10ULL;
-
- ubifs_assert(c, !hrtimer_active(&wbuf->timer));
- ubifs_assert(c, delta <= ULONG_MAX);
-
- if (wbuf->no_timer)
- return;
- dbg_io("set timer for jhead %s, %llu-%llu millisecs",
- dbg_jhead(wbuf->jhead),
- div_u64(ktime_to_ns(softlimit), USEC_PER_SEC),
- div_u64(ktime_to_ns(softlimit) + delta, USEC_PER_SEC));
- hrtimer_start_range_ns(&wbuf->timer, softlimit, delta,
- HRTIMER_MODE_REL);
-}
-
-/**
- * cancel_wbuf_timer_nolock - cancel write-buffer timer.
- * @wbuf: write-buffer descriptor
- */
-static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
-{
- if (wbuf->no_timer)
- return;
- wbuf->need_sync = 0;
- hrtimer_cancel(&wbuf->timer);
-}
-
-/**
* ubifs_wbuf_sync_nolock - synchronize write-buffer.
* @wbuf: write-buffer to synchronize
*
@@ -560,7 +533,6 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
struct ubifs_info *c = wbuf->c;
int err, dirt, sync_len;
- cancel_wbuf_timer_nolock(wbuf);
if (!wbuf->used || wbuf->lnum == -1)
/* Write-buffer is empty or not seeked */
return 0;
@@ -658,70 +630,6 @@ int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs)
}
/**
- * ubifs_bg_wbufs_sync - synchronize write-buffers.
- * @c: UBIFS file-system description object
- *
- * This function is called by background thread to synchronize write-buffers.
- * Returns zero in case of success and a negative error code in case of
- * failure.
- */
-int ubifs_bg_wbufs_sync(struct ubifs_info *c)
-{
- int err, i;
-
- ubifs_assert(c, !c->ro_media && !c->ro_mount);
- if (!c->need_wbuf_sync)
- return 0;
- c->need_wbuf_sync = 0;
-
- if (c->ro_error) {
- err = -EROFS;
- goto out_timers;
- }
-
- dbg_io("synchronize");
- for (i = 0; i < c->jhead_cnt; i++) {
- struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
-
- cond_resched();
-
- /*
- * If the mutex is locked then wbuf is being changed, so
- * synchronization is not necessary.
- */
- if (mutex_is_locked(&wbuf->io_mutex))
- continue;
-
- mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
- if (!wbuf->need_sync) {
- mutex_unlock(&wbuf->io_mutex);
- continue;
- }
-
- err = ubifs_wbuf_sync_nolock(wbuf);
- mutex_unlock(&wbuf->io_mutex);
- if (err) {
- ubifs_err(c, "cannot sync write-buffer, error %d", err);
- ubifs_ro_mode(c, err);
- goto out_timers;
- }
- }
-
- return 0;
-
-out_timers:
- /* Cancel all timers to prevent repeated errors */
- for (i = 0; i < c->jhead_cnt; i++) {
- struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
-
- mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
- cancel_wbuf_timer_nolock(wbuf);
- mutex_unlock(&wbuf->io_mutex);
- }
- return err;
-}
-
-/**
* ubifs_wbuf_write_nolock - write data to flash via write-buffer.
* @wbuf: write-buffer
* @buf: node to write
@@ -763,8 +671,6 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
goto out;
}
- cancel_wbuf_timer_nolock(wbuf);
-
if (c->ro_error)
return -EROFS;
@@ -925,9 +831,6 @@ exit:
goto out;
}
- if (wbuf->used)
- new_wbuf_timer_nolock(c, wbuf);
-
return 0;
out:
@@ -1110,32 +1013,30 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
return err;
if (type != ch->node_type) {
- ubifs_errc(c, "bad node type (%d but expected %d)",
- ch->node_type, type);
+ ubifs_err(c, "bad node type (%d but expected %d)",
+ ch->node_type, type);
goto out;
}
err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
if (err) {
- ubifs_errc(c, "expected node type %d", type);
+ ubifs_err(c, "expected node type %d", type);
return err;
}
l = le32_to_cpu(ch->len);
if (l != len) {
- ubifs_errc(c, "bad node length %d, expected %d", l, len);
+ ubifs_err(c, "bad node length %d, expected %d", l, len);
goto out;
}
return 0;
out:
- ubifs_errc(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
- offs, ubi_is_mapped(c->ubi, lnum));
- if (!c->probing) {
- ubifs_dump_node(c, buf, len);
- dump_stack();
- }
+ ubifs_err(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
+ offs, ubi_is_mapped(c->dev_fd, lnum));
+ ubifs_dump_node(c, buf, len);
+ dump_stack();
return -EINVAL;
}
@@ -1166,12 +1067,12 @@ int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
wbuf->used = 0;
wbuf->lnum = wbuf->offs = -1;
/*
- * If the LEB starts at the max. write size aligned address, then
- * write-buffer size has to be set to @c->max_write_size. Otherwise,
- * set it to something smaller so that it ends at the closest max.
- * write size boundary.
+ * Different from linux kernel, there is no way to get leb_start in
+ * userspace, set write-buffer size as @c->max_write_size directly.
+ * Since wbuf->lnum is initialized as -1, wbuf->size will always be
+ * reset in ubifs_wbuf_seek_nolock, it won't be any problems.
*/
- size = c->max_write_size - (c->leb_start % c->max_write_size);
+ size = c->max_write_size;
wbuf->avail = wbuf->size = size;
wbuf->sync_callback = NULL;
mutex_init(&wbuf->io_mutex);
@@ -1179,90 +1080,5 @@ int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
wbuf->c = c;
wbuf->next_ino = 0;
- hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- wbuf->timer.function = wbuf_timer_callback_nolock;
- return 0;
-}
-
-/**
- * ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array.
- * @wbuf: the write-buffer where to add
- * @inum: the inode number
- *
- * This function adds an inode number to the inode array of the write-buffer.
- */
-void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum)
-{
- if (!wbuf->buf)
- /* NOR flash or something similar */
- return;
-
- spin_lock(&wbuf->lock);
- if (wbuf->used)
- wbuf->inodes[wbuf->next_ino++] = inum;
- spin_unlock(&wbuf->lock);
-}
-
-/**
- * wbuf_has_ino - returns if the wbuf contains data from the inode.
- * @wbuf: the write-buffer
- * @inum: the inode number
- *
- * This function returns with %1 if the write-buffer contains some data from the
- * given inode otherwise it returns with %0.
- */
-static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum)
-{
- int i, ret = 0;
-
- spin_lock(&wbuf->lock);
- for (i = 0; i < wbuf->next_ino; i++)
- if (inum == wbuf->inodes[i]) {
- ret = 1;
- break;
- }
- spin_unlock(&wbuf->lock);
-
- return ret;
-}
-
-/**
- * ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode.
- * @c: UBIFS file-system description object
- * @inode: inode to synchronize
- *
- * This function synchronizes write-buffers which contain nodes belonging to
- * @inode. Returns zero in case of success and a negative error code in case of
- * failure.
- */
-int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode)
-{
- int i, err = 0;
-
- for (i = 0; i < c->jhead_cnt; i++) {
- struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
-
- if (i == GCHD)
- /*
- * GC head is special, do not look at it. Even if the
- * head contains something related to this inode, it is
- * a _copy_ of corresponding on-flash node which sits
- * somewhere else.
- */
- continue;
-
- if (!wbuf_has_ino(wbuf, inode->i_ino))
- continue;
-
- mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
- if (wbuf_has_ino(wbuf, inode->i_ino))
- err = ubifs_wbuf_sync_nolock(wbuf);
- mutex_unlock(&wbuf->io_mutex);
-
- if (err) {
- ubifs_ro_mode(c, err);
- return err;
- }
- }
return 0;
}
--
2.13.6
Replace ubifs related source code with the implementation of linux kernel.
It makes userspace implementation be same with linux kernel, then
fsck.ubifs can resuse the code.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 42 ++-
ubifs-utils/common/README | 3 -
ubifs-utils/common/defs.h | 9 -
ubifs-utils/common/key.h | 222 --------------
ubifs-utils/common/lpt.c | 595 ------------------------------------
ubifs-utils/common/lpt.h | 30 --
ubifs-utils/common/sign.c | 148 ++++-----
ubifs-utils/common/sign.h | 65 +---
ubifs-utils/common/super.c | 123 --------
ubifs-utils/common/ubifs.h | 498 ------------------------------
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 236 +++++---------
11 files changed, 185 insertions(+), 1786 deletions(-)
delete mode 100644 ubifs-utils/common/key.h
delete mode 100644 ubifs-utils/common/lpt.c
delete mode 100644 ubifs-utils/common/lpt.h
delete mode 100644 ubifs-utils/common/super.c
delete mode 100644 ubifs-utils/common/ubifs.h
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index c02ec313..936b49fc 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -24,25 +24,51 @@ common_SOURCES = \
ubifs-utils/common/hashtable/hashtable_itr.c \
ubifs-utils/common/devtable.h \
ubifs-utils/common/devtable.c \
- ubifs-utils/common/hexdump.c \
- ubifs-utils/common/ubifs.h \
- ubifs-utils/common/key.h \
- ubifs-utils/common/lpt.h \
- ubifs-utils/common/lpt.c \
- ubifs-utils/common/super.c \
- ubifs-utils/common/sign.h \
- ubifs-utils/libubifs/ubifs-media.h
+ ubifs-utils/common/hexdump.c
+
+libubifs_SOURCES = \
+ ubifs-utils/libubifs/ubifs-media.h \
+ ubifs-utils/libubifs/ubifs.h \
+ ubifs-utils/libubifs/key.h \
+ ubifs-utils/libubifs/misc.h \
+ ubifs-utils/libubifs/io.c \
+ ubifs-utils/libubifs/sb.c \
+ ubifs-utils/libubifs/super.c \
+ ubifs-utils/libubifs/master.c \
+ ubifs-utils/libubifs/debug.h \
+ ubifs-utils/libubifs/debug.c \
+ ubifs-utils/libubifs/scan.c \
+ ubifs-utils/libubifs/find.c \
+ ubifs-utils/libubifs/dir.c \
+ ubifs-utils/libubifs/budget.c \
+ ubifs-utils/libubifs/journal.c \
+ ubifs-utils/libubifs/gc.c \
+ ubifs-utils/libubifs/lpt.c \
+ ubifs-utils/libubifs/lpt_commit.c \
+ ubifs-utils/libubifs/lprops.c \
+ ubifs-utils/libubifs/tnc_misc.c \
+ ubifs-utils/libubifs/tnc.c \
+ ubifs-utils/libubifs/tnc_commit.c \
+ ubifs-utils/libubifs/commit.c \
+ ubifs-utils/libubifs/orphan.c \
+ ubifs-utils/libubifs/log.c \
+ ubifs-utils/libubifs/replay.c \
+ ubifs-utils/libubifs/recovery.c
if WITH_CRYPTO
common_SOURCES += ubifs-utils/common/crypto.c \
ubifs-utils/common/crypto.h \
ubifs-utils/common/fscrypt.c \
ubifs-utils/common/fscrypt.h \
+ ubifs-utils/common/sign.h \
ubifs-utils/common/sign.c
+
+libubifs_SOURCES += ubifs-utils/libubifs/auth.c
endif
mkfs_ubifs_SOURCES = \
$(common_SOURCES) \
+ $(libubifs_SOURCES) \
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
diff --git a/ubifs-utils/common/README b/ubifs-utils/common/README
index 32d04e3a..a93ebd02 100644
--- a/ubifs-utils/common/README
+++ b/ubifs-utils/common/README
@@ -2,10 +2,7 @@ Common Library
* crc16.h and crc16.c were copied from the linux kernel.
* crc32.h and crc32.c were copied from mtd-utils and amended.
-* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel.
-* key.h is copied from fs/ubifs/key.h from the linux kernel.
* defs.h is a bunch of definitions to smooth things over.
-* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended.
* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/
* atomic.h was downloaded from https://the-linux-channel.the-toffee-project.org/index.php?page=6-tutorials-linux-user-space-atomic-operations
* bitops.h and bitops.c were copied from the linux kernel.
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index 506f752c..79451c40 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -15,7 +15,6 @@
#include <assert.h>
#include <execinfo.h>
-#include "linux_types.h"
#include "ubifs.h"
/* common.h requires the PROGRAM_NAME macro */
@@ -27,8 +26,6 @@ extern struct ubifs_info info_;
enum { MKFS_PROGRAM_TYPE = 0 };
-enum { ERR_LEVEL = 1, WARN_LEVEL, INFO_LEVEL, DEBUG_LEVEL };
-
enum {
DUMP_PREFIX_NONE,
DUMP_PREFIX_ADDRESS,
@@ -114,12 +111,6 @@ static inline u64 div_u64(u64 dividend, u32 divisor)
return dividend / divisor;
}
-#define do_div(n,base) ({ \
-int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
-__res; })
-
#if INT_MAX != 0x7fffffff
#error : sizeof(int) must be 4 for this program
#endif
diff --git a/ubifs-utils/common/key.h b/ubifs-utils/common/key.h
deleted file mode 100644
index 2de530b8..00000000
--- a/ubifs-utils/common/key.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * This file is part of UBIFS.
- *
- * Copyright (C) 2006-2008 Nokia Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Authors: Artem Bityutskiy (Битюцкий Артём)
- * Adrian Hunter
- */
-
-/*
- * This header contains various key-related definitions and helper function.
- * UBIFS allows several key schemes, so we access key fields only via these
- * helpers. At the moment only one key scheme is supported.
- *
- * Simple key scheme
- * ~~~~~~~~~~~~~~~~~
- *
- * Keys are 64-bits long. First 32-bits are inode number (parent inode number
- * in case of direntry key). Next 3 bits are node type. The last 29 bits are
- * 4KiB offset in case of inode node, and direntry hash in case of a direntry
- * node. We use "r5" hash borrowed from reiserfs.
- */
-
-#ifndef __UBIFS_KEY_H__
-#define __UBIFS_KEY_H__
-
-#include <assert.h>
-
-/**
- * key_mask_hash - mask a valid hash value.
- * @val: value to be masked
- *
- * We use hash values as offset in directories, so values %0 and %1 are
- * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This
- * function makes sure the reserved values are not used.
- */
-static inline uint32_t key_mask_hash(uint32_t hash)
-{
- hash &= UBIFS_S_KEY_HASH_MASK;
- if (unlikely(hash <= 2))
- hash += 3;
- return hash;
-}
-
-/**
- * key_r5_hash - R5 hash function (borrowed from reiserfs).
- * @s: direntry name
- * @len: name length
- */
-static inline uint32_t key_r5_hash(const char *s, int len)
-{
- uint32_t a = 0;
- const signed char *str = (const signed char *)s;
-
- while (len--) {
- a += *str << 4;
- a += *str >> 4;
- a *= 11;
- str++;
- }
-
- return key_mask_hash(a);
-}
-
-/**
- * key_test_hash - testing hash function.
- * @str: direntry name
- * @len: name length
- */
-static inline uint32_t key_test_hash(const char *str, int len)
-{
- uint32_t a = 0;
-
- len = min_t(uint32_t, len, 4);
- memcpy(&a, str, len);
- return key_mask_hash(a);
-}
-
-/**
- * ino_key_init - initialize inode key.
- * @c: UBIFS file-system description object
- * @key: key to initialize
- * @inum: inode number
- */
-static inline void ino_key_init(union ubifs_key *key, ino_t inum)
-{
- key->u32[0] = inum;
- key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
-}
-
-/**
- * dent_key_init - initialize directory entry key.
- * @c: UBIFS file-system description object
- * @key: key to initialize
- * @inum: parent inode number
- * @nm: direntry name and length
- */
-static inline void dent_key_init(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum,
- const void *name, int name_len)
-{
- uint32_t hash = c->key_hash(name, name_len);
-
- assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
- key->u32[0] = inum;
- key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
-}
-
-/**
- * xent_key_init - initialize extended attribute entry key.
- * @c: UBIFS file-system description object
- * @key: key to initialize
- * @inum: host inode number
- * @nm: extended attribute entry name and length
- */
-static inline void xent_key_init(const struct ubifs_info *c,
- union ubifs_key *key, ino_t inum,
- const struct qstr *nm)
-{
- uint32_t hash = c->key_hash(nm->name, nm->len);
-
- assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
- key->u32[0] = inum;
- key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
-}
-
-/**
- * data_key_init - initialize data key.
- * @c: UBIFS file-system description object
- * @key: key to initialize
- * @inum: inode number
- * @block: block number
- */
-static inline void data_key_init(union ubifs_key *key, ino_t inum,
- unsigned int block)
-{
- assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
- key->u32[0] = inum;
- key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
-}
-
-/**
- * key_write - transform a key from in-memory format.
- * @c: UBIFS file-system description object
- * @from: the key to transform
- * @to: the key to store the result
- */
-static inline void key_write(const union ubifs_key *from, void *to)
-{
- __le32 x[2];
-
- x[0] = cpu_to_le32(from->u32[0]);
- x[1] = cpu_to_le32(from->u32[1]);
-
- memcpy(to, &x, 8);
- memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
-}
-
-/**
- * key_write_idx - transform a key from in-memory format for the index.
- * @c: UBIFS file-system description object
- * @from: the key to transform
- * @to: the key to store the result
- */
-static inline void key_write_idx(const union ubifs_key *from, void *to)
-{
- __le32 x[2];
-
- x[0] = cpu_to_le32(from->u32[0]);
- x[1] = cpu_to_le32(from->u32[1]);
-
- memcpy(to, &x, 8);
-}
-
-/**
- * keys_cmp - compare keys.
- * @c: UBIFS file-system description object
- * @key1: the first key to compare
- * @key2: the second key to compare
- *
- * This function compares 2 keys and returns %-1 if @key1 is less than
- * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2.
- */
-static inline int keys_cmp(const union ubifs_key *key1,
- const union ubifs_key *key2)
-{
- if (key1->u32[0] < key2->u32[0])
- return -1;
- if (key1->u32[0] > key2->u32[0])
- return 1;
- if (key1->u32[1] < key2->u32[1])
- return -1;
- if (key1->u32[1] > key2->u32[1])
- return 1;
-
- return 0;
-}
-
-/**
- * key_type - get key type.
- * @c: UBIFS file-system description object
- * @key: key to get type of
- */
-static inline int key_type(const union ubifs_key *key)
-{
- return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
-}
-
-#endif /* !__UBIFS_KEY_H__ */
diff --git a/ubifs-utils/common/lpt.c b/ubifs-utils/common/lpt.c
deleted file mode 100644
index 0723698d..00000000
--- a/ubifs-utils/common/lpt.c
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * This file is part of UBIFS.
- *
- * Copyright (C) 2006, 2007 Nokia Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Authors: Adrian Hunter
- * Artem Bityutskiy
- */
-
-#ifdef WITH_CRYPTO
-#include <openssl/evp.h>
-#endif
-
-#include "lpt.h"
-#include "bitops.h"
-#include "defs.h"
-#include "ubifs.h"
-#include "crc16.h"
-#include "sign.h"
-
-/**
- * do_calc_lpt_geom - calculate sizes for the LPT area.
- * @c: the UBIFS file-system description object
- *
- * Calculate the sizes of LPT bit fields, nodes, and tree, based on the
- * properties of the flash and whether LPT is "big" (c->big_lpt).
- */
-static void do_calc_lpt_geom(struct ubifs_info *c)
-{
- int n, bits, per_leb_wastage;
- long long sz, tot_wastage;
-
- c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
-
- n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
- c->nnode_cnt = n;
- while (n > 1) {
- n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
- c->nnode_cnt += n;
- }
-
- c->lpt_hght = 1;
- n = UBIFS_LPT_FANOUT;
- while (n < c->pnode_cnt) {
- c->lpt_hght += 1;
- n <<= UBIFS_LPT_FANOUT_SHIFT;
- }
-
- c->space_bits = fls(c->leb_size) - 3;
- c->lpt_lnum_bits = fls(c->lpt_lebs);
- c->lpt_offs_bits = fls(c->leb_size - 1);
- c->lpt_spc_bits = fls(c->leb_size);
-
- n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
- c->pcnt_bits = fls(n - 1);
-
- c->lnum_bits = fls(c->max_leb_cnt - 1);
-
- bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
- (c->big_lpt ? c->pcnt_bits : 0) +
- (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT;
- c->pnode_sz = (bits + 7) / 8;
-
- bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
- (c->big_lpt ? c->pcnt_bits : 0) +
- (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT;
- c->nnode_sz = (bits + 7) / 8;
-
- bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
- c->lpt_lebs * c->lpt_spc_bits * 2;
- c->ltab_sz = (bits + 7) / 8;
-
- bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
- c->lnum_bits * c->lsave_cnt;
- c->lsave_sz = (bits + 7) / 8;
-
- /* Calculate the minimum LPT size */
- c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
- c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
- c->lpt_sz += c->ltab_sz;
- c->lpt_sz += c->lsave_sz;
-
- /* Add wastage */
- sz = c->lpt_sz;
- per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz);
- sz += per_leb_wastage;
- tot_wastage = per_leb_wastage;
- while (sz > c->leb_size) {
- sz += per_leb_wastage;
- sz -= c->leb_size;
- tot_wastage += per_leb_wastage;
- }
- tot_wastage += ALIGN(sz, c->min_io_size) - sz;
- c->lpt_sz += tot_wastage;
-}
-
-/**
- * calc_dflt_lpt_geom - calculate default LPT geometry.
- * @c: the UBIFS file-system description object
- * @main_lebs: number of main area LEBs is passed and returned here
- * @big_lpt: whether the LPT area is "big" is returned here
- *
- * The size of the LPT area depends on parameters that themselves are dependent
- * on the size of the LPT area. This function, successively recalculates the LPT
- * area geometry until the parameters and resultant geometry are consistent.
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt)
-{
- int i, lebs_needed;
- long long sz;
-
- /* Start by assuming the minimum number of LPT LEBs */
- c->lpt_lebs = UBIFS_MIN_LPT_LEBS;
- c->main_lebs = *main_lebs - c->lpt_lebs;
- if (c->main_lebs <= 0)
- return -EINVAL;
-
- /* And assume we will use the small LPT model */
- c->big_lpt = 0;
-
- /*
- * Calculate the geometry based on assumptions above and then see if it
- * makes sense
- */
- do_calc_lpt_geom(c);
-
- /* Small LPT model must have lpt_sz < leb_size */
- if (c->lpt_sz > c->leb_size) {
- /* Nope, so try again using big LPT model */
- c->big_lpt = 1;
- do_calc_lpt_geom(c);
- }
-
- /* Now check there are enough LPT LEBs */
- for (i = 0; i < 64 ; i++) {
- sz = c->lpt_sz * 4; /* Allow 4 times the size */
- sz += c->leb_size - 1;
- do_div(sz, c->leb_size);
- lebs_needed = sz;
- if (lebs_needed > c->lpt_lebs) {
- /* Not enough LPT LEBs so try again with more */
- c->lpt_lebs = lebs_needed;
- c->main_lebs = *main_lebs - c->lpt_lebs;
- if (c->main_lebs <= 0)
- return -EINVAL;
- do_calc_lpt_geom(c);
- continue;
- }
- if (c->ltab_sz > c->leb_size) {
- errmsg("LPT ltab too big");
- return -EINVAL;
- }
- *main_lebs = c->main_lebs;
- *big_lpt = c->big_lpt;
- return 0;
- }
- return -EINVAL;
-}
-
-/**
- * pack_bits - pack bit fields end-to-end.
- * @addr: address at which to pack (passed and next address returned)
- * @pos: bit position at which to pack (passed and next position returned)
- * @val: value to pack
- * @nrbits: number of bits of value to pack (1-32)
- */
-static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits)
-{
- uint8_t *p = *addr;
- int b = *pos;
-
- if (b) {
- *p |= ((uint8_t)val) << b;
- nrbits += b;
- if (nrbits > 8) {
- *++p = (uint8_t)(val >>= (8 - b));
- if (nrbits > 16) {
- *++p = (uint8_t)(val >>= 8);
- if (nrbits > 24) {
- *++p = (uint8_t)(val >>= 8);
- if (nrbits > 32)
- *++p = (uint8_t)(val >>= 8);
- }
- }
- }
- } else {
- *p = (uint8_t)val;
- if (nrbits > 8) {
- *++p = (uint8_t)(val >>= 8);
- if (nrbits > 16) {
- *++p = (uint8_t)(val >>= 8);
- if (nrbits > 24)
- *++p = (uint8_t)(val >>= 8);
- }
- }
- }
- b = nrbits & 7;
- if (b == 0)
- p++;
- *addr = p;
- *pos = b;
-}
-
-/**
- * pack_pnode - pack all the bit fields of a pnode.
- * @c: UBIFS file-system description object
- * @buf: buffer into which to pack
- * @pnode: pnode to pack
- */
-static void pack_pnode(struct ubifs_info *c, void *buf,
- struct ubifs_pnode *pnode)
-{
- uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
- int i, pos = 0;
- uint16_t crc;
-
- pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS);
- if (c->big_lpt)
- pack_bits(&addr, &pos, pnode->num, c->pcnt_bits);
- for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
- pack_bits(&addr, &pos, pnode->lprops[i].free >> 3,
- c->space_bits);
- pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3,
- c->space_bits);
- if (pnode->lprops[i].flags & LPROPS_INDEX)
- pack_bits(&addr, &pos, 1, 1);
- else
- pack_bits(&addr, &pos, 0, 1);
- }
- crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
- c->pnode_sz - UBIFS_LPT_CRC_BYTES);
- addr = buf;
- pos = 0;
- pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
-}
-
-/**
- * pack_nnode - pack all the bit fields of a nnode.
- * @c: UBIFS file-system description object
- * @buf: buffer into which to pack
- * @nnode: nnode to pack
- */
-static void pack_nnode(struct ubifs_info *c, void *buf,
- struct ubifs_nnode *nnode)
-{
- uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
- int i, pos = 0;
- uint16_t crc;
-
- pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS);
- if (c->big_lpt)
- pack_bits(&addr, &pos, nnode->num, c->pcnt_bits);
- for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
- int lnum = nnode->nbranch[i].lnum;
-
- if (lnum == 0)
- lnum = c->lpt_last + 1;
- pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits);
- pack_bits(&addr, &pos, nnode->nbranch[i].offs,
- c->lpt_offs_bits);
- }
- crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
- c->nnode_sz - UBIFS_LPT_CRC_BYTES);
- addr = buf;
- pos = 0;
- pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
-}
-
-/**
- * pack_ltab - pack the LPT's own lprops table.
- * @c: UBIFS file-system description object
- * @buf: buffer into which to pack
- * @ltab: LPT's own lprops table to pack
- */
-static void pack_ltab(struct ubifs_info *c, void *buf,
- struct ubifs_lpt_lprops *ltab)
-{
- uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
- int i, pos = 0;
- uint16_t crc;
-
- pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS);
- for (i = 0; i < c->lpt_lebs; i++) {
- pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits);
- pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits);
- }
- crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
- c->ltab_sz - UBIFS_LPT_CRC_BYTES);
- addr = buf;
- pos = 0;
- pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
-}
-
-/**
- * pack_lsave - pack the LPT's save table.
- * @c: UBIFS file-system description object
- * @buf: buffer into which to pack
- * @lsave: LPT's save table to pack
- */
-static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave)
-{
- uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
- int i, pos = 0;
- uint16_t crc;
-
- pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS);
- for (i = 0; i < c->lsave_cnt; i++)
- pack_bits(&addr, &pos, lsave[i], c->lnum_bits);
- crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
- c->lsave_sz - UBIFS_LPT_CRC_BYTES);
- addr = buf;
- pos = 0;
- pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
-}
-
-/**
- * set_ltab - set LPT LEB properties.
- * @c: UBIFS file-system description object
- * @lnum: LEB number
- * @free: amount of free space
- * @dirty: amount of dirty space
- */
-static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
-{
- pr_debug("LEB %d free %d dirty %d to %d %d\n",
- lnum, c->ltab[lnum - c->lpt_first].free,
- c->ltab[lnum - c->lpt_first].dirty, free, dirty);
- c->ltab[lnum - c->lpt_first].free = free;
- c->ltab[lnum - c->lpt_first].dirty = dirty;
-}
-
-/**
- * calc_nnode_num - calculate nnode number.
- * @row: the row in the tree (root is zero)
- * @col: the column in the row (leftmost is zero)
- *
- * The nnode number is a number that uniquely identifies a nnode and can be used
- * easily to traverse the tree from the root to that nnode.
- *
- * This function calculates and returns the nnode number for the nnode at @row
- * and @col.
- */
-static int calc_nnode_num(int row, int col)
-{
- int num, bits;
-
- num = 1;
- while (row--) {
- bits = (col & (UBIFS_LPT_FANOUT - 1));
- col >>= UBIFS_LPT_FANOUT_SHIFT;
- num <<= UBIFS_LPT_FANOUT_SHIFT;
- num |= bits;
- }
- return num;
-}
-
-/**
- * create_lpt - create LPT.
- * @c: UBIFS file-system description object
- *
- * This function returns %0 on success and a negative error code on failure.
- */
-int create_lpt(struct ubifs_info *c)
-{
- int lnum, err = 0, i, j, cnt, len, alen, row;
- int blnum, boffs, bsz, bcnt;
- struct ubifs_pnode *pnode = NULL;
- struct ubifs_nnode *nnode = NULL;
- void *buf = NULL, *p;
- int *lsave = NULL;
- unsigned int md_len;
-
- pnode = malloc(sizeof(struct ubifs_pnode));
- nnode = malloc(sizeof(struct ubifs_nnode));
- buf = malloc(c->leb_size);
- lsave = malloc(sizeof(int) * c->lsave_cnt);
- if (!pnode || !nnode || !buf || !lsave) {
- err = -ENOMEM;
- goto out;
- }
- memset(pnode, 0 , sizeof(struct ubifs_pnode));
- memset(nnode, 0 , sizeof(struct ubifs_nnode));
-
- hash_digest_init();
-
- c->lscan_lnum = c->main_first;
-
- lnum = c->lpt_first;
- p = buf;
- len = 0;
- /* Number of leaf nodes (pnodes) */
- cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT;
- //printf("pnode_cnt=%d\n",cnt);
-
- /*
- * To calculate the internal node branches, we keep information about
- * the level below.
- */
- blnum = lnum; /* LEB number of level below */
- boffs = 0; /* Offset of level below */
- bcnt = cnt; /* Number of nodes in level below */
- bsz = c->pnode_sz; /* Size of nodes in level below */
-
- /* Add pnodes */
- for (i = 0; i < cnt; i++) {
- if (len + c->pnode_sz > c->leb_size) {
- alen = ALIGN(len, c->min_io_size);
- set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = write_leb(c, lnum++, alen, buf);
- if (err)
- goto out;
- p = buf;
- len = 0;
- }
- /* Fill in the pnode */
- for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
- int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j;
-
- if (k < c->main_lebs)
- pnode->lprops[j] = c->lpt[k];
- else {
- pnode->lprops[j].free = c->leb_size;
- pnode->lprops[j].dirty = 0;
- pnode->lprops[j].flags = 0;
- }
- }
- pack_pnode(c, p, pnode);
-
- hash_digest_update(p, c->pnode_sz);
-
- p += c->pnode_sz;
- len += c->pnode_sz;
- /*
- * pnodes are simply numbered left to right starting at zero,
- * which means the pnode number can be used easily to traverse
- * down the tree to the corresponding pnode.
- */
- pnode->num += 1;
- }
-
- hash_digest_final(c->lpt_hash, &md_len);
-
- row = c->lpt_hght - 1;
- /* Add all nnodes, one level at a time */
- while (1) {
- /* Number of internal nodes (nnodes) at next level */
- cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
- if (cnt == 0)
- cnt = 1;
- for (i = 0; i < cnt; i++) {
- if (len + c->nnode_sz > c->leb_size) {
- alen = ALIGN(len, c->min_io_size);
- set_ltab(c, lnum, c->leb_size - alen,
- alen - len);
- memset(p, 0xff, alen - len);
- err = write_leb(c, lnum++, alen, buf);
- if (err)
- goto out;
- p = buf;
- len = 0;
- }
- /* The root is on row zero */
- if (row == 0) {
- c->lpt_lnum = lnum;
- c->lpt_offs = len;
- }
- /* Set branches to the level below */
- for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
- if (bcnt) {
- if (boffs + bsz > c->leb_size) {
- blnum += 1;
- boffs = 0;
- }
- nnode->nbranch[j].lnum = blnum;
- nnode->nbranch[j].offs = boffs;
- boffs += bsz;
- bcnt--;
- } else {
- nnode->nbranch[j].lnum = 0;
- nnode->nbranch[j].offs = 0;
- }
- }
- nnode->num = calc_nnode_num(row, i);
- pack_nnode(c, p, nnode);
- p += c->nnode_sz;
- len += c->nnode_sz;
- }
- /* Row zero is the top row */
- if (row == 0)
- break;
- /* Update the information about the level below */
- bcnt = cnt;
- bsz = c->nnode_sz;
- row -= 1;
- }
-
- if (c->big_lpt) {
- /* Need to add LPT's save table */
- if (len + c->lsave_sz > c->leb_size) {
- alen = ALIGN(len, c->min_io_size);
- set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = write_leb(c, lnum++, alen, buf);
- if (err)
- goto out;
- p = buf;
- len = 0;
- }
-
- c->lsave_lnum = lnum;
- c->lsave_offs = len;
-
- for (i = 0; i < c->lsave_cnt; i++)
- lsave[i] = c->main_first + i;
-
- pack_lsave(c, p, lsave);
- p += c->lsave_sz;
- len += c->lsave_sz;
- }
-
- /* Need to add LPT's own LEB properties table */
- if (len + c->ltab_sz > c->leb_size) {
- alen = ALIGN(len, c->min_io_size);
- set_ltab(c, lnum, c->leb_size - alen, alen - len);
- memset(p, 0xff, alen - len);
- err = write_leb(c, lnum++, alen, buf);
- if (err)
- goto out;
- p = buf;
- len = 0;
- }
-
- c->ltab_lnum = lnum;
- c->ltab_offs = len;
-
- /* Update ltab before packing it */
- len += c->ltab_sz;
- alen = ALIGN(len, c->min_io_size);
- set_ltab(c, lnum, c->leb_size - alen, alen - len);
-
- pack_ltab(c, p, c->ltab);
- p += c->ltab_sz;
-
- /* Write remaining buffer */
- memset(p, 0xff, alen - len);
- err = write_leb(c, lnum, alen, buf);
- if (err)
- goto out;
-
- c->nhead_lnum = lnum;
- c->nhead_offs = ALIGN(len, c->min_io_size);
-
- pr_debug("lpt_sz: %lld\n", c->lpt_sz);
- pr_debug("space_bits: %d\n", c->space_bits);
- pr_debug("lpt_lnum_bits: %d\n", c->lpt_lnum_bits);
- pr_debug("lpt_offs_bits: %d\n", c->lpt_offs_bits);
- pr_debug("lpt_spc_bits: %d\n", c->lpt_spc_bits);
- pr_debug("pcnt_bits: %d\n", c->pcnt_bits);
- pr_debug("lnum_bits: %d\n", c->lnum_bits);
- pr_debug("pnode_sz: %d\n", c->pnode_sz);
- pr_debug("nnode_sz: %d\n", c->nnode_sz);
- pr_debug("ltab_sz: %d\n", c->ltab_sz);
- pr_debug("lsave_sz: %d\n", c->lsave_sz);
- pr_debug("lsave_cnt: %d\n", c->lsave_cnt);
- pr_debug("lpt_hght: %d\n", c->lpt_hght);
- pr_debug("big_lpt: %d\n", c->big_lpt);
- pr_debug("LPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs);
- pr_debug("LPT head is at %d:%d\n", c->nhead_lnum, c->nhead_offs);
- pr_debug("LPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs);
- if (c->big_lpt)
- pr_debug("LPT lsave is at %d:%d\n",
- c->lsave_lnum, c->lsave_offs);
-out:
- free(lsave);
- free(buf);
- free(nnode);
- free(pnode);
- return err;
-}
diff --git a/ubifs-utils/common/lpt.h b/ubifs-utils/common/lpt.h
deleted file mode 100644
index 86148a2a..00000000
--- a/ubifs-utils/common/lpt.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2008 Nokia Corporation.
- * Copyright (C) 2008 University of Szeged, Hungary
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Authors: Artem Bityutskiy
- * Adrian Hunter
- */
-
-#ifndef __UBIFS_LPT_H__
-#define __UBIFS_LPT_H__
-
-#include "ubifs.h"
-
-int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt);
-int create_lpt(struct ubifs_info *c);
-
-#endif
diff --git a/ubifs-utils/common/sign.c b/ubifs-utils/common/sign.c
index dfbc96bf..032a6acc 100644
--- a/ubifs-utils/common/sign.c
+++ b/ubifs-utils/common/sign.c
@@ -30,8 +30,8 @@
#include "linux_types.h"
#include "sign.h"
-#include "defs.h"
#include "ubifs.h"
+#include "defs.h"
extern struct ubifs_info info_;
static struct ubifs_info *c = &info_;
@@ -39,11 +39,6 @@ static struct ubifs_info *c = &info_;
EVP_MD_CTX *hash_md;
const EVP_MD *md;
-int authenticated(void)
-{
- return c->hash_algo_name != NULL;
-}
-
static int match_string(const char * const *array, size_t n, const char *string)
{
int index;
@@ -242,36 +237,32 @@ out:
return x509;
}
-int sign_superblock_node(void *node)
+int hash_sign_node(const char *auth_key_filename, const char *auth_cert_filename,
+ void *buf, int *len, void *outbuf)
{
EVP_PKEY *private_key;
CMS_ContentInfo *cms = NULL;
X509 *cert = NULL;
BIO *bd, *bm;
void *obuf;
- long len;
int ret;
void *pret;
- struct ubifs_sig_node *sig = node + UBIFS_SB_NODE_SZ;
-
- if (!authenticated())
- return 0;
ERR_load_crypto_strings();
ERR_clear_error();
key_pass = getenv("MKFS_UBIFS_SIGN_PIN");
- bm = BIO_new_mem_buf(node, UBIFS_SB_NODE_SZ);
+ bm = BIO_new_mem_buf(buf, UBIFS_SB_NODE_SZ);
- private_key = read_private_key(c->auth_key_filename, &cert);
+ private_key = read_private_key(auth_key_filename, &cert);
if (!private_key)
return -1;
if (!cert) {
- if (!c->auth_cert_filename)
+ if (!auth_cert_filename)
return errmsg("authentication certificate not provided (--auth-cert)");
- cert = read_x509(c->auth_cert_filename);
+ cert = read_x509(auth_cert_filename);
}
if (!cert)
@@ -302,13 +293,9 @@ int sign_superblock_node(void *node)
if (!ret)
return errmsg("i2d_CMS_bio_stream failed");
- len = BIO_get_mem_data(bd, &obuf);
-
- sig->type = UBIFS_SIGNATURE_TYPE_PKCS7;
- sig->len = cpu_to_le32(len);
- sig->ch.node_type = UBIFS_SIG_NODE;
+ *len = BIO_get_mem_data(bd, &obuf);
- memcpy(sig + 1, obuf, len);
+ memcpy(outbuf, obuf, *len);
BIO_free(bd);
BIO_free(bm);
@@ -316,83 +303,61 @@ int sign_superblock_node(void *node)
return 0;
}
-/**
- * ubifs_node_calc_hash - calculate the hash of a UBIFS node
- * @c: UBIFS file-system description object
- * @node: the node to calculate a hash for
- * @hash: the returned hash
- */
-void ubifs_node_calc_hash(const void *node, uint8_t *hash)
+int hash_digest(const void *buf, unsigned int len, uint8_t *hash)
{
- const struct ubifs_ch *ch = node;
+ int err;
unsigned int md_len;
- if (!authenticated())
- return;
+ err = EVP_DigestInit_ex(hash_md, md, NULL);
+ if (!err)
+ return errmsg("Init hash digest failed");
+ err = EVP_DigestUpdate(hash_md, buf, len);
+ if (!err)
+ return errmsg("Update hash digest failed");
+ err = EVP_DigestFinal_ex(hash_md, hash, &md_len);
+ if (!err)
+ return errmsg("Finalize hash digest failed");
- EVP_DigestInit_ex(hash_md, md, NULL);
- EVP_DigestUpdate(hash_md, node, le32_to_cpu(ch->len));
- EVP_DigestFinal_ex(hash_md, hash, &md_len);
+ return 0;
}
-/**
- * mst_node_calc_hash - calculate the hash of a UBIFS master node
- * @c: UBIFS file-system description object
- * @node: the node to calculate a hash for
- * @hash: the returned hash
- */
-void mst_node_calc_hash(const void *node, uint8_t *hash)
+int hash_digest_init(void)
{
- unsigned int md_len;
+ int err;
- if (!authenticated())
- return;
+ err = EVP_DigestInit_ex(hash_md, md, NULL);
+ if (!err)
+ return errmsg("Init hash digest failed");
- EVP_DigestInit_ex(hash_md, md, NULL);
- EVP_DigestUpdate(hash_md, node + sizeof(struct ubifs_ch),
- UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch));
- EVP_DigestFinal_ex(hash_md, hash, &md_len);
+ return 0;
}
-void hash_digest_init(void)
+int hash_digest_update(const void *buf, int len)
{
- if (!authenticated())
- return;
-
- EVP_DigestInit_ex(hash_md, md, NULL);
-}
+ int err;
-void hash_digest_update(const void *buf, int len)
-{
- if (!authenticated())
- return;
+ err = EVP_DigestUpdate(hash_md, buf, len);
+ if (!err)
+ return errmsg("Update hash digest failed");
- EVP_DigestUpdate(hash_md, buf, len);
+ return 0;
}
-void hash_digest_final(void *hash, unsigned int *len)
+int hash_digest_final(void *hash)
{
- if (!authenticated())
- return;
+ int err;
+ unsigned int md_len;
+
+ err = EVP_DigestFinal_ex(hash_md, hash, &md_len);
+ if (!err)
+ return errmsg("Finalize hash digest failed");
- EVP_DigestFinal_ex(hash_md, hash, len);
+ return 0;
}
-int init_authentication(void)
+int init_authentication(const char *algo_name, int *hash_len, int *hash_algo)
{
- int hash_algo;
-
- if (!c->auth_key_filename && !c->auth_cert_filename && !c->hash_algo_name)
- return 0;
-
- if (!c->auth_key_filename)
- return errmsg("authentication key not given (--auth-key)");
-
- if (!c->hash_algo_name)
- return errmsg("Hash algorithm not given (--hash-algo)");
-
OPENSSL_config(NULL);
-
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
@@ -401,13 +366,30 @@ int init_authentication(void)
return errmsg("Unknown message digest %s", c->hash_algo_name);
hash_md = EVP_MD_CTX_create();
- c->hash_len = EVP_MD_size(md);
-
- hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST, c->hash_algo_name);
- if (hash_algo < 0)
- return errmsg("Unsupported message digest %s", c->hash_algo_name);
+ if (!hash_md)
+ return errmsg("Cannot create md ctx");
+
+ *hash_len = EVP_MD_size(md);
+ if (*hash_len < 0) {
+ EVP_MD_CTX_destroy(hash_md);
+ hash_md = NULL;
+ return errmsg("Cannot init hash len");
+ }
- c->hash_algo = hash_algo;
+ *hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST, algo_name);
+ if (*hash_algo < 0) {
+ EVP_MD_CTX_destroy(hash_md);
+ hash_md = NULL;
+ return errmsg("Unsupported message digest %s", algo_name);
+ }
return 0;
}
+
+void exit_authentication(void)
+{
+ if (hash_md) {
+ EVP_MD_CTX_destroy(hash_md);
+ hash_md = NULL;
+ }
+}
diff --git a/ubifs-utils/common/sign.h b/ubifs-utils/common/sign.h
index fe9fdd8b..f49c76a3 100644
--- a/ubifs-utils/common/sign.h
+++ b/ubifs-utils/common/sign.h
@@ -20,61 +20,20 @@
#ifndef __UBIFS_SIGN_H__
#define __UBIFS_SIGN_H__
-#ifdef WITH_CRYPTO
#include <openssl/evp.h>
-void ubifs_node_calc_hash(const void *node, uint8_t *hash);
-void mst_node_calc_hash(const void *node, uint8_t *hash);
-void hash_digest_init(void);
-void hash_digest_update(const void *buf, int len);
-void hash_digest_final(void *hash, unsigned int *len);
-int init_authentication(void);
-int sign_superblock_node(void *node);
-int authenticated(void);
-
-extern EVP_MD_CTX *hash_md;
-extern const EVP_MD *md;
-
-#else
-static inline void ubifs_node_calc_hash(__attribute__((unused)) const void *node,
- __attribute__((unused)) uint8_t *hash)
-{
-}
-
-static inline void mst_node_calc_hash(__attribute__((unused)) const void *node,
- __attribute__((unused)) uint8_t *hash)
-{
-}
-
-static inline void hash_digest_init(void)
-{
-}
-
-static inline void hash_digest_update(__attribute__((unused)) const void *buf,
- __attribute__((unused)) int len)
-{
-}
+struct shash_desc {
+ void *ctx;
+};
-static inline void hash_digest_final(__attribute__((unused)) void *hash,
- __attribute__((unused)) unsigned int *len)
-{
-}
-
-static inline int init_authentication(void)
-{
- return 0;
-}
-
-static inline int sign_superblock_node(__attribute__((unused)) void *node)
-{
- return 0;
-}
-
-static inline int authenticated(void)
-{
- return 0;
-}
-
-#endif
+int hash_digest(const void *buf, unsigned int len, uint8_t *hash);
+int hash_digest_init(void);
+int hash_digest_update(const void *buf, int len);
+int hash_digest_final(void *hash);
+int init_authentication(const char *algo_name, int *hash_len, int *hash_algo);
+void exit_authentication(void);
+void mst_node_calc_hash(const void *node, uint8_t *hash);
+int hash_sign_node(const char *auth_key_filename, const char *auth_cert_filename,
+ void *buf, int *len, void *outbuf);
#endif /* __UBIFS_SIGN_H__ */
diff --git a/ubifs-utils/common/super.c b/ubifs-utils/common/super.c
deleted file mode 100644
index eee0197d..00000000
--- a/ubifs-utils/common/super.c
+++ /dev/null
@@ -1,123 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "defs.h"
-
-/**
- * open_ubi - open the libubi.
- * @c: the UBIFS file-system description object
- * @node: name of the UBI volume character device to fetch information about
- *
- * This function opens libubi, and initialize device & volume information
- * according to @node. Returns %0 in case of success and %-1 in case of failure.
- */
-int open_ubi(struct ubifs_info *c, const char *node)
-{
- struct stat st;
-
- if (stat(node, &st) || !S_ISCHR(st.st_mode))
- return -1;
-
- c->libubi = libubi_open();
- if (!c->libubi)
- return -1;
- if (ubi_get_vol_info(c->libubi, node, &c->vi))
- goto out_err;
- if (ubi_get_dev_info1(c->libubi, c->vi.dev_num, &c->di))
- goto out_err;
-
- return 0;
-
-out_err:
- close_ubi(c);
- return -1;
-}
-
-void close_ubi(struct ubifs_info *c)
-{
- if (c->libubi) {
- libubi_close(c->libubi);
- c->libubi = NULL;
- }
-}
-
-/**
- * open_target - open the output target.
- * @c: the UBIFS file-system description object
- *
- * Open the output target. The target can be an UBI volume
- * or a file.
- *
- * Returns %0 in case of success and %-1 in case of failure.
- */
-int open_target(struct ubifs_info *c)
-{
- if (c->libubi) {
- c->dev_fd = open(c->dev_name, O_RDWR | O_EXCL);
-
- if (c->dev_fd == -1)
- return sys_errmsg("cannot open the UBI volume '%s'",
- c->dev_name);
- if (ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) {
- close(c->dev_fd);
- return sys_errmsg("ubi_set_property(set direct_write) failed");
- }
- } else {
- c->dev_fd = open(c->dev_name, O_CREAT | O_RDWR | O_TRUNC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
- if (c->dev_fd == -1)
- return sys_errmsg("cannot create output file '%s'",
- c->dev_name);
- }
- return 0;
-}
-
-
-/**
- * close_target - close the output target.
- * @c: the UBIFS file-system description object
- *
- * Close the output target. If the target was an UBI
- * volume, also close libubi.
- *
- * Returns %0 in case of success and %-1 in case of failure.
- */
-int close_target(struct ubifs_info *c)
-{
- if (c->dev_fd >= 0) {
- if (c->libubi && ubi_set_property(c->dev_fd, UBI_VOL_PROP_DIRECT_WRITE, 0))
- return sys_errmsg("ubi_set_property(clear direct_write) failed");
- if (close(c->dev_fd) == -1)
- return sys_errmsg("cannot close the target '%s'", c->dev_name);
- }
- return 0;
-}
-
-/**
- * check_volume_empty - check if the UBI volume is empty.
- * @c: the UBIFS file-system description object
- *
- * This function checks if the UBI volume is empty by looking if its LEBs are
- * mapped or not.
- *
- * Returns %0 in case of success, %1 is the volume is not empty,
- * and a negative error code in case of failure.
- */
-int check_volume_empty(struct ubifs_info *c)
-{
- int lnum, err;
-
- for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) {
- err = ubi_is_mapped(c->dev_fd, lnum);
- if (err < 0)
- return err;
- if (err == 1)
- return 1;
- }
- return 0;
-}
diff --git a/ubifs-utils/common/ubifs.h b/ubifs-utils/common/ubifs.h
deleted file mode 100644
index ed297cc7..00000000
--- a/ubifs-utils/common/ubifs.h
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * This file is part of UBIFS.
- *
- * Copyright (C) 2008 Nokia Corporation.
- * Copyright (C) 2008 University of Szeged, Hungary
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Authors: Artem Bityutskiy
- * Adrian Hunter
- * Zoltan Sogor
- */
-
-#ifndef __UBIFS_H__
-#define __UBIFS_H__
-
-#include "ubifs-media.h"
-#include "libubi.h"
-
-/* Maximum logical eraseblock size in bytes */
-#define UBIFS_MAX_LEB_SZ (2*1024*1024)
-
-/* Minimum amount of data UBIFS writes to the flash */
-#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8)
-
-/* Largest key size supported in this implementation */
-#define CUR_MAX_KEY_LEN UBIFS_SK_LEN
-
-/*
- * There is no notion of truncation key because truncation nodes do not exist
- * in TNC. However, when replaying, it is handy to introduce fake "truncation"
- * keys for truncation nodes because the code becomes simpler. So we define
- * %UBIFS_TRUN_KEY type.
- */
-#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT
-
-/*
- * How much a directory entry/extended attribute entry adds to the parent/host
- * inode.
- */
-#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1, 8)
-
-/* How much an extended attribute adds to the host inode */
-#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8)
-
-/* The below union makes it easier to deal with keys */
-union ubifs_key
-{
- uint8_t u8[CUR_MAX_KEY_LEN];
- uint32_t u32[CUR_MAX_KEY_LEN/4];
- uint64_t u64[CUR_MAX_KEY_LEN/8];
- __le32 j32[CUR_MAX_KEY_LEN/4];
-};
-
-/*
- * LEB properties flags.
- *
- * LPROPS_UNCAT: not categorized
- * LPROPS_DIRTY: dirty > 0, not index
- * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index
- * LPROPS_FREE: free > 0, not empty, not index
- * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs
- * LPROPS_EMPTY: LEB is empty, not taken
- * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken
- * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken
- * LPROPS_CAT_MASK: mask for the LEB categories above
- * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media)
- * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash)
- */
-enum {
- LPROPS_UNCAT = 0,
- LPROPS_DIRTY = 1,
- LPROPS_DIRTY_IDX = 2,
- LPROPS_FREE = 3,
- LPROPS_HEAP_CNT = 3,
- LPROPS_EMPTY = 4,
- LPROPS_FREEABLE = 5,
- LPROPS_FRDI_IDX = 6,
- LPROPS_CAT_MASK = 15,
- LPROPS_TAKEN = 16,
- LPROPS_INDEX = 32,
-};
-
-/**
- * struct ubifs_lprops - logical eraseblock properties.
- * @free: amount of free space in bytes
- * @dirty: amount of dirty space in bytes
- * @flags: LEB properties flags (see above)
- */
-struct ubifs_lprops
-{
- int free;
- int dirty;
- int flags;
-};
-
-/**
- * struct ubifs_lpt_lprops - LPT logical eraseblock properties.
- * @free: amount of free space in bytes
- * @dirty: amount of dirty space in bytes
- */
-struct ubifs_lpt_lprops
-{
- int free;
- int dirty;
-};
-
-struct ubifs_nnode;
-
-/**
- * struct ubifs_cnode - LEB Properties Tree common node.
- * @parent: parent nnode
- * @cnext: next cnode to commit
- * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
- * @iip: index in parent
- * @level: level in the tree (zero for pnodes, greater than zero for nnodes)
- * @num: node number
- */
-struct ubifs_cnode
-{
- struct ubifs_nnode *parent;
- struct ubifs_cnode *cnext;
- unsigned long flags;
- int iip;
- int level;
- int num;
-};
-
-/**
- * struct ubifs_pnode - LEB Properties Tree leaf node.
- * @parent: parent nnode
- * @cnext: next cnode to commit
- * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
- * @iip: index in parent
- * @level: level in the tree (always zero for pnodes)
- * @num: node number
- * @lprops: LEB properties array
- */
-struct ubifs_pnode
-{
- struct ubifs_nnode *parent;
- struct ubifs_cnode *cnext;
- unsigned long flags;
- int iip;
- int level;
- int num;
- struct ubifs_lprops lprops[UBIFS_LPT_FANOUT];
-};
-
-/**
- * struct ubifs_nbranch - LEB Properties Tree internal node branch.
- * @lnum: LEB number of child
- * @offs: offset of child
- * @nnode: nnode child
- * @pnode: pnode child
- * @cnode: cnode child
- */
-struct ubifs_nbranch
-{
- int lnum;
- int offs;
- union
- {
- struct ubifs_nnode *nnode;
- struct ubifs_pnode *pnode;
- struct ubifs_cnode *cnode;
- };
-};
-
-/**
- * struct ubifs_nnode - LEB Properties Tree internal node.
- * @parent: parent nnode
- * @cnext: next cnode to commit
- * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
- * @iip: index in parent
- * @level: level in the tree (always greater than zero for nnodes)
- * @num: node number
- * @nbranch: branches to child nodes
- */
-struct ubifs_nnode
-{
- struct ubifs_nnode *parent;
- struct ubifs_cnode *cnext;
- unsigned long flags;
- int iip;
- int level;
- int num;
- struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT];
-};
-
-/**
- * struct ubifs_lp_stats - statistics of eraseblocks in the main area.
- * @empty_lebs: number of empty LEBs
- * @taken_empty_lebs: number of taken LEBs
- * @idx_lebs: number of indexing LEBs
- * @total_free: total free space in bytes
- * @total_dirty: total dirty space in bytes
- * @total_used: total used space in bytes (includes only data LEBs)
- * @total_dead: total dead space in bytes (includes only data LEBs)
- * @total_dark: total dark space in bytes (includes only data LEBs)
- */
-struct ubifs_lp_stats {
- int empty_lebs;
- int taken_empty_lebs;
- int idx_lebs;
- long long total_free;
- long long total_dirty;
- long long total_used;
- long long total_dead;
- long long total_dark;
-};
-
-/**
- * struct ubifs_zbranch - key/coordinate/length branch stored in znodes.
- * @key: key
- * @znode: znode address in memory
- * @lnum: LEB number of the indexing node
- * @offs: offset of the indexing node within @lnum
- * @len: target node length
- */
-struct ubifs_zbranch
-{
- union ubifs_key key;
- struct ubifs_znode *znode;
- int lnum;
- int offs;
- int len;
-};
-
-/**
- * struct ubifs_znode - in-memory representation of an indexing node.
- * @parent: parent znode or NULL if it is the root
- * @cnext: next znode to commit
- * @flags: flags
- * @time: last access time (seconds)
- * @level: level of the entry in the TNC tree
- * @child_cnt: count of child znodes
- * @iip: index in parent's zbranch array
- * @alt: lower bound of key range has altered i.e. child inserted at slot 0
- * @zbranch: array of znode branches (@c->fanout elements)
- */
-struct ubifs_znode
-{
- struct ubifs_znode *parent;
- struct ubifs_znode *cnext;
- unsigned long flags;
- unsigned long time;
- int level;
- int child_cnt;
- int iip;
- int alt;
-#ifdef CONFIG_UBIFS_FS_DEBUG
- int lnum, offs, len;
-#endif
- struct ubifs_zbranch zbranch[];
-};
-
-/**
- * struct ubifs_info - UBIFS file-system description data structure
- * (per-superblock).
- *
- * @highest_inum: highest used inode number
- * @max_sqnum: current global sequence number
- *
- * @debug_level: level of debug messages, 0 - none, 1 - statistics,
- * 2 - files, 3 - more details
- * @program_type: used to identify the type of current program
- * @program_name: program name
- * @dev_name: device name
- * @dev_fd: opening handler for an UBI volume or an image file
- * @libubi: opening handler for libubi
- *
- * @jhead_cnt: count of journal heads
- * @max_bud_bytes: maximum number of bytes allowed in buds
- *
- * @zroot: zbranch which points to the root index node and znode
- * @ihead_lnum: LEB number of index head
- * @ihead_offs: offset of index head
- *
- * @log_lebs: number of logical eraseblocks in the log
- * @lpt_lebs: number of LEBs used for lprops table
- * @lpt_first: first LEB of the lprops table area
- * @lpt_last: last LEB of the lprops table area
- * @main_lebs: count of LEBs in the main area
- * @main_first: first LEB of the main area
- * @default_compr: default compression type
- * @favor_lzo: favor LZO compression method
- * @favor_percent: lzo vs. zlib threshold used in case favor LZO
- *
- * @key_hash_type: type of the key hash
- * @key_hash: direntry key hash function
- * @key_fmt: key format
- * @key_len: key length
- * @fanout: fanout of the index tree (number of links per indexing node)
- *
- * @min_io_size: minimal input/output unit size
- * @leb_size: logical eraseblock size in bytes
- * @leb_cnt: count of logical eraseblocks
- * @max_leb_cnt: maximum count of logical eraseblocks
- *
- * @old_idx_sz: size of index on flash
- * @lst: lprops statistics
- *
- * @dead_wm: LEB dead space watermark
- * @dark_wm: LEB dark space watermark
- *
- * @di: UBI device information
- * @vi: UBI volume information
- *
- * @gc_lnum: LEB number used for garbage collection
- * @rp_size: reserved pool size
- *
- * @space_bits: number of bits needed to record free or dirty space
- * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT
- * @lpt_offs_bits: number of bits needed to record an offset in the LPT
- * @lpt_spc_bits: number of bits needed to space in the LPT
- * @pcnt_bits: number of bits needed to record pnode or nnode number
- * @lnum_bits: number of bits needed to record LEB number
- * @nnode_sz: size of on-flash nnode
- * @pnode_sz: size of on-flash pnode
- * @ltab_sz: size of on-flash LPT lprops table
- * @lsave_sz: size of on-flash LPT save table
- * @pnode_cnt: number of pnodes
- * @nnode_cnt: number of nnodes
- * @lpt_hght: height of the LPT
- *
- * @lpt_lnum: LEB number of the root nnode of the LPT
- * @lpt_offs: offset of the root nnode of the LPT
- * @nhead_lnum: LEB number of LPT head
- * @nhead_offs: offset of LPT head
- * @big_lpt: flag that LPT is too big to write whole during commit
- * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
- * @double_hash: flag indicating that we can do lookups by hash
- * @lpt_sz: LPT size
- *
- * @ltab_lnum: LEB number of LPT's own lprops table
- * @ltab_offs: offset of LPT's own lprops table
- * @lpt: lprops table
- * @ltab: LPT's own lprops table
- * @lsave_cnt: number of LEB numbers in LPT's save table
- * @lsave_lnum: LEB number of LPT's save table
- * @lsave_offs: offset of LPT's save table
- * @lsave: LPT's save table
- * @lscan_lnum: LEB number of last LPT scan
- *
- * @hash_algo_name: the name of the hashing algorithm to use
- * @hash_algo: The hash algo number (from include/linux/hash_info.h)
- * @auth_key_filename: authentication key file name
- * @x509_filename: x509 certificate file name for authentication
- * @hash_len: the length of the hash
- * @root_idx_hash: The hash of the root index node
- * @lpt_hash: The hash of the LPT
- * @mst_hash: The hash of the master node
- */
-struct ubifs_info
-{
- ino_t highest_inum;
- unsigned long long max_sqnum;
-
- int debug_level;
- int program_type;
- const char *program_name;
- char *dev_name;
- int dev_fd;
- libubi_t libubi;
-
- int jhead_cnt;
- long long max_bud_bytes;
-
- struct ubifs_zbranch zroot;
- int ihead_lnum;
- int ihead_offs;
-
- int log_lebs;
- int lpt_lebs;
- int lpt_first;
- int lpt_last;
- int orph_lebs;
- int main_lebs;
- int main_first;
- int default_compr;
- int favor_lzo;
- int favor_percent;
-
- uint8_t key_hash_type;
- uint32_t (*key_hash)(const char *str, int len);
- int key_fmt;
- int key_len;
- int fanout;
-
- int min_io_size;
- int leb_size;
- int leb_cnt;
- int max_leb_cnt;
-
- unsigned long long old_idx_sz;
- struct ubifs_lp_stats lst;
-
- int dead_wm;
- int dark_wm;
-
- struct ubi_dev_info di;
- struct ubi_vol_info vi;
-
- int gc_lnum;
- long long rp_size;
-
- int space_bits;
- int lpt_lnum_bits;
- int lpt_offs_bits;
- int lpt_spc_bits;
- int pcnt_bits;
- int lnum_bits;
- int nnode_sz;
- int pnode_sz;
- int ltab_sz;
- int lsave_sz;
- int pnode_cnt;
- int nnode_cnt;
- int lpt_hght;
-
- int lpt_lnum;
- int lpt_offs;
- int nhead_lnum;
- int nhead_offs;
- int big_lpt;
- int space_fixup;
- int double_hash;
- int encrypted;
- long long lpt_sz;
-
- int ltab_lnum;
- int ltab_offs;
- struct ubifs_lprops *lpt;
- struct ubifs_lpt_lprops *ltab;
- int lsave_cnt;
- int lsave_lnum;
- int lsave_offs;
- int *lsave;
- int lscan_lnum;
-
- char *hash_algo_name;
- int hash_algo;
- char *auth_key_filename;
- char *auth_cert_filename;
- int hash_len;
- uint8_t root_idx_hash[UBIFS_MAX_HASH_LEN];
- uint8_t lpt_hash[UBIFS_MAX_HASH_LEN];
- uint8_t mst_hash[UBIFS_MAX_HASH_LEN];
-};
-
-/**
- * ubifs_idx_node_sz - return index node size.
- * @c: the UBIFS file-system description object
- * @child_cnt: number of children of this index node
- */
-static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
-{
- return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len + c->hash_len)
- * child_cnt;
-}
-
-/**
- * ubifs_idx_branch - return pointer to an index branch.
- * @c: the UBIFS file-system description object
- * @idx: index node
- * @bnum: branch number
- */
-static inline
-struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
- const struct ubifs_idx_node *idx,
- int bnum)
-{
- return (struct ubifs_branch *)((void *)idx->branches +
- (UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
-}
-
-int write_leb(struct ubifs_info *c, int lnum, int len, void *buf);
-
-/* super.c */
-int open_ubi(struct ubifs_info *c, const char *node);
-void close_ubi(struct ubifs_info *c);
-int open_target(struct ubifs_info *c);
-int close_target(struct ubifs_info *c);
-int check_volume_empty(struct ubifs_info *c);
-
-#endif /* __UBIFS_H__ */
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index fb99222a..2817b6ce 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -46,15 +46,15 @@
#include <zstd.h>
#endif
-#include "linux_types.h"
-#include "defs.h"
+#include "bitops.h"
#include "crypto.h"
#include "fscrypt.h"
#include "ubifs.h"
-#include "lpt.h"
-#include "compr.h"
+#include "defs.h"
+#include "debug.h"
#include "key.h"
-#include "sign.h"
+#include "compr.h"
+#include "misc.h"
#include "devtable.h"
/* Size (prime number) of hash table for link counting */
@@ -291,11 +291,6 @@ static const char *helptext =
"mkfs.ubifs supports building signed images. For this the \"--hash-algo\",\n"
"\"--auth-key\" and \"--auth-cert\" options have to be specified.\n";
-static inline uint8_t *ubifs_branch_hash(struct ubifs_branch *br)
-{
- return (void *)br + sizeof(*br) + c->key_len;
-}
-
/**
* make_path - make a path name from a directory and a name.
* @dir: directory path name
@@ -914,97 +909,13 @@ static int get_options(int argc, char**argv)
}
/**
- * prepare_node - fill in the common header.
- * @node: node
- * @len: node length
- */
-static void prepare_node(void *node, int len)
-{
- uint32_t crc;
- struct ubifs_ch *ch = node;
-
- ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
- ch->len = cpu_to_le32(len);
- ch->group_type = UBIFS_NO_NODE_GROUP;
- ch->sqnum = cpu_to_le64(++c->max_sqnum);
- ch->padding[0] = ch->padding[1] = 0;
- crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
- ch->crc = cpu_to_le32(crc);
-}
-
-/**
- * write_leb - copy the image of a LEB to the output target.
- * @c: the UBIFS file-system description object
- * @lnum: LEB number
- * @len: length of data in the buffer
- * @buf: buffer (must be at least c->leb_size bytes)
- */
-int write_leb(struct ubifs_info *c, int lnum, int len, void *buf)
-{
- off_t pos = (off_t)lnum * c->leb_size;
-
- pr_debug("LEB %d len %d\n", lnum, len);
- memset(buf + len, 0xff, c->leb_size - len);
- if (c->libubi)
- if (ubi_leb_change_start(c->libubi, c->dev_fd, lnum, c->leb_size))
- return sys_errmsg("ubi_leb_change_start failed");
-
- if (lseek(c->dev_fd, pos, SEEK_SET) != pos)
- return sys_errmsg("lseek failed seeking %lld", (long long)pos);
-
- if (write(c->dev_fd, buf, c->leb_size) != c->leb_size)
- return sys_errmsg("write failed writing %d bytes at pos %lld",
- c->leb_size, (long long)pos);
-
- return 0;
-}
-
-/**
* write_empty_leb - copy the image of an empty LEB to the output target.
* @lnum: LEB number
*/
static int write_empty_leb(int lnum)
{
- return write_leb(c, lnum, 0, leb_buf);
-}
-
-/**
- * do_pad - pad a buffer to the minimum I/O size.
- * @buf: buffer
- * @len: buffer length
- */
-static int do_pad(void *buf, int len)
-{
- int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size);
- uint32_t crc;
-
- memset(buf + len, 0xff, alen - len);
- pad_len = wlen - alen;
- pr_debug("len %d pad_len %d\n", len, pad_len);
- buf += alen;
- if (pad_len >= (int)UBIFS_PAD_NODE_SZ) {
- struct ubifs_ch *ch = buf;
- struct ubifs_pad_node *pad_node = buf;
-
- ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
- ch->node_type = UBIFS_PAD_NODE;
- ch->group_type = UBIFS_NO_NODE_GROUP;
- ch->padding[0] = ch->padding[1] = 0;
- ch->sqnum = cpu_to_le64(0);
- ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ);
-
- pad_len -= UBIFS_PAD_NODE_SZ;
- pad_node->pad_len = cpu_to_le32(pad_len);
-
- crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8,
- UBIFS_PAD_NODE_SZ - 8);
- ch->crc = cpu_to_le32(crc);
-
- memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len);
- } else if (pad_len > 0)
- memset(buf, UBIFS_PADDING_BYTE, pad_len);
-
- return wlen;
+ memset(leb_buf, 0xff, c->leb_size);
+ return ubifs_leb_change(c, lnum, leb_buf, c->leb_size);
}
/**
@@ -1015,13 +926,16 @@ static int do_pad(void *buf, int len)
*/
static int write_node(void *node, int len, int lnum)
{
- prepare_node(node, len);
+ int alen = ALIGN(len, 8), wlen = ALIGN(len, c->min_io_size);
+ ubifs_prepare_node(c, node, len, 0);
memcpy(leb_buf, node, len);
+ memset(leb_buf + len, 0xff, alen - len);
+ ubifs_pad(c, leb_buf + alen, wlen - alen);
- len = do_pad(leb_buf, len);
+ memset(leb_buf + wlen, 0xff, c->leb_size - wlen);
- return write_leb(c, lnum, len, leb_buf);
+ return ubifs_leb_change(c, lnum, leb_buf, c->leb_size);
}
/**
@@ -1133,8 +1047,10 @@ static int flush_nodes(void)
if (!head_offs)
return 0;
- len = do_pad(leb_buf, head_offs);
- err = write_leb(c, head_lnum, len, leb_buf);
+ len = ALIGN(head_offs, c->min_io_size);
+ ubifs_pad(c, leb_buf + head_offs, len - head_offs);
+ memset(leb_buf + len, 0xff, c->leb_size - len);
+ err = ubifs_leb_change(c, head_lnum, leb_buf, c->leb_size);
if (err)
return err;
set_lprops(head_lnum, head_offs, head_flags);
@@ -1172,7 +1088,7 @@ static int reserve_space(int len, int *lnum, int *offs)
*/
static int add_node(union ubifs_key *key, char *name, int name_len, void *node, int len)
{
- int err, lnum, offs, type = key_type(key);
+ int err, lnum, offs, type = key_type(c, key);
uint8_t hash[UBIFS_MAX_HASH_LEN];
if (type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY) {
@@ -1184,7 +1100,7 @@ static int add_node(union ubifs_key *key, char *name, int name_len, void *node,
return errmsg("Name given for non dir/xattr node!");
}
- prepare_node(node, len);
+ ubifs_prepare_node(c, node, len, 0);
err = reserve_space(len, &lnum, &offs);
if (err)
@@ -1193,7 +1109,7 @@ static int add_node(union ubifs_key *key, char *name, int name_len, void *node,
memcpy(leb_buf + offs, node, len);
memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len);
- ubifs_node_calc_hash(node, hash);
+ ubifs_node_calc_hash(c, node, hash);
add_to_index(key, name, name_len, lnum, offs, len, hash);
@@ -1206,43 +1122,43 @@ static int add_xattr(struct ubifs_ino_node *host_ino, struct stat *st,
{
struct ubifs_ino_node *ino;
struct ubifs_dent_node *xent;
- struct qstr nm;
+ struct fscrypt_name nm;
char *tmp_name;
union ubifs_key xkey, nkey;
int len, ret;
- nm.len = strlen(name);
- tmp_name = xmalloc(nm.len + 1);
- memcpy(tmp_name, name, nm.len + 1);
- nm.name = tmp_name;
+ fname_len(&nm) = strlen(name);
+ tmp_name = xmalloc(fname_len(&nm) + 1);
+ memcpy(tmp_name, name, fname_len(&nm) + 1);
+ fname_name(&nm) = tmp_name;
host_ino->xattr_cnt++;
- host_ino->xattr_size += CALC_DENT_SIZE(nm.len);
+ host_ino->xattr_size += CALC_DENT_SIZE(fname_len(&nm));
host_ino->xattr_size += CALC_XATTR_BYTES(data_len);
- host_ino->xattr_names += nm.len;
+ host_ino->xattr_names += fname_len(&nm);
- xent = xzalloc(sizeof(*xent) + nm.len + 1);
+ xent = xzalloc(sizeof(*xent) + fname_len(&nm) + 1);
ino = xzalloc(sizeof(*ino) + data_len);
xent_key_init(c, &xkey, inum, &nm);
xent->ch.node_type = UBIFS_XENT_NODE;
- key_write(&xkey, &xent->key);
+ key_write(c, &xkey, &xent->key);
- len = UBIFS_XENT_NODE_SZ + nm.len + 1;
+ len = UBIFS_XENT_NODE_SZ + fname_len(&nm) + 1;
xent->ch.len = len;
xent->padding1 = 0;
xent->type = UBIFS_ITYPE_REG;
- xent->nlen = cpu_to_le16(nm.len);
+ xent->nlen = cpu_to_le16(fname_len(&nm));
- memcpy(xent->name, nm.name, nm.len + 1);
+ memcpy(xent->name, fname_name(&nm), fname_len(&nm) + 1);
inum = ++c->highest_inum;
creat_sqnum = ++c->max_sqnum;
xent->inum = cpu_to_le64(inum);
- ret = add_node(&xkey, tmp_name, nm.len, xent, len);
+ ret = add_node(&xkey, tmp_name, fname_len(&nm), xent, len);
if (ret)
goto out;
@@ -1263,8 +1179,8 @@ static int add_xattr(struct ubifs_ino_node *host_ino, struct stat *st,
ino->compr_type = cpu_to_le16(c->default_compr);
ino->ch.node_type = UBIFS_INO_NODE;
- ino_key_init(&nkey, inum);
- key_write(&nkey, &ino->key);
+ ino_key_init(c, &nkey, inum);
+ key_write(c, &nkey, &ino->key);
ino->size = cpu_to_le64(data_len);
ino->mode = cpu_to_le32(S_IFREG);
@@ -1557,9 +1473,9 @@ static int add_inode(struct stat *st, ino_t inum, void *data,
use_flags |= UBIFS_CRYPT_FL;
memset(ino, 0, UBIFS_INO_NODE_SZ);
- ino_key_init(&key, inum);
+ ino_key_init(c, &key, inum);
ino->ch.node_type = UBIFS_INO_NODE;
- key_write(&key, &ino->key);
+ key_write(c, &key, &ino->key);
ino->creat_sqnum = cpu_to_le64(creat_sqnum);
ino->size = cpu_to_le64(st->st_size);
ino->nlink = cpu_to_le32(st->st_nlink);
@@ -1710,6 +1626,7 @@ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
struct ubifs_dent_node *dent = node_buf;
union ubifs_key key;
struct qstr dname;
+ struct fscrypt_name nm;
char *kname;
int len;
@@ -1747,13 +1664,15 @@ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
*kname_len = ret;
}
- dent_key_init(c, &key, dir_inum, kname, *kname_len);
+ fname_name(&nm) = kname;
+ fname_len(&nm) = *kname_len;
+ dent_key_init(c, &key, dir_inum, &nm);
dent->nlen = cpu_to_le16(*kname_len);
memcpy(dent->name, kname, *kname_len);
dent->name[*kname_len] = '\0';
len = UBIFS_DENT_NODE_SZ + *kname_len + 1;
- key_write(&key, dent->key);
+ key_write(c, &key, dent->key);
return add_node(&key, kname, *kname_len, dent, len);
}
@@ -1849,9 +1768,9 @@ static int add_file(const char *path_name, struct stat *st, ino_t inum,
}
/* Make data node */
memset(dn, 0, UBIFS_DATA_NODE_SZ);
- data_key_init(&key, inum, block_no);
+ data_key_init(c, &key, inum, block_no);
dn->ch.node_type = UBIFS_DATA_NODE;
- key_write(&key, &dn->key);
+ key_write(c, &key, &dn->key);
out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ;
if (c->default_compr == UBIFS_COMPR_NONE &&
!c->encrypted && (flags & FS_COMPR_FL))
@@ -2313,7 +2232,7 @@ static int cmp_idx(const void *a, const void *b)
const struct idx_entry *e2 = *(const struct idx_entry **)b;
int cmp;
- cmp = keys_cmp(&e1->key, &e2->key);
+ cmp = keys_cmp(c, &e1->key, &e2->key);
if (cmp)
return cmp;
return namecmp(e1, e2);
@@ -2330,7 +2249,7 @@ static int add_idx_node(void *node, int child_cnt)
len = ubifs_idx_node_sz(c, child_cnt);
- prepare_node(node, len);
+ ubifs_prepare_node(c, node, len, 0);
err = reserve_space(len, &lnum, &offs);
if (err)
@@ -2339,10 +2258,10 @@ static int add_idx_node(void *node, int child_cnt)
memcpy(leb_buf + offs, node, len);
memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len);
- c->old_idx_sz += ALIGN(len, 8);
+ c->bi.old_idx_sz += ALIGN(len, 8);
pr_debug("at %d:%d len %d index size %llu\n", lnum, offs, len,
- c->old_idx_sz);
+ c->bi.old_idx_sz);
/* The last index node written will be the root */
c->zroot.lnum = lnum;
@@ -2409,15 +2328,15 @@ static int write_index(void)
idx->level = cpu_to_le16(0);
for (j = 0; j < child_cnt; j++, p++) {
br = ubifs_idx_branch(c, idx, j);
- key_write_idx(&(*p)->key, &br->key);
+ key_write_idx(c, &(*p)->key, &br->key);
br->lnum = cpu_to_le32((*p)->lnum);
br->offs = cpu_to_le32((*p)->offs);
br->len = cpu_to_le32((*p)->len);
- memcpy(ubifs_branch_hash(br), (*p)->hash, c->hash_len);
+ memcpy(ubifs_branch_hash(c, br), (*p)->hash, c->hash_len);
}
add_idx_node(idx, child_cnt);
- ubifs_node_calc_hash(idx, hashes + i * c->hash_len);
+ ubifs_node_calc_hash(c, idx, hashes + i * c->hash_len);
}
/* Write level 1 index nodes and above */
level = 0;
@@ -2484,7 +2403,7 @@ static int write_index(void)
* of the index node from the level below.
*/
br = ubifs_idx_branch(c, idx, j);
- key_write_idx(&(*p)->key, &br->key);
+ key_write_idx(c, &(*p)->key, &br->key);
br->lnum = cpu_to_le32(blnum);
br->offs = cpu_to_le32(boffs);
br->len = cpu_to_le32(blen);
@@ -2495,12 +2414,12 @@ static int write_index(void)
boffs += ALIGN(blen, 8);
p += pstep;
- memcpy(ubifs_branch_hash(br),
+ memcpy(ubifs_branch_hash(c, br),
hashes + bn * c->hash_len,
c->hash_len);
}
add_idx_node(idx, child_cnt);
- ubifs_node_calc_hash(idx, hashes + i * c->hash_len);
+ ubifs_node_calc_hash(c, idx, hashes + i * c->hash_len);
}
}
@@ -2571,7 +2490,7 @@ static int finalize_leb_cnt(void)
pr_debug("total_used: %llu\n", c->lst.total_used);
pr_debug("total_dead: %llu\n", c->lst.total_dead);
pr_debug("total_dark: %llu\n", c->lst.total_dark);
- pr_debug("index size: %llu\n", c->old_idx_sz);
+ pr_debug("index size: %llu\n", c->bi.old_idx_sz);
pr_debug("empty_lebs: %d\n", c->lst.empty_lebs);
return 0;
}
@@ -2598,7 +2517,6 @@ static int write_super(void)
buf = xzalloc(c->leb_size);
sup = buf;
- sig = buf + UBIFS_SB_NODE_SZ;
sup->ch.node_type = UBIFS_SB_NODE;
sup->key_hash = c->key_hash_type;
@@ -2634,27 +2552,27 @@ static int write_super(void)
sup->flags |= cpu_to_le32(UBIFS_FLG_DOUBLE_HASH);
if (c->encrypted)
sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
- if (authenticated()) {
+ if (ubifs_authenticated(c)) {
sup->flags |= cpu_to_le32(UBIFS_FLG_AUTHENTICATION);
memcpy(sup->hash_mst, c->mst_hash, c->hash_len);
}
- prepare_node(sup, UBIFS_SB_NODE_SZ);
+ ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 0);
- err = sign_superblock_node(sup);
+ err = ubifs_sign_superblock_node(c, sup);
if (err)
goto out;
sig = (void *)(sup + 1);
- prepare_node(sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len));
+ ubifs_prepare_node(c, sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len), 1);
- len = do_pad(sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len));
+ len = ALIGN(ALIGN(UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len), 8), c->min_io_size);
+ memset(buf + UBIFS_SB_NODE_SZ + len, 0xff, c->leb_size - (UBIFS_SB_NODE_SZ + len));
- err = write_leb(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ + len, sup);
+ err = ubifs_leb_change(c, UBIFS_SB_LNUM, buf, c->leb_size);
if (err)
goto out;
- err = 0;
out:
free(buf);
@@ -2682,7 +2600,7 @@ static int write_master(void)
mst.gc_lnum = cpu_to_le32(c->gc_lnum);
mst.ihead_lnum = cpu_to_le32(c->ihead_lnum);
mst.ihead_offs = cpu_to_le32(c->ihead_offs);
- mst.index_size = cpu_to_le64(c->old_idx_sz);
+ mst.index_size = cpu_to_le64(c->bi.old_idx_sz);
mst.lpt_lnum = cpu_to_le32(c->lpt_lnum);
mst.lpt_offs = cpu_to_le32(c->lpt_offs);
mst.nhead_lnum = cpu_to_le32(c->nhead_lnum);
@@ -2701,7 +2619,7 @@ static int write_master(void)
mst.total_dark = cpu_to_le64(c->lst.total_dark);
mst.leb_cnt = cpu_to_le32(c->leb_cnt);
- if (authenticated()) {
+ if (ubifs_authenticated(c)) {
memcpy(mst.hash_root_idx, c->root_idx_hash, c->hash_len);
memcpy(mst.hash_lpt, c->lpt_hash, c->hash_len);
}
@@ -2714,7 +2632,9 @@ static int write_master(void)
if (err)
return err;
- mst_node_calc_hash(&mst, c->mst_hash);
+ err = ubifs_master_node_calc_hash(c, &mst, c->mst_hash);
+ if (err)
+ return err;
return 0;
}
@@ -2754,7 +2674,8 @@ static int write_lpt(void)
{
int err, lnum;
- err = create_lpt(c);
+ c->lscan_lnum = c->main_first;
+ err = ubifs_create_lpt(c, c->lpt, c->main_lebs, c->lpt_hash);
if (err)
return err;
@@ -2789,7 +2710,7 @@ static int write_orphan_area(void)
*/
static int init(void)
{
- int err, i, main_lebs, big_lpt = 0, sz;
+ int err, main_lebs, big_lpt = 0, sz;
c->highest_inum = UBIFS_FIRST_INO;
@@ -2798,7 +2719,7 @@ static int init(void)
main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
main_lebs -= c->log_lebs + c->orph_lebs;
- err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt);
+ err = ubifs_calc_dflt_lpt_geom(c, &main_lebs, &big_lpt);
if (err)
return err;
@@ -2811,13 +2732,6 @@ static int init(void)
c->lpt_last = c->lpt_first + c->lpt_lebs - 1;
c->lpt = xmalloc(c->main_lebs * sizeof(struct ubifs_lprops));
- c->ltab = xmalloc(c->lpt_lebs * sizeof(struct ubifs_lprops));
-
- /* Initialize LPT's own lprops */
- for (i = 0; i < c->lpt_lebs; i++) {
- c->ltab[i].free = c->leb_size;
- c->ltab[i].dirty = 0;
- }
c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size);
c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size);
@@ -2877,7 +2791,6 @@ static void deinit(void)
#endif
free(c->lpt);
- free(c->ltab);
free(leb_buf);
free(node_buf);
free(block_buf);
@@ -2885,6 +2798,7 @@ static void deinit(void)
free(hash_table);
destroy_compression();
free_devtable_info();
+ ubifs_exit_authentication(c);
}
/**
@@ -2904,7 +2818,7 @@ static int mkfs(void)
if (err)
goto out;
- err = init_authentication();
+ err = ubifs_init_authentication(c);
if (err)
goto out;
@@ -2951,9 +2865,7 @@ int main(int argc, char *argv[])
{
int err;
- info_.program_name = MKFS_PROGRAM_NAME;
- info_.program_type = MKFS_PROGRAM_TYPE;
- info_.debug_level = WARN_LEVEL;
+ init_ubifs_info(c, MKFS_PROGRAM_TYPE);
if (crypto_init())
return -1;
--
2.13.6
This is the 12/12 step of rebuilding. Since all meta areas are ready,
master node can be updated. After this step, a consistent UBIFS image
can be mounted, and it should pass all tests from chk_fs, chk_general,
chk_index, chk_lprops and chk_orphans.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/rebuild_fs.c | 78 +++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 9ea4c224..382687b3 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -1126,6 +1126,7 @@ static int record_file_used_lebs(struct ubifs_info *c,
file->ino.is_xattr ? "xattr" :
ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
c->dev_name);
+ c->highest_inum = max_t(ino_t, c->highest_inum, file->inum);
err = parse_node_info(c, &file->ino.header, &file->ino.key,
NULL, 0, idx_list, idx_cnt);
@@ -1345,6 +1346,75 @@ static int clean_log(struct ubifs_info *c)
}
/**
+ * write_master - write master nodes.
+ * @c: UBIFS file-system description object
+ *
+ * This function updates meta information into master node and writes master
+ * node into master area.
+ */
+static int write_master(struct ubifs_info *c)
+{
+ int err, lnum;
+ struct ubifs_mst_node *mst;
+
+ mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+ if (!mst)
+ return -ENOMEM;
+
+ mst->ch.node_type = UBIFS_MST_NODE;
+ mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
+ mst->highest_inum = cpu_to_le64(c->highest_inum);
+ mst->cmt_no = 0;
+ mst->root_lnum = cpu_to_le32(c->zroot.lnum);
+ mst->root_offs = cpu_to_le32(c->zroot.offs);
+ mst->root_len = cpu_to_le32(c->zroot.len);
+ mst->gc_lnum = cpu_to_le32(c->gc_lnum);
+ mst->ihead_lnum = cpu_to_le32(c->ihead_lnum);
+ mst->ihead_offs = cpu_to_le32(c->ihead_offs);
+ mst->index_size = cpu_to_le64(c->calc_idx_sz);
+ mst->lpt_lnum = cpu_to_le32(c->lpt_lnum);
+ mst->lpt_offs = cpu_to_le32(c->lpt_offs);
+ mst->nhead_lnum = cpu_to_le32(c->nhead_lnum);
+ mst->nhead_offs = cpu_to_le32(c->nhead_offs);
+ mst->ltab_lnum = cpu_to_le32(c->ltab_lnum);
+ mst->ltab_offs = cpu_to_le32(c->ltab_offs);
+ mst->lsave_lnum = cpu_to_le32(c->lsave_lnum);
+ mst->lsave_offs = cpu_to_le32(c->lsave_offs);
+ mst->lscan_lnum = cpu_to_le32(c->main_first);
+ mst->empty_lebs = cpu_to_le32(c->lst.empty_lebs);
+ mst->idx_lebs = cpu_to_le32(c->lst.idx_lebs);
+ mst->leb_cnt = cpu_to_le32(c->leb_cnt);
+ mst->total_free = cpu_to_le64(c->lst.total_free);
+ mst->total_dirty = cpu_to_le64(c->lst.total_dirty);
+ mst->total_used = cpu_to_le64(c->lst.total_used);
+ mst->total_dead = cpu_to_le64(c->lst.total_dead);
+ mst->total_dark = cpu_to_le64(c->lst.total_dark);
+ mst->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+
+ lnum = UBIFS_MST_LNUM;
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+ err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, lnum, 0,
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ goto out;
+ lnum++;
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+ err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, lnum, 0,
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ goto out;
+
+out:
+ kfree(mst);
+
+ return err;
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -1429,6 +1499,14 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
goto out;
}
err = ubifs_clear_orphans(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 12. Write master node. */
+ log_out(c, "Write master");
+ err = write_master(c);
if (err)
exit_code |= FSCK_ERROR;
--
2.13.6
This is the 4/18 step of fsck. Consolidate log to ensure enough space
in log area. There could be following possible errors:
1. corrupted scanning data in log area: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 1 +
ubifs-utils/fsck.ubifs/load_fs.c | 26 ++++++++++++++++++++++++++
ubifs-utils/libubifs/log.c | 1 +
3 files changed, 28 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 4d5296d7..9d69a4fd 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -433,6 +433,7 @@ int main(int argc, char *argv[])
* Step 1: Read master & init lpt
* Step 2: Replay journal
* Step 3: Handle orphan nodes
+ * Step 4: Consolidate log
*/
err = ubifs_load_filesystem(c);
if (err) {
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index f376383c..42b1afaa 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -199,6 +199,32 @@ int ubifs_load_filesystem(struct ubifs_info *c)
goto out_orphans;
}
+ if (!c->ro_mount) {
+ int lnum;
+
+ /* Check for enough log space */
+ lnum = c->lhead_lnum + 1;
+ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ lnum = UBIFS_LOG_LNUM;
+ if (lnum == c->ltail_lnum) {
+ log_out(c, "Consolidate log");
+ err = ubifs_consolidate_log(c);
+ if (err) {
+ unsigned int reason = get_failure_reason_callback(c);
+
+ clear_failure_reason_callback(c);
+ if (reason & FR_DATA_CORRUPTED) {
+ if (fix_problem(c, LOG_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ } else {
+ ubifs_assert(c, reason == 0);
+ exit_code |= FSCK_ERROR;
+ }
+ goto out_orphans;
+ }
+ }
+ }
+
c->mounting = 0;
return 0;
diff --git a/ubifs-utils/libubifs/log.c b/ubifs-utils/libubifs/log.c
index 0d459261..c3dfd98e 100644
--- a/ubifs-utils/libubifs/log.c
+++ b/ubifs-utils/libubifs/log.c
@@ -711,6 +711,7 @@ int ubifs_consolidate_log(struct ubifs_info *c)
destroy_done_tree(&done_tree);
vfree(buf);
if (write_lnum == c->lhead_lnum) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "log is too full");
return -EINVAL;
}
--
2.13.6
This is the 3/18 step of fsck. Handle orphan nodes, update TNC & LPT.
There could be following steps and possible errors:
Step 1. scan orphan LEB, get all orphan nodes
a. corrupted scanning data in orphan area: danger mode and normal mode
with 'yes' answer will drop orphan LEB, other modes will exit.
Step 2. parse orphan node, find the original inode for each inum
a. corrupted node searched from TNC: skip node for danger mode and
normal mode with 'yes' answer, other modes will exit.
b. corrupted index node read from TNC: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
Step 4. remove inode for each inum, update TNC & LPT
a. corrupted index node read from TNC: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
b. corrupted lpt: Set %FR_LPT_CORRUPTED for lpt status. Ignore the
error.
c. incorrect lpt: Set %FR_LPT_INCORRECT for lpt status. Ignore the
error.
d. If lpt status is not empty, skip updating lpt.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 2 ++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +-
ubifs-utils/fsck.ubifs/load_fs.c | 19 +++++++++++++++++
ubifs-utils/fsck.ubifs/problem.c | 20 ++++++++++++++++--
ubifs-utils/libubifs/orphan.c | 42 ++++++++++++++++++++++++++++++++++---
ubifs-utils/libubifs/ubifs.h | 1 +
6 files changed, 80 insertions(+), 6 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 3e7e7093..4d5296d7 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -328,6 +328,7 @@ static bool fsck_can_ignore_failure(const struct ubifs_info *c,
static const unsigned int reason_mapping_table[] = {
BUD_CORRUPTED, /* FR_H_BUD_CORRUPTED */
TNC_DATA_CORRUPTED, /* FR_H_TNC_DATA_CORRUPTED */
+ ORPHAN_CORRUPTED, /* FR_H_ORPHAN_CORRUPTED */
};
static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason,
@@ -431,6 +432,7 @@ int main(int argc, char *argv[])
* Init: Read superblock
* Step 1: Read master & init lpt
* Step 2: Replay journal
+ * Step 3: Handle orphan nodes
*/
err = ubifs_load_filesystem(c);
if (err) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 917f124e..b98c3f7d 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -38,7 +38,7 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/* Types of inconsistent problems */
enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
- TNC_CORRUPTED, TNC_DATA_CORRUPTED };
+ TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED };
struct scanned_file;
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index f45c4117..f376383c 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -183,10 +183,28 @@ int ubifs_load_filesystem(struct ubifs_info *c)
/* Calculate 'min_idx_lebs' after journal replay */
c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ log_out(c, "Handle orphan nodes");
+ err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
+ if (err) {
+ unsigned int reason = get_failure_reason_callback(c);
+
+ clear_failure_reason_callback(c);
+ if (reason & FR_TNC_CORRUPTED) {
+ if (fix_problem(c, TNC_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ } else {
+ ubifs_assert(c, reason == 0);
+ exit_code |= FSCK_ERROR;
+ }
+ goto out_orphans;
+ }
+
c->mounting = 0;
return 0;
+out_orphans:
+ free_orphans(c);
out_journal:
destroy_journal(c);
out_lpt:
@@ -214,6 +232,7 @@ void ubifs_destroy_filesystem(struct ubifs_info *c)
{
destroy_journal(c);
free_wbufs(c);
+ free_orphans(c);
ubifs_lpt_free(c, 0);
c->max_sqnum = 0;
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 9df2c2ae..9c8730a5 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -41,6 +41,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted bud LEB"}, // BUD_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted index node"}, // TNC_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data searched from TNC"}, // TNC_DATA_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted orphan LEB"}, // ORPHAN_CORRUPTED
};
static const char *get_question(const struct fsck_problem *problem,
@@ -54,6 +55,8 @@ static const char *get_question(const struct fsck_problem *problem,
return "Drop bud?";
case TNC_DATA_CORRUPTED:
return "Drop it?";
+ case ORPHAN_CORRUPTED:
+ return "Drop orphans on the LEB?";
}
return "Fix it?";
@@ -63,13 +66,26 @@ static void print_problem(const struct ubifs_info *c,
const struct fsck_problem *problem, int problem_type,
const void *priv)
{
- if (problem_type == BUD_CORRUPTED) {
+ switch (problem_type) {
+ case BUD_CORRUPTED:
+ {
const struct ubifs_bud *bud = (const struct ubifs_bud *)priv;
log_out(c, "problem: %s %d:%d %s", problem->desc, bud->lnum,
bud->start, dbg_jhead(bud->jhead));
- } else
+ break;
+ }
+ case ORPHAN_CORRUPTED:
+ {
+ const int *lnum = (const int *)priv;
+
+ log_out(c, "problem: %s %d", problem->desc, *lnum);
+ break;
+ }
+ default:
log_out(c, "problem: %s", problem->desc);
+ break;
+ }
}
static void fatal_error(const struct ubifs_info *c,
diff --git a/ubifs-utils/libubifs/orphan.c b/ubifs-utils/libubifs/orphan.c
index 26668cbd..baa4db78 100644
--- a/ubifs-utils/libubifs/orphan.c
+++ b/ubifs-utils/libubifs/orphan.c
@@ -423,6 +423,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
ubifs_dump_node(c, snod->node,
c->leb_size - snod->offs);
err = -EINVAL;
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
goto out_free;
}
@@ -452,6 +453,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
ubifs_dump_node(c, snod->node,
c->leb_size - snod->offs);
err = -EINVAL;
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
goto out_free;
}
dbg_rcvry("out of date LEB %d", sleb->lnum);
@@ -471,8 +473,19 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
ino_key_init(c, &key, inum);
err = ubifs_tnc_lookup(c, &key, ino);
- if (err && err != -ENOENT)
+ if (err && err != -ENOENT) {
+ unsigned int reason;
+
+ reason = get_failure_reason_callback(c);
+ if (reason & FR_DATA_CORRUPTED) {
+ test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED);
+ if (handle_failure_callback(c, FR_H_TNC_DATA_CORRUPTED, NULL)) {
+ /* Leave the inode to be deleted by subsequent steps */
+ continue;
+ }
+ }
goto out_free;
+ }
/*
* Check whether an inode can really get deleted.
@@ -483,8 +496,11 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
(unsigned long)inum);
err = ubifs_tnc_remove_ino(c, inum);
- if (err)
+ if (err) {
+ if (c->program_type == FSCK_PROGRAM_TYPE)
+ goto out_free;
goto out_ro;
+ }
}
}
@@ -553,13 +569,33 @@ static int kill_orphans(struct ubifs_info *c)
c->sbuf, -1);
}
if (IS_ERR(sleb)) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED) &&
+ handle_failure_callback(c, FR_H_ORPHAN_CORRUPTED, &lnum)) {
+ /* Skip the orphan LEB. */
+ continue;
+ }
err = PTR_ERR(sleb);
break;
}
}
err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate,
&last_flagged);
- if (err || outofdate) {
+ if (err) {
+ unsigned int reason = get_failure_reason_callback(c);
+
+ if (reason & FR_DATA_CORRUPTED) {
+ test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED);
+ if (handle_failure_callback(c, FR_H_ORPHAN_CORRUPTED, &lnum)) {
+ err = 0;
+ /* Skip the orphan LEB. */
+ ubifs_scan_destroy(sleb);
+ continue;
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ break;
+ }
+ if (outofdate) {
ubifs_scan_destroy(sleb);
break;
}
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index ae812ff8..8a506a8b 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1546,6 +1546,7 @@ enum {
enum {
FR_H_BUD_CORRUPTED = 0, /* Bud LEB is corrupted */
FR_H_TNC_DATA_CORRUPTED, /* Data searched from TNC is corrupted */
+ FR_H_ORPHAN_CORRUPTED, /* Orphan LEB is corrupted */
};
/* Callback functions for failure(which can be handled by fsck) happens. */
static inline void set_failure_reason_callback(const struct ubifs_info *c,
--
2.13.6
This is the 10/12 step of rebuilding. All LEBs' properties can be
calculated in previous steps according to all nodes' position, then
construct LPT just like mkfs does, and write LPT on flash.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 149 ++++++++++++++++++++++++++++--------
ubifs-utils/libubifs/ubifs.h | 2 +
3 files changed, 119 insertions(+), 34 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index bc1d7503..1d97aed3 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -186,6 +186,7 @@ struct scanned_file {
* @write_buf: write buffer for LEB @head_lnum
* @head_lnum: current writing LEB number
* @head_offs: current writing position in LEB @head_lnum
+ * @need_update_lpt: whether to update lpt while writing index nodes
*/
struct ubifs_rebuild_info {
unsigned long *used_lebs;
@@ -194,6 +195,7 @@ struct ubifs_rebuild_info {
void *write_buf;
int head_lnum;
int head_offs;
+ bool need_update_lpt;
};
/**
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index e1d1957f..085df2b9 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -489,6 +489,22 @@ lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si,
return NULL;
}
+static void update_lpt(struct ubifs_info *c, struct scanned_node *sn,
+ bool deleted)
+{
+ int index = sn->lnum - c->main_first;
+ int pos = sn->offs + ALIGN(sn->len, 8);
+
+ set_bit(index, FSCK(c)->rebuild->used_lebs);
+ FSCK(c)->rebuild->lpts[index].end = max_t(int,
+ FSCK(c)->rebuild->lpts[index].end, pos);
+
+ if (deleted)
+ return;
+
+ FSCK(c)->rebuild->lpts[index].used += ALIGN(sn->len, 8);
+}
+
/**
* remove_del_nodes - remove deleted nodes from valid node tree.
* @c: UBIFS file-system description object
@@ -512,13 +528,7 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_ino_node = lookup_valid_ino_node(c, si, del_ino_node);
if (valid_ino_node) {
- int lnum = del_ino_node->header.lnum - c->main_first;
- int pos = del_ino_node->header.offs +
- ALIGN(del_ino_node->header.len, 8);
-
- set_bit(lnum, FSCK(c)->rebuild->used_lebs);
- FSCK(c)->rebuild->lpts[lnum].end =
- max_t(int, FSCK(c)->rebuild->lpts[lnum].end, pos);
+ update_lpt(c, &del_ino_node->header, true);
rb_erase(&valid_ino_node->rb, &si->valid_inos);
kfree(valid_ino_node);
}
@@ -534,13 +544,7 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_dent_node = lookup_valid_dent_node(c, si, del_dent_node);
if (valid_dent_node) {
- int lnum = del_dent_node->header.lnum - c->main_first;
- int pos = del_dent_node->header.offs +
- ALIGN(del_dent_node->header.len, 8);
-
- set_bit(lnum, FSCK(c)->rebuild->used_lebs);
- FSCK(c)->rebuild->lpts[lnum].end =
- max_t(int, FSCK(c)->rebuild->lpts[lnum].end, pos);
+ update_lpt(c, &del_dent_node->header, true);
rb_erase(&valid_dent_node->rb, &si->valid_dents);
kfree(valid_dent_node);
}
@@ -730,12 +734,12 @@ static void init_root_ino(struct ubifs_info *c, struct ubifs_ino_node *ino)
* get_free_leb - get a free LEB according to @FSCK(c)->rebuild->used_lebs.
* @c: UBIFS file-system description object
*
- * This function tries to find a free LEB, %0 is returned if found, otherwise
- * %ENOSPC is returned.
+ * This function tries to find a free LEB, lnum is returned if found, otherwise
+ * %-ENOSPC is returned.
*/
static int get_free_leb(struct ubifs_info *c)
{
- int lnum, err;
+ int lnum;
lnum = find_next_zero_bit(FSCK(c)->rebuild->used_lebs, c->main_lebs, 0);
if (lnum >= c->main_lebs) {
@@ -745,14 +749,7 @@ static int get_free_leb(struct ubifs_info *c)
set_bit(lnum, FSCK(c)->rebuild->used_lebs);
lnum += c->main_first;
- err = ubifs_leb_unmap(c, lnum);
- if (err)
- return err;
-
- FSCK(c)->rebuild->head_lnum = lnum;
- FSCK(c)->rebuild->head_offs = 0;
-
- return 0;
+ return lnum;
}
/**
@@ -780,6 +777,14 @@ static int flush_write_buf(struct ubifs_info *c)
if (err)
return err;
+ if (FSCK(c)->rebuild->need_update_lpt) {
+ int index = FSCK(c)->rebuild->head_lnum - c->main_first;
+
+ FSCK(c)->rebuild->lpts[index].free = c->leb_size - len;
+ FSCK(c)->rebuild->lpts[index].dirty = pad;
+ FSCK(c)->rebuild->lpts[index].flags = LPROPS_INDEX;
+ }
+
FSCK(c)->rebuild->head_lnum = -1;
return 0;
@@ -797,13 +802,20 @@ static int flush_write_buf(struct ubifs_info *c)
*/
static int reserve_space(struct ubifs_info *c, int len, int *lnum, int *offs)
{
- int err;
+ int err, new_lnum;
if (FSCK(c)->rebuild->head_lnum == -1) {
get_new:
- err = get_free_leb(c);
+ new_lnum = get_free_leb(c);
+ if (new_lnum < 0)
+ return new_lnum;
+
+ err = ubifs_leb_unmap(c, new_lnum);
if (err)
return err;
+
+ FSCK(c)->rebuild->head_lnum = new_lnum;
+ FSCK(c)->rebuild->head_offs = 0;
}
if (len > c->leb_size - FSCK(c)->rebuild->head_offs) {
@@ -921,15 +933,9 @@ static int parse_node_info(struct ubifs_info *c, struct scanned_node *sn,
union ubifs_key *key, char *name, int name_len,
struct list_head *idx_list, int *idx_cnt)
{
- int lnum, pos;
struct idx_entry *e;
- lnum = sn->lnum - c->main_first;
- pos = sn->offs + ALIGN(sn->len, 8);
-
- set_bit(lnum, FSCK(c)->rebuild->used_lebs);
- FSCK(c)->rebuild->lpts[lnum].end = max_t(int,
- FSCK(c)->rebuild->lpts[lnum].end, pos);
+ update_lpt(c, sn, idx_cnt == NULL);
if (idx_cnt == NULL)
/* Skip truncation node. */
@@ -1026,6 +1032,7 @@ static int build_tnc(struct ubifs_info *c, struct list_head *lower_idxs,
return -ENOMEM;
list_sort(c, lower_idxs, cmp_idx);
+ FSCK(c)->rebuild->need_update_lpt = true;
ubifs_assert(c, lower_cnt != 0);
@@ -1089,6 +1096,7 @@ static int build_tnc(struct ubifs_info *c, struct list_head *lower_idxs,
/* Flush the last index LEB */
err = flush_write_buf(c);
+ FSCK(c)->rebuild->need_update_lpt = false;
out:
list_for_each_entry_safe(e, tmp_e, lower_idxs, list) {
@@ -1239,6 +1247,71 @@ out_idx_list:
}
/**
+ * build_lpt - construct LPT and write it into flash.
+ * @c: UBIFS file-system description object
+ *
+ * This function builds LPT according to @FSCK(c)->rebuild->lpts and writes
+ * LPT into flash.
+ */
+static int build_lpt(struct ubifs_info *c)
+{
+ int i, len, free, dirty, lnum;
+ u8 hash_lpt[UBIFS_HASH_ARR_SZ];
+
+ memset(&c->lst, 0, sizeof(struct ubifs_lp_stats));
+ /* Set gc lnum. */
+ lnum = get_free_leb(c);
+ if (lnum < 0)
+ return lnum;
+ c->gc_lnum = lnum;
+
+ /* Update LPT. */
+ for (i = 0; i < c->main_lebs; i++) {
+ if (!test_bit(i, FSCK(c)->rebuild->used_lebs) ||
+ c->gc_lnum == i + c->main_first) {
+ free = c->leb_size;
+ dirty = 0;
+ } else if (FSCK(c)->rebuild->lpts[i].flags & LPROPS_INDEX) {
+ free = FSCK(c)->rebuild->lpts[i].free;
+ dirty = FSCK(c)->rebuild->lpts[i].dirty;
+ } else {
+ len = ALIGN(FSCK(c)->rebuild->lpts[i].end, c->min_io_size);
+ free = c->leb_size - len;
+ dirty = len - FSCK(c)->rebuild->lpts[i].used;
+
+ if (dirty == c->leb_size) {
+ free = c->leb_size;
+ dirty = 0;
+ }
+ }
+
+ FSCK(c)->rebuild->lpts[i].free = free;
+ FSCK(c)->rebuild->lpts[i].dirty = dirty;
+ c->lst.total_free += free;
+ c->lst.total_dirty += dirty;
+
+ if (free == c->leb_size)
+ c->lst.empty_lebs++;
+
+ if (!(FSCK(c)->rebuild->lpts[i].flags & LPROPS_INDEX)) {
+ int spc;
+
+ spc = free + dirty;
+ if (spc < c->dead_wm)
+ c->lst.total_dead += spc;
+ else
+ c->lst.total_dark += ubifs_calc_dark(c, spc);
+ c->lst.total_used += c->leb_size - spc;
+ } else {
+ c->lst.idx_lebs += 1;
+ }
+ }
+
+ /* Write LPT. */
+ return ubifs_create_lpt(c, FSCK(c)->rebuild->lpts, c->main_lebs, hash_lpt);
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -1302,6 +1375,14 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
* Step 9: Build TNC.
*/
err = traverse_files_and_nodes(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 10. Build LPT. */
+ log_out(c, "Build LPT");
+ err = build_lpt(c);
if (err)
exit_code |= FSCK_ERROR;
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index c9d582da..6f965555 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -338,6 +338,7 @@ enum {
* @flags: LEB properties flags (see above)
* @lnum: LEB number
* @end: the end postition of LEB calculated by the last node
+ * @used: amount of used space in bytes
* @list: list of same-category lprops (for LPROPS_EMPTY and LPROPS_FREEABLE)
* @hpos: heap position in heap of same-category lprops (other categories)
*/
@@ -347,6 +348,7 @@ struct ubifs_lprops {
int flags;
int lnum;
int end;
+ int used;
union {
struct list_head list;
int hpos;
--
2.13.6
This is the 5/18 step of fsck. Recover isize. There could be following
steps and possible errors:
Step 1. Traverse size tree, lookup corresponding inode from TNC
a. corrupted node searched from TNC: skip node for danger mode and
normal mode with 'yes' answer, other modes will exit.
b. corrupted index node read from TNC: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
Step 2. update isize for inode. Keep <inum, isize> in size tree for check
mode, update inode node in place for other modes.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 1 +
ubifs-utils/fsck.ubifs/load_fs.c | 79 +++++++++++++++++++++----------------
ubifs-utils/libubifs/recovery.c | 34 +++++++++++++---
3 files changed, 75 insertions(+), 39 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 9d69a4fd..77013851 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -434,6 +434,7 @@ int main(int argc, char *argv[])
* Step 2: Replay journal
* Step 3: Handle orphan nodes
* Step 4: Consolidate log
+ * Step 5: Recover isize
*/
err = ubifs_load_filesystem(c);
if (err) {
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index 42b1afaa..58540543 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -17,6 +17,33 @@
#include "misc.h"
#include "fsck.ubifs.h"
+enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
+
+static void handle_error(const struct ubifs_info *c, int reason_set)
+{
+ bool handled = false;
+ unsigned int reason = get_failure_reason_callback(c);
+
+ clear_failure_reason_callback(c);
+ if ((reason_set & HAS_DATA_CORRUPTED) && (reason & FR_DATA_CORRUPTED)) {
+ handled = true;
+ reason &= ~FR_DATA_CORRUPTED;
+ if (fix_problem(c, LOG_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ }
+ if ((reason_set & HAS_TNC_CORRUPTED) && (reason & FR_TNC_CORRUPTED)) {
+ ubifs_assert(c, !handled);
+ handled = true;
+ reason &= ~FR_TNC_CORRUPTED;
+ if (fix_problem(c, TNC_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ }
+
+ ubifs_assert(c, reason == 0);
+ if (!handled)
+ exit_code |= FSCK_ERROR;
+}
+
int ubifs_load_filesystem(struct ubifs_info *c)
{
int err;
@@ -164,19 +191,7 @@ int ubifs_load_filesystem(struct ubifs_info *c)
log_out(c, "Replay journal");
err = ubifs_replay_journal(c);
if (err) {
- unsigned int reason = get_failure_reason_callback(c);
-
- clear_failure_reason_callback(c);
- if (reason & FR_DATA_CORRUPTED) {
- if (fix_problem(c, LOG_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- } else if (reason & FR_TNC_CORRUPTED) {
- if (fix_problem(c, TNC_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- } else {
- ubifs_assert(c, reason == 0);
- exit_code |= FSCK_ERROR;
- }
+ handle_error(c, HAS_DATA_CORRUPTED | HAS_TNC_CORRUPTED);
goto out_journal;
}
@@ -186,16 +201,7 @@ int ubifs_load_filesystem(struct ubifs_info *c)
log_out(c, "Handle orphan nodes");
err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
if (err) {
- unsigned int reason = get_failure_reason_callback(c);
-
- clear_failure_reason_callback(c);
- if (reason & FR_TNC_CORRUPTED) {
- if (fix_problem(c, TNC_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- } else {
- ubifs_assert(c, reason == 0);
- exit_code |= FSCK_ERROR;
- }
+ handle_error(c, HAS_TNC_CORRUPTED);
goto out_orphans;
}
@@ -210,19 +216,26 @@ int ubifs_load_filesystem(struct ubifs_info *c)
log_out(c, "Consolidate log");
err = ubifs_consolidate_log(c);
if (err) {
- unsigned int reason = get_failure_reason_callback(c);
-
- clear_failure_reason_callback(c);
- if (reason & FR_DATA_CORRUPTED) {
- if (fix_problem(c, LOG_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- } else {
- ubifs_assert(c, reason == 0);
- exit_code |= FSCK_ERROR;
- }
+ handle_error(c, HAS_DATA_CORRUPTED);
+ goto out_orphans;
+ }
+ }
+
+ if (c->need_recovery) {
+ log_out(c, "Recover isize");
+ err = ubifs_recover_size(c, true);
+ if (err) {
+ handle_error(c, HAS_TNC_CORRUPTED);
goto out_orphans;
}
}
+ } else if (c->need_recovery) {
+ log_out(c, "Recover isize");
+ err = ubifs_recover_size(c, false);
+ if (err) {
+ handle_error(c, HAS_TNC_CORRUPTED);
+ goto out_orphans;
+ }
}
c->mounting = 0;
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
index 9115b17a..a5133a0f 100644
--- a/ubifs-utils/libubifs/recovery.c
+++ b/ubifs-utils/libubifs/recovery.c
@@ -1272,8 +1272,18 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
/* Locate the inode node LEB number and offset */
ino_key_init(c, &key, e->inum);
err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs);
- if (err)
+ if (err) {
+ unsigned int reason = get_failure_reason_callback(c);
+
+ if (reason & FR_DATA_CORRUPTED) {
+ test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED);
+ if (handle_failure_callback(c, FR_H_TNC_DATA_CORRUPTED, NULL)) {
+ /* Leave the inode to be deleted by subsequent steps */
+ return 0;
+ }
+ }
goto out;
+ }
/*
* If the size recorded on the inode node is greater than the size that
* was calculated from nodes in the journal then don't change the inode.
@@ -1320,10 +1330,10 @@ out:
*/
static int inode_fix_size(struct ubifs_info *c, __unused struct size_entry *e)
{
- ubifs_assert(c, 0);
-
- // To be implemented
- return -EINVAL;
+ /* Don't remove entry, keep it in the size tree. */
+ /* Remove this assertion after supporting authentication. */
+ ubifs_assert(c, c->ro_mount);
+ return 0;
}
/**
@@ -1353,8 +1363,19 @@ int ubifs_recover_size(struct ubifs_info *c, bool in_place)
ino_key_init(c, &key, e->inum);
err = ubifs_tnc_lookup(c, &key, c->sbuf);
- if (err && err != -ENOENT)
+ if (err && err != -ENOENT) {
+ unsigned int reason;
+
+ reason = get_failure_reason_callback(c);
+ if (reason & FR_DATA_CORRUPTED) {
+ test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED);
+ if (handle_failure_callback(c, FR_H_TNC_DATA_CORRUPTED, NULL)) {
+ /* Leave the inode to be deleted by subsequent steps */
+ goto delete_entry;
+ }
+ }
return err;
+ }
if (err == -ENOENT) {
/* Remove data nodes that have no inode */
dbg_rcvry("removing ino %lu",
@@ -1390,6 +1411,7 @@ int ubifs_recover_size(struct ubifs_info *c, bool in_place)
}
}
+delete_entry:
rb_erase(&e->rb, &c->size_tree);
kfree(e);
}
--
2.13.6
This is the 13/18 step of fsck. Commit problem fixing modifications
(which are generated from the previous steps) to disk.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 436831cb..66b9c578 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -432,6 +432,33 @@ void handle_error(const struct ubifs_info *c, int reason_set)
exit_code |= FSCK_ERROR;
}
+static int commit_fix_modifications(struct ubifs_info *c)
+{
+ int err;
+
+ if (exit_code & FSCK_NONDESTRUCT) {
+ log_out(c, "Commit problem fixing modifications");
+
+ /*
+ * Force UBIFS to do commit by setting @c->mounting if changes
+ * happen on disk. Committing is required once before allocating
+ * new space(subsequent steps may need), because building lpt
+ * could mark LEB(which holds stale data nodes) as unused, if
+ * the LEB is overwritten by new data, old data won't be found
+ * in next fsck run(assume that first fsck run is interrupted by
+ * the powercut), which could affect the correctness of LEB
+ * properties after replaying journal in the second fsck run.
+ */
+ c->mounting = 1;
+ }
+ err = ubifs_run_commit(c);
+
+ if (exit_code & FSCK_NONDESTRUCT)
+ c->mounting = 0;
+
+ return err;
+}
+
/*
* do_fsck - Check & repair the filesystem.
*/
@@ -479,9 +506,16 @@ static int do_fsck(void)
err = check_and_correct_space(c);
kfree(FSCK(c)->used_lebs);
destroy_file_tree(c, &FSCK(c)->scanned_files);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
+
+ err = commit_fix_modifications(c);
if (err)
exit_code |= FSCK_ERROR;
+free_disconnected_files_2:
destroy_file_list(c, &FSCK(c)->disconnected_files);
return err;
@@ -532,6 +566,7 @@ int main(int argc, char *argv[])
* Step 10: Check and correct files
* Step 11: Check whether the TNC is empty
* Step 12: Check and correct the space statistics
+ * Step 13: Commit problem fixing modifications
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
--
2.13.6
This is a preparation for adding TNC checking support. Following data
structures and functions are moved into fsck.ubifs.c:
1. Move 'scanned_files' and 'used_lebs' from rebuild module, make them
resuable for non-rebuild_fs modes.
2. Move function 'handle_error' from load_fs.c, it could be reused in
other steps.
3. Add new function ubifs_tnc_remove_node in libubifs, which could
remove index entry for a node by given position.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 2 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 25 +++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 13 ++++---
ubifs-utils/fsck.ubifs/load_fs.c | 27 --------------
ubifs-utils/fsck.ubifs/rebuild_fs.c | 42 +++++++++++-----------
ubifs-utils/libubifs/tnc.c | 64 ++++++++++++++++++++++++++++++++++
ubifs-utils/libubifs/ubifs.h | 2 ++
7 files changed, 122 insertions(+), 53 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index b8777f6c..c3ab2b76 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -1234,7 +1234,7 @@ int check_and_correct_files(struct ubifs_info *c)
int err;
struct rb_node *node;
struct scanned_file *file;
- struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 77013851..471c2cd9 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -404,6 +404,31 @@ static void destroy_fsck_info(struct ubifs_info *c)
c->dev_name = NULL;
}
+void handle_error(const struct ubifs_info *c, int reason_set)
+{
+ bool handled = false;
+ unsigned int reason = get_failure_reason_callback(c);
+
+ clear_failure_reason_callback(c);
+ if ((reason_set & HAS_DATA_CORRUPTED) && (reason & FR_DATA_CORRUPTED)) {
+ handled = true;
+ reason &= ~FR_DATA_CORRUPTED;
+ if (fix_problem(c, LOG_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ }
+ if ((reason_set & HAS_TNC_CORRUPTED) && (reason & FR_TNC_CORRUPTED)) {
+ ubifs_assert(c, !handled);
+ handled = true;
+ reason &= ~FR_TNC_CORRUPTED;
+ if (fix_problem(c, TNC_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ }
+
+ ubifs_assert(c, reason == 0);
+ if (!handled)
+ exit_code |= FSCK_ERROR;
+}
+
/*
* do_fsck - Check & repair the filesystem.
*/
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index b98c3f7d..109c3924 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -40,6 +40,8 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED };
+enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
+
struct scanned_file;
/**
@@ -181,18 +183,14 @@ struct scanned_file {
/**
* ubifs_rebuild_info - UBIFS rebuilding information.
- * @used_lebs: a bitmap used for recording used lebs
* @lpts: lprops table
- * @scanned_files: tree of all scanned files
* @write_buf: write buffer for LEB @head_lnum
* @head_lnum: current writing LEB number
* @head_offs: current writing position in LEB @head_lnum
* @need_update_lpt: whether to update lpt while writing index nodes
*/
struct ubifs_rebuild_info {
- unsigned long *used_lebs;
struct ubifs_lprops *lpts;
- struct rb_root scanned_files;
void *write_buf;
int head_lnum;
int head_offs;
@@ -205,6 +203,8 @@ struct ubifs_rebuild_info {
* @failure_reason: reasons for failed operations
* @lpt_status: the status of lpt, could be: %0(OK), %FR_LPT_CORRUPTED or
* %FR_LPT_INCORRECT
+ * @scanned_files: tree of all scanned files
+ * @used_lebs: a bitmap used for recording used lebs
* @try_rebuild: %true means that try to rebuild fs when fsck failed
* @rebuild: rebuilding-related information
*/
@@ -212,6 +212,8 @@ struct ubifs_fsck_info {
int mode;
unsigned int failure_reason;
unsigned int lpt_status;
+ struct rb_root scanned_files;
+ unsigned long *used_lebs;
bool try_rebuild;
struct ubifs_rebuild_info *rebuild;
};
@@ -260,6 +262,9 @@ static inline const char *mode_name(const struct ubifs_info *c)
/* Exit code for fsck program. */
extern int exit_code;
+/* fsck.ubifs.c */
+void handle_error(const struct ubifs_info *c, int reason_set);
+
/* problem.c */
bool fix_problem(const struct ubifs_info *c, int problem_type, const void *priv);
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index 58540543..04208a14 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -17,33 +17,6 @@
#include "misc.h"
#include "fsck.ubifs.h"
-enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
-
-static void handle_error(const struct ubifs_info *c, int reason_set)
-{
- bool handled = false;
- unsigned int reason = get_failure_reason_callback(c);
-
- clear_failure_reason_callback(c);
- if ((reason_set & HAS_DATA_CORRUPTED) && (reason & FR_DATA_CORRUPTED)) {
- handled = true;
- reason &= ~FR_DATA_CORRUPTED;
- if (fix_problem(c, LOG_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- }
- if ((reason_set & HAS_TNC_CORRUPTED) && (reason & FR_TNC_CORRUPTED)) {
- ubifs_assert(c, !handled);
- handled = true;
- reason &= ~FR_TNC_CORRUPTED;
- if (fix_problem(c, TNC_CORRUPTED, NULL))
- FSCK(c)->try_rebuild = true;
- }
-
- ubifs_assert(c, reason == 0);
- if (!handled)
- exit_code |= FSCK_ERROR;
-}
-
int ubifs_load_filesystem(struct ubifs_info *c)
{
int err;
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 382687b3..f190517c 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -73,10 +73,10 @@ static int init_rebuild_info(struct ubifs_info *c)
log_err(c, errno, "can not allocate rebuild info");
goto free_sbuf;
}
- FSCK(c)->rebuild->scanned_files = RB_ROOT;
- FSCK(c)->rebuild->used_lebs = kcalloc(BITS_TO_LONGS(c->main_lebs),
- sizeof(unsigned long), GFP_KERNEL);
- if (!FSCK(c)->rebuild->used_lebs) {
+ FSCK(c)->scanned_files = RB_ROOT;
+ FSCK(c)->used_lebs = kcalloc(BITS_TO_LONGS(c->main_lebs),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!FSCK(c)->used_lebs) {
err = -ENOMEM;
log_err(c, errno, "can not allocate bitmap of used lebs");
goto free_rebuild;
@@ -100,7 +100,7 @@ static int init_rebuild_info(struct ubifs_info *c)
free_lpts:
kfree(FSCK(c)->rebuild->lpts);
free_used_lebs:
- kfree(FSCK(c)->rebuild->used_lebs);
+ kfree(FSCK(c)->used_lebs);
free_rebuild:
kfree(FSCK(c)->rebuild);
free_sbuf:
@@ -112,7 +112,7 @@ static void destroy_rebuild_info(struct ubifs_info *c)
{
vfree(FSCK(c)->rebuild->write_buf);
kfree(FSCK(c)->rebuild->lpts);
- kfree(FSCK(c)->rebuild->used_lebs);
+ kfree(FSCK(c)->used_lebs);
kfree(FSCK(c)->rebuild);
vfree(c->sbuf);
}
@@ -313,7 +313,7 @@ static int process_scanned_node(struct ubifs_info *c, int lnum,
return 1;
}
- tree = &FSCK(c)->rebuild->scanned_files;
+ tree = &FSCK(c)->scanned_files;
return insert_or_update_file(c, tree, sn, key_type(c, key), inum);
}
@@ -331,7 +331,7 @@ static void destroy_scanned_info(struct ubifs_info *c, struct scanned_info *si)
struct scanned_dent_node *dent_node;
struct rb_node *this;
- destroy_file_tree(c, &FSCK(c)->rebuild->scanned_files);
+ destroy_file_tree(c, &FSCK(c)->scanned_files);
this = rb_first(&si->valid_inos);
while (this) {
@@ -377,7 +377,7 @@ static void destroy_scanned_info(struct ubifs_info *c, struct scanned_info *si)
*
* This function scans nodes from flash, all ino/dent nodes are split
* into valid tree and deleted tree, all trun/data nodes are collected
- * into file, the file is inserted into @FSCK(c)->rebuild->scanned_files.
+ * into file, the file is inserted into @FSCK(c)->scanned_files.
*/
static int scan_nodes(struct ubifs_info *c, struct scanned_info *si)
{
@@ -495,7 +495,7 @@ static void update_lpt(struct ubifs_info *c, struct scanned_node *sn,
int index = sn->lnum - c->main_first;
int pos = sn->offs + ALIGN(sn->len, 8);
- set_bit(index, FSCK(c)->rebuild->used_lebs);
+ set_bit(index, FSCK(c)->used_lebs);
FSCK(c)->rebuild->lpts[index].end = max_t(int,
FSCK(c)->rebuild->lpts[index].end, pos);
@@ -572,7 +572,7 @@ static int add_valid_nodes_into_file(struct ubifs_info *c,
struct scanned_ino_node *ino_node;
struct scanned_dent_node *dent_node;
struct rb_node *this;
- struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
this = rb_first(&si->valid_inos);
while (this) {
@@ -621,7 +621,7 @@ static void filter_invalid_files(struct ubifs_info *c)
{
struct rb_node *node;
struct scanned_file *file;
- struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
LIST_HEAD(tmp_list);
/* Add all xattr files into a list. */
@@ -678,7 +678,7 @@ static void extract_dentry_tree(struct ubifs_info *c)
{
struct rb_node *node;
struct scanned_file *file;
- struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
LIST_HEAD(unreachable);
for (node = rb_first(tree); node; node = rb_next(node)) {
@@ -731,7 +731,7 @@ static void init_root_ino(struct ubifs_info *c, struct ubifs_ino_node *ino)
}
/**
- * get_free_leb - get a free LEB according to @FSCK(c)->rebuild->used_lebs.
+ * get_free_leb - get a free LEB according to @FSCK(c)->used_lebs.
* @c: UBIFS file-system description object
*
* This function tries to find a free LEB, lnum is returned if found, otherwise
@@ -741,12 +741,12 @@ static int get_free_leb(struct ubifs_info *c)
{
int lnum;
- lnum = find_next_zero_bit(FSCK(c)->rebuild->used_lebs, c->main_lebs, 0);
+ lnum = find_next_zero_bit(FSCK(c)->used_lebs, c->main_lebs, 0);
if (lnum >= c->main_lebs) {
ubifs_err(c, "No space left.");
return -ENOSPC;
}
- set_bit(lnum, FSCK(c)->rebuild->used_lebs);
+ set_bit(lnum, FSCK(c)->used_lebs);
lnum += c->main_first;
return lnum;
@@ -897,8 +897,8 @@ static int create_root(struct ubifs_info *c)
file->calc_xnms = file->ino.xnms = le32_to_cpu(ino->xattr_names);
file->calc_size = file->ino.size = le64_to_cpu(ino->size);
- rb_link_node(&file->rb, NULL, &FSCK(c)->rebuild->scanned_files.rb_node);
- rb_insert_color(&file->rb, &FSCK(c)->rebuild->scanned_files);
+ rb_link_node(&file->rb, NULL, &FSCK(c)->scanned_files.rb_node);
+ rb_insert_color(&file->rb, &FSCK(c)->scanned_files);
out:
kfree(ino);
@@ -1188,7 +1188,7 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
int i, err = 0, idx_cnt = 0;
struct rb_node *node;
struct scanned_file *file;
- struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
struct idx_entry *ie, *tmp_ie;
LIST_HEAD(idx_list);
@@ -1214,7 +1214,7 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
for (i = 0; i < c->main_lebs; ++i) {
int lnum, len, end;
- if (!test_bit(i, FSCK(c)->rebuild->used_lebs))
+ if (!test_bit(i, FSCK(c)->used_lebs))
continue;
lnum = i + c->main_first;
@@ -1268,7 +1268,7 @@ static int build_lpt(struct ubifs_info *c)
/* Update LPT. */
for (i = 0; i < c->main_lebs; i++) {
- if (!test_bit(i, FSCK(c)->rebuild->used_lebs) ||
+ if (!test_bit(i, FSCK(c)->used_lebs) ||
c->gc_lnum == i + c->main_first) {
free = c->leb_size;
dirty = 0;
diff --git a/ubifs-utils/libubifs/tnc.c b/ubifs-utils/libubifs/tnc.c
index cd1013d5..92770623 100644
--- a/ubifs-utils/libubifs/tnc.c
+++ b/ubifs-utils/libubifs/tnc.c
@@ -2457,6 +2457,70 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
}
/**
+ * ubifs_tnc_remove_node - remove an index entry of a node by given position.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ * @lnum: LEB number of node
+ * @offs: node offset
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove_node(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "pos %d:%d, key ", lnum, offs);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+ if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ if (zbr->lnum == lnum && zbr->offs == offs) {
+ err = tnc_delete(c, znode, n);
+ } else if (is_hash_key(c, key)) {
+ found = resolve_collision_directly(c, key, &znode, &n,
+ lnum, offs);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found) {
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+ err = tnc_delete(c, znode, n);
+ } else {
+ goto not_found;
+ }
+ } else {
+ goto not_found;
+ }
+ } else {
+not_found:
+ /* Impossible, the node has been found before being deleted. */
+ ubifs_assert(c, 0);
+ }
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
* ubifs_tnc_next_ent - walk directory or extended attribute entries.
* @c: UBIFS file-system description object
* @key: key of last entry
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 8a506a8b..03150cdb 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1661,6 +1661,8 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
union ubifs_key *to_key);
int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
+int ubifs_tnc_remove_node(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs);
struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
union ubifs_key *key,
const struct fscrypt_name *nm);
--
2.13.6
This is the 11/18 step of fsck. Check whether the TNC is empty, turn to
rebuild_fs if it is not found. Can we recreate a new root dir to avoid
empty TNC? The answer is no, lpt fixing should be done before creating
new entry, but lpt fixing needs a committing before new dirty data
generated to ensure that bud data won't be overwritten(bud LEB could
become freeable after replaying journal, corrected lpt may treat it as
a free one to hold new data, see details in space checking & correcting
step). Then we have to create the new root dir after fixing lpt and a
committing, znode without childs(empty TNC) maybe written on disk at the
moment of committing, which corrupts the UBIFS image. So we choose to
rebuild the filesystem if the TNC is empty, this case is equivalent to
corrupted TNC.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 25 +++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 7 +++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 4 +++-
ubifs-utils/fsck.ubifs/problem.c | 1 +
4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index 2be96193..4190bf91 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -499,3 +499,28 @@ int handle_dentry_tree(struct ubifs_info *c)
return 0;
}
+
+/**
+ * tnc_is_empty - Check whether the TNC is empty.
+ * @c: UBIFS file-system description object
+ *
+ * Returns %true if the TNC is empty, otherwise %false is returned.
+ */
+bool tnc_is_empty(struct ubifs_info *c)
+{
+ /*
+ * Check whether the TNC is empty, turn to rebuild_fs if it is empty.
+ * Can we recreate a new root dir to avoid empty TNC? The answer is no,
+ * lpt fixing should be done before creating new entry, but lpt fixing
+ * needs a committing before new dirty data generated to ensure that
+ * bud data won't be overwritten(bud LEB could become freeable after
+ * replaying journal, corrected lpt may treat it as a free one to hold
+ * new data, see details in space checking & correcting step). Then we
+ * have to create the new root dir after fixing lpt and a committing,
+ * znode without childs(empty TNC) maybe written on disk at the moment
+ * of committing, which corrupts the UBIFS image. So we choose to
+ * rebuild the filesystem if the TNC is empty, this case is equivalent
+ * to corrupted TNC.
+ */
+ return c->zroot.znode->child_cnt == 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 85a5baf0..14f77fc2 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -467,6 +467,12 @@ static int do_fsck(void)
goto free_disconnected_files;
}
+ log_out(c, "Check whether the TNC is empty");
+ if (tnc_is_empty(c) && fix_problem(c, EMPTY_TNC, NULL)) {
+ err = -EINVAL;
+ FSCK(c)->try_rebuild = true;
+ }
+
free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
@@ -512,6 +518,7 @@ int main(int argc, char *argv[])
* Step 8: Check and handle invalid files
* Step 9: Check and handle unreachable files
* Step 10: Check and correct files
+ * Step 11: Check whether the TNC is empty
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index d1423c01..663d5dcf 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -43,7 +43,8 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_HAS_0_NLINK_INODE, FILE_HAS_INCONSIST_TYPE, FILE_HAS_TOO_MANY_DENT,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
- FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT };
+ FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
+ EMPTY_TNC };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -319,5 +320,6 @@ int traverse_tnc_and_construct_files(struct ubifs_info *c);
void update_files_size(struct ubifs_info *c);
int handle_invalid_files(struct ubifs_info *c);
int handle_dentry_tree(struct ubifs_info *c);
+bool tnc_is_empty(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index e8f08606..795f05fa 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -60,6 +60,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"}, // FILE_IS_INCONSISTENT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "TNC is empty"}, // EMPTY_TNC
};
static const char *get_question(const struct fsck_problem *problem,
--
2.13.6
From: Xiang Yang <[email protected]>
This is the 10/18 step of fsck. Check and handle inconsistent files, the
checking rule is same as rebuild mode which has been implemented in
check_and_correct_files, but the methods of handling are different:
1. Correct the file information for safe mode, danger mode and normal
mode with 'yes' answer, other modes will exit.
Signed-off-by: Xiang Yang <[email protected]>
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 76 ++++++++++++++++++++++------------
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 8 ++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +-
ubifs-utils/fsck.ubifs/problem.c | 16 +++++++
4 files changed, 74 insertions(+), 28 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index b24445be..c83d3774 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -1292,8 +1292,8 @@ reachable:
* data nodes and truncation node. The calculated informaion will be used
* to correct inode node.
*/
-static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
- struct rb_root *file_tree)
+static int calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree)
{
int nlink = 0;
bool corrupted_truncation = false;
@@ -1306,15 +1306,24 @@ static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
xattr_file = rb_entry(node, struct scanned_file, rb);
+ dent_node = rb_entry(rb_first(&xattr_file->dent_nodes),
+ struct scanned_dent_node, rb);
+ ubifs_assert(c, xattr_file->ino.is_xattr);
ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
- calculate_file_info(c, xattr_file, file_tree);
+ xattr_file->calc_nlink = 1;
+ xattr_file->calc_size = xattr_file->ino.size;
+
+ file->calc_xcnt += 1;
+ file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
+ file->calc_xsz += CALC_XATTR_BYTES(xattr_file->ino.size);
+ file->calc_xnms += dent_node->nlen;
}
if (file->inum == UBIFS_ROOT_INO) {
file->calc_nlink += 2;
file->calc_size += UBIFS_INO_NODE_SZ;
- return;
+ return 0;
}
if (S_ISDIR(file->ino.mode)) {
@@ -1326,29 +1335,11 @@ static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
if (!parent_file) {
ubifs_assert(c, 0);
- return;
+ return 0;
}
parent_file->calc_nlink += 1;
parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
- return;
- }
-
- if (file->ino.is_xattr) {
- file->calc_nlink = 1;
- file->calc_size = file->ino.size;
-
- dent_node = rb_entry(rb_first(&file->dent_nodes),
- struct scanned_dent_node, rb);
- parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
- if (!parent_file) {
- ubifs_assert(c, 0);
- return;
- }
- parent_file->calc_xcnt += 1;
- parent_file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
- parent_file->calc_xsz += CALC_XATTR_BYTES(file->ino.size);
- parent_file->calc_xnms += dent_node->nlen;
- return;
+ return 0;
}
for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
@@ -1359,7 +1350,7 @@ static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
if (!parent_file) {
ubifs_assert(c, 0);
- return;
+ return 0;
}
parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
}
@@ -1368,7 +1359,7 @@ static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
if (!S_ISREG(file->ino.mode)) {
/* No need to verify i_size for symlink/sock/block/char/fifo. */
file->calc_size = file->ino.size;
- return;
+ return 0;
}
/*
@@ -1452,10 +1443,22 @@ drop_data:
data_node = list_entry(drop_list.next, struct scanned_data_node,
list);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ /*
+ * Don't ask, inconsistent file correcting will be
+ * asked in function correct_file_info().
+ */
+ int err = delete_node(c, &data_node->key,
+ data_node->header.lnum, data_node->header.offs);
+ if (err)
+ return err;
+ }
list_del(&data_node->list);
rb_erase(&data_node->rb, &file->data_nodes);
kfree(data_node);
}
+
+ return 0;
}
/**
@@ -1490,6 +1493,7 @@ static int correct_file_info(struct ubifs_info *c, struct scanned_file *file)
file->calc_size == file->ino.size)
return 0;
+ handle_invalid_file(c, FILE_IS_INCONSISTENT, file, NULL);
lnum = file->ino.header.lnum;
dbg_fsck("correct file(inum:%lu type:%s), nlink %u->%u, xattr cnt %u->%u, xattr size %u->%u, xattr names %u->%u, size %llu->%llu, at %d:%d, in %s",
file->inum, file->ino.is_xattr ? "xattr" :
@@ -1537,7 +1541,9 @@ int check_and_correct_files(struct ubifs_info *c)
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
- calculate_file_info(c, file, tree);
+ err = calculate_file_info(c, file, tree);
+ if (err)
+ return err;
}
for (node = rb_first(tree); node; node = rb_next(node)) {
@@ -1548,5 +1554,21 @@ int check_and_correct_files(struct ubifs_info *c)
return err;
}
+ if (list_empty(&FSCK(c)->disconnected_files))
+ return 0;
+
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+ list_for_each_entry(file, &FSCK(c)->disconnected_files, list) {
+ err = calculate_file_info(c, file, tree);
+ if (err)
+ return err;
+
+ /* Reset disconnected file's nlink as one. */
+ file->calc_nlink = 1;
+ err = correct_file_info(c, file);
+ if (err)
+ return err;
+ }
+
return 0;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index fd4890a0..85a5baf0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -460,6 +460,13 @@ static int do_fsck(void)
goto free_disconnected_files;
}
+ log_out(c, "Check and correct files");
+ err = check_and_correct_files(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files;
+ }
+
free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
@@ -504,6 +511,7 @@ int main(int argc, char *argv[])
* Step 7: Update files' size
* Step 8: Check and handle invalid files
* Step 9: Check and handle unreachable files
+ * Step 10: Check and correct files
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index ba4771a3..d1423c01 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -43,7 +43,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_HAS_0_NLINK_INODE, FILE_HAS_INCONSIST_TYPE, FILE_HAS_TOO_MANY_DENT,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
- FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE };
+ FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 0395a34f..e8f08606 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -59,6 +59,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"}, // FILE_IS_DISCONNECTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"}, // FILE_IS_INCONSISTENT
};
static const char *get_question(const struct fsck_problem *problem,
@@ -212,6 +213,21 @@ static void print_problem(const struct ubifs_info *c,
key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
break;
}
+ case FILE_IS_INCONSISTENT:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_file *file = ifp->file;
+
+ log_out(c, "problem: %s, ino %lu type %s, nlink %u xcnt %u xsz %u xnms %u size %llu, "
+ "should be nlink %u xcnt %u xsz %u xnms %u size %llu",
+ problem->desc, file->inum,
+ file->ino.is_xattr ? "xattr" : ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
+ file->ino.nlink, file->ino.xcnt, file->ino.xsz,
+ file->ino.xnms, file->ino.size,
+ file->calc_nlink, file->calc_xcnt, file->calc_xsz,
+ file->calc_xnms, file->calc_size);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6
This is the 14/18 step of fsck. Check and correct the index size by
traversing TNC just like dbg_check_idx_size does. This step should
be executed after first committing, because 'c->calc_idx_sz' can be
changed in 'ubifs_tnc_start_commit' and the initial value of
'c->calc_idx_sz' read from disk is untrusted.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_space.c | 28 ++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 8 ++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 ++-
ubifs-utils/fsck.ubifs/problem.c | 9 +++++++++
4 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/check_space.c b/ubifs-utils/fsck.ubifs/check_space.c
index cf4b8d3f..a22e5ae1 100644
--- a/ubifs-utils/fsck.ubifs/check_space.c
+++ b/ubifs-utils/fsck.ubifs/check_space.c
@@ -656,3 +656,31 @@ rebuild:
return err;
}
+
+/**
+ * check_and_correct_index_size - check & correct the index size.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks and corrects the index size by traversing TNC: Returns
+ * zero in case of success, a negative error code in case of failure.
+ */
+int check_and_correct_index_size(struct ubifs_info *c)
+{
+ int err;
+ unsigned long long index_size = 0;
+
+ ubifs_assert(c, c->bi.old_idx_sz == c->calc_idx_sz);
+ err = dbg_walk_index(c, NULL, add_size, &index_size);
+ if (err) {
+ /* All TNC nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ return err;
+ }
+
+ dbg_fsck("total index size %llu, in %s", index_size, c->dev_name);
+ if (index_size != c->calc_idx_sz &&
+ fix_problem(c, INCORRECT_IDX_SZ, &index_size))
+ c->bi.old_idx_sz = c->calc_idx_sz = index_size;
+
+ return 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 66b9c578..d3525e30 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -512,6 +512,13 @@ static int do_fsck(void)
}
err = commit_fix_modifications(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
+
+ log_out(c, "Check and correct the index size");
+ err = check_and_correct_index_size(c);
if (err)
exit_code |= FSCK_ERROR;
@@ -567,6 +574,7 @@ int main(int argc, char *argv[])
* Step 11: Check whether the TNC is empty
* Step 12: Check and correct the space statistics
* Step 13: Commit problem fixing modifications
+ * Step 14: Check and correct the index size
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index cc8b71f5..2d17b205 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -45,7 +45,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
- LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT };
+ LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT, INCORRECT_IDX_SZ };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -380,5 +380,6 @@ int get_free_leb(struct ubifs_info *c);
int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
bool free_ltab);
int check_and_correct_space(struct ubifs_info *c);
+int check_and_correct_index_size(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index f987e480..32182c91 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -67,6 +67,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for LEB"}, // LP_INCORRECT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"}, // SPACE_STAT_INCORRECT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect index size"}, // INCORRECT_IDX_SZ
};
static const char *get_question(const struct fsck_problem *problem,
@@ -280,6 +281,14 @@ static void print_problem(const struct ubifs_info *c,
ssp->calc_lst->total_dead, ssp->calc_lst->total_dark);
break;
}
+ case INCORRECT_IDX_SZ:
+ {
+ const unsigned long long *calc_sz = (const unsigned long long *)priv;
+
+ log_out(c, "problem: %s, index size is %llu, should be %llu",
+ problem->desc, c->calc_idx_sz, *calc_sz);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6
This is the 8/18 step of fsck. Check and handle invalid files, the
checking rule is same as rebuild mode which has been implemented in
file_is_valid, but the methods of handling are different:
1. Move unattached(file has no dentries) regular file into disconnected
list, let subsequent steps to handle them with lost+found.
2. Make file type be consistent between inode, detries and data nodes by
deleting dentries or data blocks.
3. Delete file for other invalid cases(eg. file has no inode).
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 88 ++++++++++++
ubifs-utils/fsck.ubifs/extract_files.c | 239 +++++++++++++++++++++++++++++----
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 11 ++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 25 +++-
ubifs-utils/fsck.ubifs/problem.c | 100 ++++++++++++++
ubifs-utils/fsck.ubifs/rebuild_fs.c | 4 +-
6 files changed, 434 insertions(+), 33 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index 0fd6b32a..c5c606e1 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -354,3 +354,91 @@ void update_files_size(struct ubifs_info *c)
kfree(e);
}
}
+
+/**
+ * handle_invalid_files - Handle invalid files.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks and handles invalid files, there are three situations:
+ * 1. Move unattached(file has no dentries, or file's parent file has invalid
+ * type) regular file into disconnected list, let subsequent steps to handle
+ * them with lost+found.
+ * 2. Make file type be consistent between inode, detries and data nodes by
+ * deleting dentries or data blocks.
+ * 3. Delete file for other invalid cases(eg. file has no inode).
+ *
+ * Returns zero in case of success, a negative error code in case of failure.
+ */
+int handle_invalid_files(struct ubifs_info *c)
+{
+ int err;
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
+ LIST_HEAD(tmp_list);
+
+ /* Add all xattr files into a list. */
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ if (file->ino.is_xattr)
+ list_add(&file->list, &tmp_list);
+ }
+
+ /*
+ * Round 1: Traverse xattr files, check whether the xattr file is
+ * valid, move valid xattr file into corresponding host file's subtree.
+ */
+ while (!list_empty(&tmp_list)) {
+ file = list_entry(tmp_list.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ rb_erase(&file->rb, tree);
+ err = file_is_valid(c, file, tree, NULL);
+ if (err < 0) {
+ destroy_file_content(c, file);
+ kfree(file);
+ return err;
+ } else if (!err) {
+ err = delete_file(c, file);
+ kfree(file);
+ if (err)
+ return err;
+ }
+ }
+
+ /* Round 2: Traverse non-xattr files. */
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ int is_diconnected = 0;
+
+ file = rb_entry(node, struct scanned_file, rb);
+ err = file_is_valid(c, file, tree, &is_diconnected);
+ if (err < 0) {
+ return err;
+ } else if (!err) {
+ if (is_diconnected)
+ list_add(&file->list, &FSCK(c)->disconnected_files);
+ else
+ list_add(&file->list, &tmp_list);
+ }
+ }
+
+ /* Delete & remove invalid files. */
+ while (!list_empty(&tmp_list)) {
+ file = list_entry(tmp_list.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ err = delete_file(c, file);
+ if (err)
+ return err;
+ rb_erase(&file->rb, tree);
+ kfree(file);
+ }
+
+ /* Remove disconnected file from the file tree. */
+ list_for_each_entry(file, &FSCK(c)->disconnected_files, list) {
+ rb_erase(&file->rb, tree);
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index e9c71a3c..51b83b82 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -784,6 +784,26 @@ void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
}
/**
+ * destroy_file_list - destroy files from a given list head.
+ * @c: UBIFS file-system description object
+ * @file_list: list of the scanned files
+ *
+ * Destroy scanned files from a given list.
+ */
+void destroy_file_list(struct ubifs_info *c, struct list_head *file_list)
+{
+ struct scanned_file *file;
+
+ while (!list_empty(file_list)) {
+ file = list_entry(file_list->next, struct scanned_file, list);
+
+ destroy_file_content(c, file);
+ list_del(&file->list);
+ kfree(file);
+ }
+}
+
+/**
* lookup_file - lookup file according to inode number.
* @file_tree: tree of all scanned files
* @inum: inode number
@@ -810,6 +830,109 @@ struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum)
return NULL;
}
+static void handle_invalid_file(struct ubifs_info *c, int problem_type,
+ struct scanned_file *file, void *priv)
+{
+ struct invalid_file_problem ifp = {
+ .file = file,
+ .priv = priv,
+ };
+
+ if (FSCK(c)->mode == REBUILD_MODE)
+ return;
+
+ fix_problem(c, problem_type, &ifp);
+}
+
+static int delete_node(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs)
+{
+ int err;
+
+ err = ubifs_tnc_remove_node(c, key, lnum, offs);
+ if (err) {
+ /* TNC traversing is finished, any TNC path is accessible */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ }
+
+ return err;
+}
+
+static int delete_dent_nodes(struct ubifs_info *c, struct scanned_file *file,
+ int err)
+{
+ int ret = 0;
+ struct rb_node *this = rb_first(&file->dent_nodes);
+ struct scanned_dent_node *dent_node;
+
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ if (!err) {
+ err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ rb_erase(&dent_node->rb, &file->dent_nodes);
+ kfree(dent_node);
+ }
+
+ return ret;
+}
+
+int delete_file(struct ubifs_info *c, struct scanned_file *file)
+{
+ int err = 0, ret = 0;
+ struct rb_node *this;
+ struct scanned_file *xattr_file;
+ struct scanned_data_node *data_node;
+
+ if (file->ino.header.exist) {
+ err = delete_node(c, &file->ino.key, file->ino.header.lnum,
+ file->ino.header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ this = rb_first(&file->data_nodes);
+ while (this) {
+ data_node = rb_entry(this, struct scanned_data_node, rb);
+ this = rb_next(this);
+
+ if (!err) {
+ err = delete_node(c, &data_node->key,
+ data_node->header.lnum, data_node->header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ rb_erase(&data_node->rb, &file->data_nodes);
+ kfree(data_node);
+ }
+
+ err = delete_dent_nodes(c, file, err);
+ if (err)
+ ret = ret ? : err;
+
+ this = rb_first(&file->xattr_files);
+ while (this) {
+ xattr_file = rb_entry(this, struct scanned_file, rb);
+ this = rb_next(this);
+
+ ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
+ err = delete_file(c, xattr_file);
+ if (err)
+ ret = ret ? ret : err;
+ rb_erase(&xattr_file->rb, &file->xattr_files);
+ kfree(xattr_file);
+ }
+
+ return ret;
+}
+
/**
* insert_xattr_file - insert xattr file into file's subtree.
* @c: UBIFS file-system description object
@@ -848,6 +971,7 @@ static void insert_xattr_file(struct ubifs_info *c,
* @c: UBIFS file-system description object
* @file: file object
* @file_tree: tree of all scanned files
+ * @is_diconnected: reason of invalid file, whether the @file is disconnected
*
* This function checks whether given @file is valid, following checks will
* be performed:
@@ -866,12 +990,13 @@ static void insert_xattr_file(struct ubifs_info *c,
* invalid.
* Xattr file will be inserted into corresponding host file's subtree.
*
- * Returns %true is @file is valid, otherwise %false is returned.
+ * Returns %1 is @file is valid, %0 if @file is invalid, otherwise a negative
+ * error code in case of failure.
* Notice: All xattr files should be traversed before non-xattr files, because
* checking item 7 depends on it.
*/
-bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
- struct rb_root *file_tree)
+int file_is_valid(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree, int *is_diconnected)
{
int type;
struct rb_node *node;
@@ -880,8 +1005,17 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
struct scanned_data_node *data_node;
LIST_HEAD(drop_list);
- if (!file->ino.header.exist || !file->ino.nlink)
- return false;
+ dbg_fsck("check validation of file %lu, in %s", file->inum, c->dev_name);
+
+ if (!file->ino.header.exist) {
+ handle_invalid_file(c, FILE_HAS_NO_INODE, file, NULL);
+ return 0;
+ }
+
+ if (!file->ino.nlink) {
+ handle_invalid_file(c, FILE_HAS_0_NLINK_INODE, file, NULL);
+ return 0;
+ }
type = ubifs_get_dent_type(file->ino.mode);
@@ -901,6 +1035,14 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
dent_node = list_entry(drop_list.next, struct scanned_dent_node,
list);
+ handle_invalid_file(c, FILE_HAS_INCONSIST_TYPE, file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ return err;
+ }
+
list_del(&dent_node->list);
rb_erase(&dent_node->rb, &file->dent_nodes);
kfree(dent_node);
@@ -909,11 +1051,7 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
if (type != UBIFS_ITYPE_DIR && !file->ino.is_xattr)
goto check_data_nodes;
- /*
- * Make sure that directory/xattr type files only have one dentry.
- * This work should be done in step 3, but file type could be unknown
- * for lacking inode information at that time, so do it here.
- */
+ /* Make sure that directory/xattr type files only have one dentry. */
node = rb_first(&file->dent_nodes);
while (node) {
dent_node = rb_entry(node, struct scanned_dent_node, rb);
@@ -921,6 +1059,14 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
if (!node)
break;
+ handle_invalid_file(c, FILE_HAS_TOO_MANY_DENT, file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ return err;
+ }
+
rb_erase(&dent_node->rb, &file->dent_nodes);
kfree(dent_node);
}
@@ -929,43 +1075,65 @@ check_data_nodes:
if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr)
goto check_dent_node;
- /*
- * Make sure that non regular type files not have data/trun nodes.
- * This work should be done in step 3, but file type could be unknown
- * for lacking inode information at that time, so do it here.
- */
+ /* Make sure that non regular type files not have data/trun nodes. */
file->trun.header.exist = 0;
node = rb_first(&file->data_nodes);
while (node) {
data_node = rb_entry(node, struct scanned_data_node, rb);
node = rb_next(node);
+ handle_invalid_file(c, FILE_SHOULDNT_HAVE_DATA, file, data_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &data_node->key,
+ data_node->header.lnum, data_node->header.offs);
+ if (err)
+ return err;
+ }
+
rb_erase(&data_node->rb, &file->data_nodes);
kfree(data_node);
}
check_dent_node:
if (rb_first(&file->dent_nodes)) {
- if (file->inum == UBIFS_ROOT_INO)
+ if (file->inum == UBIFS_ROOT_INO) {
/* '/' has no dentries. */
- return false;
+ handle_invalid_file(c, FILE_ROOT_HAS_DENT, file,
+ rb_entry(rb_first(&file->dent_nodes),
+ struct scanned_dent_node, rb));
+ return 0;
+ }
node = rb_first(&file->dent_nodes);
dent_node = rb_entry(node, struct scanned_dent_node, rb);
parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
} else {
/* Non-root files must have dentries. */
- if (file->inum != UBIFS_ROOT_INO)
- return false;
+ if (file->inum != UBIFS_ROOT_INO) {
+ if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr) {
+ handle_invalid_file(c, FILE_IS_DISCONNECTED,
+ file, NULL);
+ if (is_diconnected)
+ *is_diconnected = 1;
+ } else {
+ handle_invalid_file(c, FILE_HAS_NO_DENT,
+ file, NULL);
+ }
+ return 0;
+ }
}
if (file->ino.is_xattr) {
- if (!parent_file)
+ if (!parent_file) {
/* Host inode is not found. */
- return false;
- if (parent_file->ino.is_xattr)
+ handle_invalid_file(c, XATTR_HAS_NO_HOST, file, NULL);
+ return 0;
+ }
+ if (parent_file->ino.is_xattr) {
/* Host cannot be a xattr file. */
- return false;
+ handle_invalid_file(c, XATTR_HAS_WRONG_HOST, file, parent_file);
+ return 0;
+ }
insert_xattr_file(c, file, parent_file);
if (parent_file->ino.is_encrypted) {
@@ -977,20 +1145,35 @@ check_dent_node:
parent_file->has_encrypted_info = true;
}
} else {
- if (parent_file && !S_ISDIR(parent_file->ino.mode))
+ if (parent_file && !S_ISDIR(parent_file->ino.mode)) {
/* Parent file should be directory. */
- return false;
+ if (type == UBIFS_ITYPE_REG) {
+ handle_invalid_file(c, FILE_IS_DISCONNECTED,
+ file, NULL);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ /* Delete dentries for the disconnected file. */
+ int err = delete_dent_nodes(c, file, 0);
+ if (err)
+ return err;
+ }
+ if (is_diconnected)
+ *is_diconnected = 1;
+ }
+ return 0;
+ }
/*
* Since xattr files are checked in first round, so all
* non-xattr files's @has_encrypted_info fields have been
* initialized.
*/
- if (file->ino.is_encrypted && !file->has_encrypted_info)
- return false;
+ if (file->ino.is_encrypted && !file->has_encrypted_info) {
+ handle_invalid_file(c, FILE_HAS_NO_ENCRYPT, file, NULL);
+ return 0;
+ }
}
- return true;
+ return 1;
}
static bool dentry_is_reachable(struct ubifs_info *c,
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 79b0babc..4b1b35b0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -369,6 +369,7 @@ static int init_fsck_info(struct ubifs_info *c, int argc, char *argv[])
c->private = fsck;
FSCK(c)->mode = mode;
+ INIT_LIST_HEAD(&FSCK(c)->disconnected_files);
c->assert_failed_cb = fsck_assert_failed;
c->set_failure_reason_cb = fsck_set_failure_reason;
c->get_failure_reason_cb = fsck_get_failure_reason;
@@ -445,6 +446,15 @@ static int do_fsck(void)
update_files_size(c);
+ log_out(c, "Check and handle invalid files");
+ err = handle_invalid_files(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_used_lebs;
+ }
+
+ destroy_file_list(c, &FSCK(c)->disconnected_files);
+free_used_lebs:
kfree(FSCK(c)->used_lebs);
destroy_file_tree(c, &FSCK(c)->scanned_files);
return err;
@@ -484,6 +494,7 @@ int main(int argc, char *argv[])
/*
* Step 6: Traverse tnc and construct files
* Step 7: Update files' size
+ * Step 8: Check and handle invalid files
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 6c93eb6b..34d300b2 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -39,7 +39,11 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/* Types of inconsistent problems */
enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED, INVALID_INO_NODE,
- INVALID_DENT_NODE, INVALID_DATA_NODE, SCAN_CORRUPTED };
+ INVALID_DENT_NODE, INVALID_DATA_NODE, SCAN_CORRUPTED, FILE_HAS_NO_INODE,
+ FILE_HAS_0_NLINK_INODE, FILE_HAS_INCONSIST_TYPE, FILE_HAS_TOO_MANY_DENT,
+ FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
+ XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
+ FILE_ROOT_HAS_DENT };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -183,6 +187,16 @@ struct scanned_file {
};
/**
+ * invalid_file_problem - problem instance for invalid file.
+ * @file: invalid file instance
+ * @priv: invalid instance in @file, could be a dent_node or data_node
+ */
+struct invalid_file_problem {
+ struct scanned_file *file;
+ void *priv;
+};
+
+/**
* ubifs_rebuild_info - UBIFS rebuilding information.
* @lpts: lprops table
* @write_buf: write buffer for LEB @head_lnum
@@ -206,6 +220,7 @@ struct ubifs_rebuild_info {
* %FR_LPT_INCORRECT
* @scanned_files: tree of all scanned files
* @used_lebs: a bitmap used for recording used lebs
+ * @disconnected_files: regular files without dentries
* @try_rebuild: %true means that try to rebuild fs when fsck failed
* @rebuild: rebuilding-related information
*/
@@ -215,6 +230,7 @@ struct ubifs_fsck_info {
unsigned int lpt_status;
struct rb_root scanned_files;
unsigned long *used_lebs;
+ struct list_head disconnected_files;
bool try_rebuild;
struct ubifs_rebuild_info *rebuild;
};
@@ -286,9 +302,11 @@ int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
struct scanned_node *sn, int key_type, ino_t inum);
void destroy_file_content(struct ubifs_info *c, struct scanned_file *file);
void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
+void destroy_file_list(struct ubifs_info *c, struct list_head *file_list);
struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum);
-bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
- struct rb_root *file_tree);
+int delete_file(struct ubifs_info *c, struct scanned_file *file);
+int file_is_valid(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree, int *is_diconnected);
bool file_is_reachable(struct ubifs_info *c, struct scanned_file *file,
struct rb_root *file_tree);
int check_and_correct_files(struct ubifs_info *c);
@@ -299,5 +317,6 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c);
/* check_files.c */
int traverse_tnc_and_construct_files(struct ubifs_info *c);
void update_files_size(struct ubifs_info *c);
+int handle_invalid_files(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index c5ecd109..9222cba4 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -12,6 +12,7 @@
#include "ubifs.h"
#include "defs.h"
#include "debug.h"
+#include "key.h"
#include "fsck.ubifs.h"
/*
@@ -46,6 +47,17 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid dentry node"}, // INVALID_DENT_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid data node"}, // INVALID_DATA_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data is scanned"}, // SCAN_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no inode"}, // FILE_HAS_NO_INODE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has zero-nlink inode"}, // FILE_HAS_0_NLINK_INODE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has inconsistent type"}, // FILE_HAS_INCONSIST_TYPE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has too many dentries"}, // FILE_HAS_TOO_MANY_DENT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File should not have data"}, // FILE_SHOULDNT_HAVE_DATA
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no dentries"}, // FILE_HAS_NO_DENT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has no host"}, // XATTR_HAS_NO_HOST
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has wrong host"}, // XATTR_HAS_WRONG_HOST
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Encrypted file has no encryption information"}, // FILE_HAS_NO_ENCRYPT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"}, // FILE_IS_DISCONNECTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
};
static const char *get_question(const struct fsck_problem *problem,
@@ -65,6 +77,21 @@ static const char *get_question(const struct fsck_problem *problem,
return "Drop it?";
case ORPHAN_CORRUPTED:
return "Drop orphans on the LEB?";
+ case FILE_HAS_NO_INODE:
+ case FILE_HAS_0_NLINK_INODE:
+ case FILE_HAS_NO_DENT:
+ case XATTR_HAS_NO_HOST:
+ case XATTR_HAS_WRONG_HOST:
+ case FILE_HAS_NO_ENCRYPT:
+ case FILE_ROOT_HAS_DENT:
+ return "Delete it?";
+ case FILE_HAS_INCONSIST_TYPE:
+ case FILE_HAS_TOO_MANY_DENT:
+ return "Remove dentry?";
+ case FILE_SHOULDNT_HAVE_DATA:
+ return "Remove data block?";
+ case FILE_IS_DISCONNECTED:
+ return "Put it into disconnected list?";
}
return "Fix it?";
@@ -98,6 +125,79 @@ static void print_problem(const struct ubifs_info *c,
problem->desc, zbr->lnum, zbr->lnum, zbr->offs);
break;
}
+ case FILE_HAS_NO_INODE:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+
+ log_out(c, "problem: %s, ino %lu", problem->desc, ifp->file->inum);
+ break;
+ }
+ case FILE_HAS_INCONSIST_TYPE:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu, inode type %s%s, dentry %s has type %s%s",
+ problem->desc, ifp->file->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
+ ifp->file->ino.is_xattr ? "(xattr)" : "",
+ c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
+ ubifs_get_type_name(dent_node->type),
+ key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
+ break;
+ }
+ case FILE_HAS_TOO_MANY_DENT:
+ case FILE_ROOT_HAS_DENT:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu, type %s%s, dentry %s",
+ problem->desc, ifp->file->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
+ ifp->file->ino.is_xattr ? "(xattr)" : "",
+ c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name);
+ break;
+ }
+ case FILE_SHOULDNT_HAVE_DATA:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_data_node *data_node = (const struct scanned_data_node *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu, type %s%s, data block %u",
+ problem->desc, ifp->file->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
+ ifp->file->ino.is_xattr ? "(xattr)" : "",
+ key_block(c, &data_node->key));
+ break;
+ }
+ case FILE_HAS_0_NLINK_INODE:
+ case FILE_HAS_NO_DENT:
+ case XATTR_HAS_NO_HOST:
+ case FILE_HAS_NO_ENCRYPT:
+ case FILE_IS_DISCONNECTED:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+
+ log_out(c, "problem: %s, ino %lu type %s%s", problem->desc,
+ ifp->file->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
+ ifp->file->ino.is_xattr ? "(xattr)" : "");
+ break;
+ }
+ case XATTR_HAS_WRONG_HOST:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_file *host = (const struct scanned_file *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu type %s%s, host ino %lu type %s%s",
+ problem->desc, ifp->file->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
+ ifp->file->ino.is_xattr ? "(xattr)" : "", host->inum,
+ ubifs_get_type_name(ubifs_get_dent_type(host->ino.mode)),
+ host->ino.is_xattr ? "(xattr)" : "");
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index f190517c..8fc78ce3 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -641,7 +641,7 @@ static void filter_invalid_files(struct ubifs_info *c)
list_del(&file->list);
rb_erase(&file->rb, tree);
- if (!file_is_valid(c, file, tree)) {
+ if (!file_is_valid(c, file, tree, NULL)) {
destroy_file_content(c, file);
kfree(file);
}
@@ -651,7 +651,7 @@ static void filter_invalid_files(struct ubifs_info *c)
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
- if (!file_is_valid(c, file, tree))
+ if (!file_is_valid(c, file, tree, NULL))
list_add(&file->list, &tmp_list);
}
--
2.13.6
Add linux hexdump implementations lib, because function print_hex_dump()
is used in UBIFS linux kernel libs.
This is a preparation for replacing implementation of UBIFS utils with
linux kernel libs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 1 +
ubifs-utils/common/defs.h | 6 ++
ubifs-utils/common/hexdump.c | 218 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 225 insertions(+)
create mode 100644 ubifs-utils/common/hexdump.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index c14ba028..cb4e1cf1 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -24,6 +24,7 @@ common_SOURCES = \
ubifs-utils/common/hashtable/hashtable_itr.c \
ubifs-utils/common/devtable.h \
ubifs-utils/common/devtable.c \
+ ubifs-utils/common/hexdump.c \
ubifs-utils/common/ubifs.h \
ubifs-utils/common/key.h \
ubifs-utils/common/lpt.h \
diff --git a/ubifs-utils/common/defs.h b/ubifs-utils/common/defs.h
index 6d99a2fd..548d9dfb 100644
--- a/ubifs-utils/common/defs.h
+++ b/ubifs-utils/common/defs.h
@@ -26,6 +26,12 @@ enum { MKFS_PROGRAM_TYPE = 0 };
enum { ERR_LEVEL = 1, WARN_LEVEL, INFO_LEVEL, DEBUG_LEVEL };
+enum {
+ DUMP_PREFIX_NONE,
+ DUMP_PREFIX_ADDRESS,
+ DUMP_PREFIX_OFFSET
+};
+
#define pr_debug(fmt, ...) do { if (info_.debug_level >= DEBUG_LEVEL) \
printf("<DEBUG> %s[%d] (%s): %s: " fmt, PROGRAM_NAME, getpid(), \
info_.dev_name, __FUNCTION__, ##__VA_ARGS__); \
diff --git a/ubifs-utils/common/hexdump.c b/ubifs-utils/common/hexdump.c
new file mode 100644
index 00000000..7ac46943
--- /dev/null
+++ b/ubifs-utils/common/hexdump.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * lib/hexdump.c
+ */
+
+#include <stdio.h>
+
+#include "linux_types.h"
+#include "defs.h"
+
+#define __get_unaligned_t(type, ptr) ({ \
+ const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
+ __pptr->x; \
+})
+
+#define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
+
+const char hex_asc[] = "0123456789abcdef";
+
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
+
+void print_hex_dump(const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii);
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ * linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+static int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
+ int groupsize, char *linebuf, size_t linebuflen,
+ bool ascii)
+{
+ const u8 *ptr = buf;
+ int ngroups;
+ u8 ch;
+ int j, lx = 0;
+ int ascii_column;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ if (len > rowsize) /* limit to one line at a time */
+ len = rowsize;
+ if (!is_power_of_2(groupsize) || groupsize > 8)
+ groupsize = 1;
+ if ((len % groupsize) != 0) /* no mixed size output */
+ groupsize = 1;
+
+ ngroups = len / groupsize;
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+ if (!linebuflen)
+ goto overflow1;
+
+ if (!len)
+ goto nil;
+
+ if (groupsize == 8) {
+ const u64 *ptr8 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%16.16llx", j ? " " : "",
+ get_unaligned(ptr8 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 4) {
+ const u32 *ptr4 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%8.8x", j ? " " : "",
+ get_unaligned(ptr4 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 2) {
+ const u16 *ptr2 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%4.4x", j ? " " : "",
+ get_unaligned(ptr2 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else {
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = hex_asc_hi(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = hex_asc_lo(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ if (j)
+ lx--;
+ }
+ if (!ascii)
+ goto nil;
+
+ while (lx < ascii_column) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+ }
+nil:
+ linebuf[lx] = '\0';
+ return lx;
+overflow2:
+ linebuf[lx++] = '\0';
+overflow1:
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+
+/**
+ * print_hex_dump - print a text hex dump to syslog for a binary blob of data
+ * @prefix_str: string to prefix each line with;
+ * caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @ascii: include ASCII after the hex output
+ *
+ * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
+ * to the kernel log at the specified kernel log level, with an optional
+ * leading prefix.
+ *
+ * print_hex_dump() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ * print_hex_dump() iterates over the entire input @buf, breaking it into
+ * "line size" chunks to format and print.
+ *
+ * E.g.:
+ * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
+ * 16, 1, frame->data, frame->len, true);
+ *
+ * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
+ * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
+ * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
+ */
+void print_hex_dump(const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ char linebuf[32 * 3 + 2 + 32 + 1];
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len; i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ linebuf, sizeof(linebuf), ascii);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ printf("%s%p: %s\n", prefix_str, ptr + i, linebuf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ printf("%s%.8x: %s\n", prefix_str, i, linebuf);
+ break;
+ default:
+ printf("%s%s\n", prefix_str, linebuf);
+ break;
+ }
+ }
+}
--
2.13.6
This is the 7/18 step of fsck. Update files' size according to size
tree for check mode, now all files are updated after replaying journal.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 45 ++++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 3 +++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 1 +
ubifs-utils/libubifs/recovery.c | 16 -------------
ubifs-utils/libubifs/ubifs.h | 16 +++++++++++++
5 files changed, 65 insertions(+), 16 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index 29848c4e..0fd6b32a 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -309,3 +309,48 @@ out:
}
return err;
}
+
+/**
+ * update_files_size - Update files' size.
+ * @c: UBIFS file-system description object
+ *
+ * This function updates files' size according to @c->size_tree for check mode.
+ */
+void update_files_size(struct ubifs_info *c)
+{
+ struct rb_node *this;
+
+ if (FSCK(c)->mode != CHECK_MODE) {
+ /* Other modes(rw) have updated inode size in place. */
+ dbg_fsck("skip updating files' size%s, in %s",
+ mode_name(c), c->dev_name);
+ return;
+ }
+
+ log_out(c, "Update files' size");
+
+ this = rb_first(&c->size_tree);
+ while (this) {
+ struct size_entry *e;
+
+ e = rb_entry(this, struct size_entry, rb);
+ this = rb_next(this);
+
+ if (e->exists && e->i_size < e->d_size) {
+ struct scanned_file *file;
+
+ file = lookup_file(&FSCK(c)->scanned_files, e->inum);
+ if (file && file->ino.header.exist &&
+ file->ino.size < e->d_size) {
+ dbg_fsck("update file(%lu) size %llu->%llu, in %s",
+ e->inum, file->ino.size,
+ (unsigned long long)e->d_size,
+ c->dev_name);
+ file->ino.size = e->d_size;
+ }
+ }
+
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+ }
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 1486ab4d..79b0babc 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -443,6 +443,8 @@ static int do_fsck(void)
return err;
}
+ update_files_size(c);
+
kfree(FSCK(c)->used_lebs);
destroy_file_tree(c, &FSCK(c)->scanned_files);
return err;
@@ -481,6 +483,7 @@ int main(int argc, char *argv[])
/*
* Step 6: Traverse tnc and construct files
+ * Step 7: Update files' size
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 0d4a0d63..6c93eb6b 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -298,5 +298,6 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c);
/* check_files.c */
int traverse_tnc_and_construct_files(struct ubifs_info *c);
+void update_files_size(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
index a5133a0f..905e1645 100644
--- a/ubifs-utils/libubifs/recovery.c
+++ b/ubifs-utils/libubifs/recovery.c
@@ -1082,22 +1082,6 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
}
/**
- * struct size_entry - inode size information for recovery.
- * @rb: link in the RB-tree of sizes
- * @inum: inode number
- * @i_size: size on inode
- * @d_size: maximum size based on data nodes
- * @exists: indicates whether the inode exists
- */
-struct size_entry {
- struct rb_node rb;
- ino_t inum;
- loff_t i_size;
- loff_t d_size;
- int exists;
-};
-
-/**
* add_ino - add an entry to the size tree.
* @c: UBIFS file-system description object
* @inum: inode number
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 03150cdb..72497cd9 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1297,6 +1297,22 @@ static inline int ubifs_authenticated(const struct ubifs_info *c)
return c->authenticated;
}
+/**
+ * struct size_entry - inode size information for recovery.
+ * @rb: link in the RB-tree of sizes
+ * @inum: inode number
+ * @i_size: size on inode
+ * @d_size: maximum size based on data nodes
+ * @exists: indicates whether the inode exists
+ */
+struct size_entry {
+ struct rb_node rb;
+ ino_t inum;
+ loff_t i_size;
+ loff_t d_size;
+ int exists;
+};
+
#ifdef WITH_CRYPTO
int ubifs_init_authentication(struct ubifs_info *c);
int ubifs_shash_init(const struct ubifs_info *c, struct shash_desc *desc);
--
2.13.6
This is the 6/18 step of fsck. Traverse TNC and construct files. There
could be following steps and possible errors:
Step 1. Traverse TNC, check whether the leaf node is valid, remove invalid
nodes, construct file for valid node and insert file into file tree.
a. corrupted node searched from TNC: remove corresponding TNC branch for
danger mode and normal mode with 'yes' answer, other modes will exit.
b. corrupted index node read from TNC: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/check_files.c | 201 ++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/extract_files.c | 216 ++++++++++++++++++++++++---------
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 15 ++-
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 6 +-
ubifs-utils/fsck.ubifs/problem.c | 6 +
ubifs-utils/libubifs/debug.c | 12 +-
ubifs-utils/libubifs/debug.h | 1 +
8 files changed, 395 insertions(+), 65 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/check_files.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index b7ee3de4..e12052ce 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -83,7 +83,8 @@ fsck_ubifs_SOURCES = \
ubifs-utils/fsck.ubifs/problem.c \
ubifs-utils/fsck.ubifs/load_fs.c \
ubifs-utils/fsck.ubifs/extract_files.c \
- ubifs-utils/fsck.ubifs/rebuild_fs.c
+ ubifs-utils/fsck.ubifs/rebuild_fs.c \
+ ubifs-utils/fsck.ubifs/check_files.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
new file mode 100644
index 00000000..982c05b7
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "fsck.ubifs.h"
+
+struct invalid_node {
+ union ubifs_key key;
+ int lnum;
+ int offs;
+ struct list_head list;
+};
+
+struct iteration_info {
+ struct list_head invalid_nodes;
+};
+
+static int add_invalid_node(struct ubifs_info *c, union ubifs_key *key,
+ int lnum, int offs, struct iteration_info *iter)
+{
+ struct invalid_node *in;
+
+ in = kmalloc(sizeof(struct invalid_node), GFP_KERNEL);
+ if (!in) {
+ log_err(c, errno, "can not allocate invalid node");
+ return -ENOMEM;
+ }
+
+ key_copy(c, key, &in->key);
+ in->lnum = lnum;
+ in->offs = offs;
+ list_add(&in->list, &iter->invalid_nodes);
+
+ return 0;
+}
+
+static int construct_file(struct ubifs_info *c, union ubifs_key *key,
+ int lnum, int offs, void *node,
+ struct iteration_info *iter)
+{
+ ino_t inum = 0;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
+ struct scanned_node *sn = NULL;
+ struct ubifs_ch *ch = (struct ubifs_ch *)node;
+
+ switch (ch->node_type) {
+ case UBIFS_INO_NODE:
+ {
+ struct scanned_ino_node ino_node;
+
+ if (!parse_ino_node(c, lnum, offs, node, key, &ino_node)) {
+ if (fix_problem(c, INVALID_INO_NODE, NULL))
+ return add_invalid_node(c, key, lnum, offs, iter);
+ }
+ inum = key_inum(c, key);
+ sn = (struct scanned_node *)&ino_node;
+ break;
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ struct scanned_dent_node dent_node;
+
+ if (!parse_dent_node(c, lnum, offs, node, key, &dent_node)) {
+ if (fix_problem(c, INVALID_DENT_NODE, NULL))
+ return add_invalid_node(c, key, lnum, offs, iter);
+ }
+ inum = dent_node.inum;
+ sn = (struct scanned_node *)&dent_node;
+ break;
+ }
+ case UBIFS_DATA_NODE:
+ {
+ struct scanned_data_node data_node;
+
+ if (!parse_data_node(c, lnum, offs, node, key, &data_node)) {
+ if (fix_problem(c, INVALID_DATA_NODE, NULL))
+ return add_invalid_node(c, key, lnum, offs, iter);
+ }
+ inum = key_inum(c, key);
+ sn = (struct scanned_node *)&data_node;
+ break;
+ }
+ default:
+ ubifs_assert(c, 0);
+ }
+
+ dbg_fsck("construct file(%lu) for %s node, TNC location %d:%d, in %s",
+ inum, ubifs_get_key_name(key_type(c, key)), sn->lnum, sn->offs,
+ c->dev_name);
+ return insert_or_update_file(c, tree, sn, key_type(c, key), inum);
+}
+
+static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *priv)
+{
+ void *node;
+ struct iteration_info *iter = (struct iteration_info *)priv;
+ union ubifs_key *key = &zbr->key;
+ int lnum = zbr->lnum, offs = zbr->offs, len = zbr->len, err = 0;
+
+ if (len < UBIFS_CH_SZ) {
+ ubifs_err(c, "bad leaf length %d (LEB %d:%d)",
+ len, lnum, offs);
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
+ return -EINVAL;
+ }
+ if (key_type(c, key) != UBIFS_INO_KEY &&
+ key_type(c, key) != UBIFS_DATA_KEY &&
+ key_type(c, key) != UBIFS_DENT_KEY &&
+ key_type(c, key) != UBIFS_XENT_KEY) {
+ ubifs_err(c, "bad key type %d (LEB %d:%d)",
+ key_type(c, key), lnum, offs);
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
+ return -EINVAL;
+ }
+
+ node = kmalloc(len, GFP_NOFS);
+ if (!node)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, node);
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED)) {
+ if (fix_problem(c, TNC_DATA_CORRUPTED, NULL))
+ err = add_invalid_node(c, key, lnum, offs, iter);
+ }
+ goto out;
+ }
+
+ err = construct_file(c, key, lnum, offs, node, iter);
+
+out:
+ kfree(node);
+ return err;
+}
+
+static int remove_invalid_nodes(struct ubifs_info *c,
+ struct list_head *invalid_nodes, int error)
+{
+ int ret = 0;;
+ struct invalid_node *in;
+
+ while (!list_empty(invalid_nodes)) {
+ in = list_entry(invalid_nodes->next, struct invalid_node, list);
+
+ if (!error) {
+ error = ubifs_tnc_remove_node(c, &in->key, in->lnum, in->offs);
+ if (error) {
+ /* TNC traversing is finished, any TNC path is accessible */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ret = error;
+ }
+ }
+
+ list_del(&in->list);
+ kfree(in);
+ }
+
+ return ret;
+}
+
+/**
+ * traverse_tnc_and_construct_files - traverse TNC and construct all files.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks all index nodes and non-index nodes by traversing TNC,
+ * then construct file according to scanned non-index nodes and insert file
+ * into file tree. Returns zero in case of success, a negative error code in
+ * case of failure.
+ */
+int traverse_tnc_and_construct_files(struct ubifs_info *c)
+{
+ int err, ret;
+ struct iteration_info iter;
+
+ FSCK(c)->scanned_files = RB_ROOT;
+ INIT_LIST_HEAD(&iter.invalid_nodes);
+
+ err = dbg_walk_index(c, check_leaf, NULL, &iter);
+
+ ret = remove_invalid_nodes(c, &iter.invalid_nodes, err);
+ if (!err)
+ err = ret;
+
+ if (err)
+ destroy_file_tree(c, &FSCK(c)->scanned_files);
+ return err;
+}
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index c3ab2b76..e9c71a3c 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -74,15 +74,24 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
ino_t inum = key_inum(c, key);
if (!inum || inum > INUM_WATERMARK) {
- dbg_fsck("bad inode node(bad inum %lu) at %d:%d, in %s",
- inum, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node(bad inum %lu) at %d:%d, in %s",
+ inum, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node(bad inum %lu) at %d:%d",
+ inum, lnum, offs);
goto out;
}
if (ch->node_type != key_type(c, key)) {
- dbg_fsck("bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
- inum, ch->node_type, key_type(c, key),
- lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
+ inum, ch->node_type, key_type(c, key),
+ lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(inconsistent node type %d vs key_type %d) at %d:%d",
+ inum, ch->node_type, key_type(c, key),
+ lnum, offs);
goto out;
}
@@ -101,60 +110,101 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
ino_node->size = le64_to_cpu(ino->size);
if (inum == UBIFS_ROOT_INO && !S_ISDIR(ino_node->mode)) {
- dbg_fsck("bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d, in %s",
- inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, lnum, offs,
+ c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(root inode is not dir, tyoe %u) at %d:%d",
+ inum, ino_node->mode & S_IFMT, lnum, offs);
goto out;
}
if (ino_node->size > c->max_inode_sz) {
- dbg_fsck("bad inode node %lu(size %llu is too large) at %d:%d, in %s",
- inum, ino_node->size, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(size %llu is too large) at %d:%d, in %s",
+ inum, ino_node->size, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(size %llu is too large) at %d:%d",
+ inum, ino_node->size, lnum, offs);
goto out;
}
if (le16_to_cpu(ino->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
- dbg_fsck("bad inode node %lu(unknown compression type %d) at %d:%d, in %s",
- inum, le16_to_cpu(ino->compr_type), lnum, offs,
- c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(unknown compression type %d) at %d:%d, in %s",
+ inum, le16_to_cpu(ino->compr_type), lnum, offs,
+ c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(unknown compression type %d) at %d:%d",
+ inum, le16_to_cpu(ino->compr_type), lnum, offs);
goto out;
}
if (ino_node->xnms + ino_node->xcnt > XATTR_LIST_MAX) {
- dbg_fsck("bad inode node %lu(too big xnames %u xcount %u) at %d:%d, in %s",
- inum, ino_node->xnms, ino_node->xcnt,
- lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(too big xnames %u xcount %u) at %d:%d, in %s",
+ inum, ino_node->xnms, ino_node->xcnt,
+ lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(too big xnames %u xcount %u) at %d:%d",
+ inum, ino_node->xnms, ino_node->xcnt,
+ lnum, offs);
goto out;
}
if (data_len < 0 || data_len > UBIFS_MAX_INO_DATA) {
- dbg_fsck("bad inode node %lu(invalid data len %d) at %d:%d, in %s",
- inum, data_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(invalid data len %d) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(invalid data len %d) at %d:%d",
+ inum, data_len, lnum, offs);
goto out;
}
if (UBIFS_INO_NODE_SZ + data_len != node_len) {
- dbg_fsck("bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d, in %s",
- inum, data_len, node_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d, in %s",
+ inum, data_len, node_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(inconsistent data len %d vs node len %d) at %d:%d",
+ inum, data_len, node_len, lnum, offs);
goto out;
}
if (ino_node->is_xattr) {
if (!S_ISREG(ino_node->mode)) {
- dbg_fsck("bad inode node %lu(bad type %u for xattr) at %d:%d, in %s",
- inum, ino_node->mode & S_IFMT,
- lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(bad type %u for xattr) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT,
+ lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(bad type %u for xattr) at %d:%d",
+ inum, ino_node->mode & S_IFMT,
+ lnum, offs);
goto out;
}
if (data_len != ino_node->size) {
- dbg_fsck("bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d, in %s",
- inum, data_len, ino_node->size,
- lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d, in %s",
+ inum, data_len, ino_node->size,
+ lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(inconsistent data_len %d vs size %llu for xattr) at %d:%d",
+ inum, data_len, ino_node->size,
+ lnum, offs);
goto out;
}
if (ino_node->xcnt || ino_node->xsz || ino_node->xnms) {
- dbg_fsck("bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d, in %s",
- inum, ino_node->xcnt, ino_node->xsz,
- ino_node->xnms, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d, in %s",
+ inum, ino_node->xcnt, ino_node->xsz,
+ ino_node->xnms, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(non zero xattr count %u xattr size %u xattr names %u for xattr) at %d:%d",
+ inum, ino_node->xcnt, ino_node->xsz,
+ ino_node->xnms, lnum, offs);
goto out;
}
}
@@ -162,15 +212,23 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
switch (ino_node->mode & S_IFMT) {
case S_IFREG:
if (!ino_node->is_xattr && data_len != 0) {
- dbg_fsck("bad inode node %lu(bad data len %d for reg file) at %d:%d, in %s",
- inum, data_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(bad data len %d for reg file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(bad data len %d for reg file) at %d:%d",
+ inum, data_len, lnum, offs);
goto out;
}
break;
case S_IFDIR:
if (data_len != 0) {
- dbg_fsck("bad inode node %lu(bad data len %d for dir file) at %d:%d, in %s",
- inum, data_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(bad data len %d for dir file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(bad data len %d for dir file) at %d:%d",
+ inum, data_len, lnum, offs);
goto out;
}
break;
@@ -187,8 +245,12 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
* Just drop the inode node when above class of
* exceptions are found.
*/
- dbg_fsck("bad symlink inode node %lu(bad data len %d) at %d:%d, in %s",
- inum, data_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad symlink inode node %lu(bad data len %d) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad symlink inode node %lu(bad data len %d) at %d:%d",
+ inum, data_len, lnum, offs);
goto out;
}
break;
@@ -200,9 +262,14 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
int sz_new = sizeof(dev->new), sz_huge = sizeof(dev->huge);
if (data_len != sz_new && data_len != sz_huge) {
- dbg_fsck("bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d, in %s",
- inum, data_len, sz_new, sz_huge, lnum,
- offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d, in %s",
+ inum, data_len, sz_new, sz_huge, lnum,
+ offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(bad data len %d for char/block file, expect %d or %d) at %d:%d",
+ inum, data_len, sz_new, sz_huge, lnum,
+ offs);
goto out;
}
break;
@@ -211,22 +278,35 @@ bool parse_ino_node(struct ubifs_info *c, int lnum, int offs, void *node,
fallthrough;
case S_IFIFO:
if (data_len != 0) {
- dbg_fsck("bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d, in %s",
- inum, data_len, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d, in %s",
+ inum, data_len, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(bad data len %d for fifo/sock file) at %d:%d",
+ inum, data_len, lnum, offs);
goto out;
}
break;
default:
/* invalid file type. */
- dbg_fsck("bad inode node %lu(unknown type %u) at %d:%d, in %s",
- inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(unknown type %u) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(unknown type %u) at %d:%d",
+ inum, ino_node->mode & S_IFMT, lnum, offs);
goto out;
}
if (ino_node->is_encrypted && !inode_can_be_encrypted(c, ino_node)) {
- dbg_fsck("bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d, in %s",
- inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
- c->encrypted, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d, in %s",
+ inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
+ c->encrypted, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad inode node %lu(encrypted but cannot be encrypted, type %u, is_xattr %d, fs_encrypted %d) at %d:%d",
+ inum, ino_node->mode & S_IFMT, ino_node->is_xattr,
+ c->encrypted, lnum, offs);
goto out;
}
@@ -272,10 +352,16 @@ bool parse_dent_node(struct ubifs_info *c, int lnum, int offs, void *node,
(key_type == UBIFS_XENT_KEY &&
strnlen((const char *)dent->name, nlen) != nlen) ||
inum > INUM_WATERMARK || key_type != ch->node_type) {
- dbg_fsck("bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d, in %s",
- ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
- node_len, nlen, dent->type, inum, key_type,
- ch->node_type, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d, in %s",
+ ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
+ node_len, nlen, dent->type, inum, key_type,
+ ch->node_type, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad %s node(len %d nlen %d type %d inum %lu key_type %d node_type %d) at %d:%d",
+ ch->node_type == UBIFS_XENT_NODE ? "xattr entry" : "directory entry",
+ node_len, nlen, dent->type, inum, key_type,
+ ch->node_type, lnum, offs);
goto out;
}
@@ -318,15 +404,23 @@ bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
ino_t inum = key_inum(c, key);
if (ch->node_type != key_type(c, key)) {
- dbg_fsck("bad data node(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
- ch->node_type, key_type(c, key),
- lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad data node(inconsistent node type %d vs key_type %d) at %d:%d, in %s",
+ ch->node_type, key_type(c, key),
+ lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad data node(inconsistent node type %d vs key_type %d) at %d:%d",
+ ch->node_type, key_type(c, key), lnum, offs);
goto out;
}
if (!inum || inum > INUM_WATERMARK) {
- dbg_fsck("bad data node(bad inum %lu) at %d:%d, in %s",
- inum, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad data node(bad inum %lu) at %d:%d, in %s",
+ inum, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad data node(bad inum %lu) at %d:%d",
+ inum, lnum, offs);
goto out;
}
@@ -336,14 +430,22 @@ bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
data_node->size = le32_to_cpu(dn->size);
if (!data_node->size || data_node->size > UBIFS_BLOCK_SIZE) {
- dbg_fsck("bad data node(invalid size %u) at %d:%d, in %s",
- data_node->size, lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad data node(invalid size %u) at %d:%d, in %s",
+ data_node->size, lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad data node(invalid size %u) at %d:%d",
+ data_node->size, lnum, offs);
goto out;
}
if (le16_to_cpu(dn->compr_type) >= UBIFS_COMPR_TYPES_CNT) {
- dbg_fsck("bad data node(invalid compression type %d) at %d:%d, in %s",
- le16_to_cpu(dn->compr_type), lnum, offs, c->dev_name);
+ if (FSCK(c)->mode == REBUILD_MODE)
+ dbg_fsck("bad data node(invalid compression type %d) at %d:%d, in %s",
+ le16_to_cpu(dn->compr_type), lnum, offs, c->dev_name);
+ else
+ log_out(c, "bad data node(invalid compression type %d) at %d:%d",
+ le16_to_cpu(dn->compr_type), lnum, offs);
goto out;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 471c2cd9..c85e9147 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -434,7 +434,17 @@ void handle_error(const struct ubifs_info *c, int reason_set)
*/
static int do_fsck(void)
{
- return 0;
+ int err;
+
+ log_out(c, "Traverse TNC and construct files");
+ err = traverse_tnc_and_construct_files(c);
+ if (err) {
+ handle_error(c, HAS_TNC_CORRUPTED);
+ return err;
+ }
+
+ destroy_file_tree(c, &FSCK(c)->scanned_files);
+ return err;
}
int main(int argc, char *argv[])
@@ -468,6 +478,9 @@ int main(int argc, char *argv[])
goto out_close;
}
+ /*
+ * Step 6: Traverse tnc and construct files
+ */
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
ubifs_destroy_filesystem(c);
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 109c3924..fe6070ac 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -38,7 +38,8 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/* Types of inconsistent problems */
enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
- TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED };
+ TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED, INVALID_INO_NODE,
+ INVALID_DENT_NODE, INVALID_DATA_NODE };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -295,4 +296,7 @@ int check_and_correct_files(struct ubifs_info *c);
/* rebuild_fs.c */
int ubifs_rebuild_filesystem(struct ubifs_info *c);
+/* check_files.c */
+int traverse_tnc_and_construct_files(struct ubifs_info *c);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 9c8730a5..f99fd90e 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -42,6 +42,9 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted index node"}, // TNC_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data searched from TNC"}, // TNC_DATA_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted orphan LEB"}, // ORPHAN_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid inode node"}, // INVALID_INO_NODE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid dentry node"}, // INVALID_DENT_NODE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid data node"}, // INVALID_DATA_NODE
};
static const char *get_question(const struct fsck_problem *problem,
@@ -54,6 +57,9 @@ static const char *get_question(const struct fsck_problem *problem,
case BUD_CORRUPTED:
return "Drop bud?";
case TNC_DATA_CORRUPTED:
+ case INVALID_INO_NODE:
+ case INVALID_DENT_NODE:
+ case INVALID_DATA_NODE:
return "Drop it?";
case ORPHAN_CORRUPTED:
return "Drop orphans on the LEB?";
diff --git a/ubifs-utils/libubifs/debug.c b/ubifs-utils/libubifs/debug.c
index eaf403f9..836cbc74 100644
--- a/ubifs-utils/libubifs/debug.c
+++ b/ubifs-utils/libubifs/debug.c
@@ -51,7 +51,7 @@ static const char *get_key_hash(int hash)
}
}
-static const char *get_key_type(int type)
+const char *ubifs_get_key_name(int type)
{
switch (type) {
case UBIFS_INO_KEY:
@@ -102,23 +102,25 @@ const char *dbg_snprintf_key(const struct ubifs_info *c,
case UBIFS_INO_KEY:
len -= snprintf(p, len, "(%lu, %s)",
(unsigned long)key_inum(c, key),
- get_key_type(type));
+ ubifs_get_key_name(type));
break;
case UBIFS_DENT_KEY:
case UBIFS_XENT_KEY:
len -= snprintf(p, len, "(%lu, %s, %#08x)",
(unsigned long)key_inum(c, key),
- get_key_type(type), key_hash(c, key));
+ ubifs_get_key_name(type),
+ key_hash(c, key));
break;
case UBIFS_DATA_KEY:
len -= snprintf(p, len, "(%lu, %s, %u)",
(unsigned long)key_inum(c, key),
- get_key_type(type), key_block(c, key));
+ ubifs_get_key_name(type),
+ key_block(c, key));
break;
case UBIFS_TRUN_KEY:
len -= snprintf(p, len, "(%lu, %s)",
(unsigned long)key_inum(c, key),
- get_key_type(type));
+ ubifs_get_key_name(type));
break;
default:
len -= snprintf(p, len, "(bad key type: %#08x, %#08x)",
diff --git a/ubifs-utils/libubifs/debug.h b/ubifs-utils/libubifs/debug.h
index 400b4759..d015e62f 100644
--- a/ubifs-utils/libubifs/debug.h
+++ b/ubifs-utils/libubifs/debug.h
@@ -85,6 +85,7 @@ static inline int dbg_is_chk_index(__unused const struct ubifs_info *c)
{ return 0; }
/* Dump functions */
+const char *ubifs_get_key_name(int type);
const char *ubifs_get_type_name(int type);
const char *dbg_ntype(int type);
const char *dbg_cstate(int cmt_state);
--
2.13.6
Adapt journal.c in libubifs, compared with linux kernel implementations:
1. Remove all ubifs_jnl_XXX functions. Only keep the basic space
reservation code, fsck will add new functions for journaling
operations without using linux in-memory inode/dentry.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/journal.c | 1497 +---------------------------------------
1 file changed, 4 insertions(+), 1493 deletions(-)
diff --git a/ubifs-utils/libubifs/journal.c b/ubifs-utils/libubifs/journal.c
index 4590d616..37dc3f0e 100644
--- a/ubifs-utils/libubifs/journal.c
+++ b/ubifs-utils/libubifs/journal.c
@@ -46,7 +46,11 @@
* all the nodes.
*/
+#include "bitops.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "misc.h"
/**
* zero_ino_node_unused - zero out unused fields of an on-flash inode node.
@@ -68,16 +72,6 @@ static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
dent->padding1 = 0;
}
-/**
- * zero_trun_node_unused - zero out unused fields of an on-flash truncation
- * node.
- * @trun: the truncation node to zero out
- */
-static inline void zero_trun_node_unused(struct ubifs_trun_node *trun)
-{
- memset(trun->padding, 0, 12);
-}
-
static void ubifs_add_auth_dirt(struct ubifs_info *c, int lnum)
{
if (ubifs_authenticated(c))
@@ -436,70 +430,6 @@ static int get_dent_type(int mode)
return 0;
}
-/**
- * pack_inode - pack an inode node.
- * @c: UBIFS file-system description object
- * @ino: buffer in which to pack inode node
- * @inode: inode to pack
- * @last: indicates the last node of the group
- */
-static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino,
- const struct inode *inode, int last)
-{
- int data_len = 0, last_reference = !inode->i_nlink;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- ino->ch.node_type = UBIFS_INO_NODE;
- ino_key_init_flash(c, &ino->key, inode->i_ino);
- ino->creat_sqnum = cpu_to_le64(ui->creat_sqnum);
- ino->atime_sec = cpu_to_le64(inode_get_atime_sec(inode));
- ino->atime_nsec = cpu_to_le32(inode_get_atime_nsec(inode));
- ino->ctime_sec = cpu_to_le64(inode_get_ctime_sec(inode));
- ino->ctime_nsec = cpu_to_le32(inode_get_ctime_nsec(inode));
- ino->mtime_sec = cpu_to_le64(inode_get_mtime_sec(inode));
- ino->mtime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode));
- ino->uid = cpu_to_le32(i_uid_read(inode));
- ino->gid = cpu_to_le32(i_gid_read(inode));
- ino->mode = cpu_to_le32(inode->i_mode);
- ino->flags = cpu_to_le32(ui->flags);
- ino->size = cpu_to_le64(ui->ui_size);
- ino->nlink = cpu_to_le32(inode->i_nlink);
- ino->compr_type = cpu_to_le16(ui->compr_type);
- ino->data_len = cpu_to_le32(ui->data_len);
- ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt);
- ino->xattr_size = cpu_to_le32(ui->xattr_size);
- ino->xattr_names = cpu_to_le32(ui->xattr_names);
- zero_ino_node_unused(ino);
-
- /*
- * Drop the attached data if this is a deletion inode, the data is not
- * needed anymore.
- */
- if (!last_reference) {
- memcpy(ino->data, ui->data, ui->data_len);
- data_len = ui->data_len;
- }
-
- ubifs_prep_grp_node(c, ino, UBIFS_INO_NODE_SZ + data_len, last);
-}
-
-/**
- * mark_inode_clean - mark UBIFS inode as clean.
- * @c: UBIFS file-system description object
- * @ui: UBIFS inode to mark as clean
- *
- * This helper function marks UBIFS inode @ui as clean by cleaning the
- * @ui->dirty flag and releasing its budget. Note, VFS may still treat the
- * inode as dirty and try to write it back, but 'ubifs_write_inode()' would
- * just do nothing.
- */
-static void mark_inode_clean(struct ubifs_info *c, struct ubifs_inode *ui)
-{
- if (ui->dirty)
- ubifs_release_dirty_inode_budget(c, ui);
- ui->dirty = 0;
-}
-
static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
{
if (c->double_hash)
@@ -507,1422 +437,3 @@ static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
else
dent->cookie = 0;
}
-
-/**
- * ubifs_jnl_update - update inode.
- * @c: UBIFS file-system description object
- * @dir: parent inode or host inode in case of extended attributes
- * @nm: directory entry name
- * @inode: inode to update
- * @deletion: indicates a directory entry deletion i.e unlink or rmdir
- * @xent: non-zero if the directory entry is an extended attribute entry
- * @in_orphan: indicates whether the @inode is in orphan list
- *
- * This function updates an inode by writing a directory entry (or extended
- * attribute entry), the inode itself, and the parent directory inode (or the
- * host inode) to the journal.
- *
- * The function writes the host inode @dir last, which is important in case of
- * extended attributes. Indeed, then we guarantee that if the host inode gets
- * synchronized (with 'fsync()'), and the write-buffer it sits in gets flushed,
- * the extended attribute inode gets flushed too. And this is exactly what the
- * user expects - synchronizing the host inode synchronizes its extended
- * attributes. Similarly, this guarantees that if @dir is synchronized, its
- * directory entry corresponding to @nm gets synchronized too.
- *
- * If the inode (@inode) or the parent directory (@dir) are synchronous, this
- * function synchronizes the write-buffer.
- *
- * This function marks the @dir and @inode inodes as clean and returns zero on
- * success. In case of failure, a negative error code is returned.
- */
-int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
- const struct fscrypt_name *nm, const struct inode *inode,
- int deletion, int xent, int in_orphan)
-{
- int err, dlen, ilen, len, lnum, ino_offs, dent_offs, orphan_added = 0;
- int aligned_dlen, aligned_ilen, sync = IS_DIRSYNC(dir);
- int last_reference = !!(deletion && inode->i_nlink == 0);
- struct ubifs_inode *ui = ubifs_inode(inode);
- struct ubifs_inode *host_ui = ubifs_inode(dir);
- struct ubifs_dent_node *dent;
- struct ubifs_ino_node *ino;
- union ubifs_key dent_key, ino_key;
- u8 hash_dent[UBIFS_HASH_ARR_SZ];
- u8 hash_ino[UBIFS_HASH_ARR_SZ];
- u8 hash_ino_host[UBIFS_HASH_ARR_SZ];
-
- ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
-
- dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
- ilen = UBIFS_INO_NODE_SZ;
-
- /*
- * If the last reference to the inode is being deleted, then there is
- * no need to attach and write inode data, it is being deleted anyway.
- * And if the inode is being deleted, no need to synchronize
- * write-buffer even if the inode is synchronous.
- */
- if (!last_reference) {
- ilen += ui->data_len;
- sync |= IS_SYNC(inode);
- }
-
- aligned_dlen = ALIGN(dlen, 8);
- aligned_ilen = ALIGN(ilen, 8);
-
- len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
- /* Make sure to also account for extended attributes */
- if (ubifs_authenticated(c))
- len += ALIGN(host_ui->data_len, 8) + ubifs_auth_node_sz(c);
- else
- len += host_ui->data_len;
-
- dent = kzalloc(len, GFP_NOFS);
- if (!dent)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, len);
- if (err)
- goto out_free;
-
- if (!xent) {
- dent->ch.node_type = UBIFS_DENT_NODE;
- if (fname_name(nm) == NULL)
- dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash);
- else
- dent_key_init(c, &dent_key, dir->i_ino, nm);
- } else {
- dent->ch.node_type = UBIFS_XENT_NODE;
- xent_key_init(c, &dent_key, dir->i_ino, nm);
- }
-
- key_write(c, &dent_key, dent->key);
- dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino);
- dent->type = get_dent_type(inode->i_mode);
- dent->nlen = cpu_to_le16(fname_len(nm));
- memcpy(dent->name, fname_name(nm), fname_len(nm));
- dent->name[fname_len(nm)] = '\0';
- set_dent_cookie(c, dent);
-
- zero_dent_node_unused(dent);
- ubifs_prep_grp_node(c, dent, dlen, 0);
- err = ubifs_node_calc_hash(c, dent, hash_dent);
- if (err)
- goto out_release;
-
- ino = (void *)dent + aligned_dlen;
- pack_inode(c, ino, inode, 0);
- err = ubifs_node_calc_hash(c, ino, hash_ino);
- if (err)
- goto out_release;
-
- ino = (void *)ino + aligned_ilen;
- pack_inode(c, ino, dir, 1);
- err = ubifs_node_calc_hash(c, ino, hash_ino_host);
- if (err)
- goto out_release;
-
- if (last_reference && !in_orphan) {
- err = ubifs_add_orphan(c, inode->i_ino);
- if (err) {
- release_head(c, BASEHD);
- goto out_finish;
- }
- ui->del_cmtno = c->cmt_no;
- orphan_added = 1;
- }
-
- err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync);
- if (err)
- goto out_release;
- if (!sync) {
- struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
-
- ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
- ubifs_wbuf_add_ino_nolock(wbuf, dir->i_ino);
- }
- release_head(c, BASEHD);
- kfree(dent);
- ubifs_add_auth_dirt(c, lnum);
-
- if (deletion) {
- if (fname_name(nm) == NULL)
- err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash);
- else
- err = ubifs_tnc_remove_nm(c, &dent_key, nm);
- if (err)
- goto out_ro;
- err = ubifs_add_dirt(c, lnum, dlen);
- } else
- err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
- hash_dent, nm);
- if (err)
- goto out_ro;
-
- /*
- * Note, we do not remove the inode from TNC even if the last reference
- * to it has just been deleted, because the inode may still be opened.
- * Instead, the inode has been added to orphan lists and the orphan
- * subsystem will take further care about it.
- */
- ino_key_init(c, &ino_key, inode->i_ino);
- ino_offs = dent_offs + aligned_dlen;
- err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
- if (err)
- goto out_ro;
-
- ino_key_init(c, &ino_key, dir->i_ino);
- ino_offs += aligned_ilen;
- err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
- UBIFS_INO_NODE_SZ + host_ui->data_len, hash_ino_host);
- if (err)
- goto out_ro;
-
- if (in_orphan && inode->i_nlink)
- ubifs_delete_orphan(c, inode->i_ino);
-
- finish_reservation(c);
- spin_lock(&ui->ui_lock);
- ui->synced_i_size = ui->ui_size;
- spin_unlock(&ui->ui_lock);
- if (xent) {
- spin_lock(&host_ui->ui_lock);
- host_ui->synced_i_size = host_ui->ui_size;
- spin_unlock(&host_ui->ui_lock);
- }
- mark_inode_clean(c, ui);
- mark_inode_clean(c, host_ui);
- return 0;
-
-out_finish:
- finish_reservation(c);
-out_free:
- kfree(dent);
- return err;
-
-out_release:
- release_head(c, BASEHD);
- kfree(dent);
-out_ro:
- ubifs_ro_mode(c, err);
- if (orphan_added)
- ubifs_delete_orphan(c, inode->i_ino);
- finish_reservation(c);
- return err;
-}
-
-/**
- * ubifs_jnl_write_data - write a data node to the journal.
- * @c: UBIFS file-system description object
- * @inode: inode the data node belongs to
- * @key: node key
- * @buf: buffer to write
- * @len: data length (must not exceed %UBIFS_BLOCK_SIZE)
- *
- * This function writes a data node to the journal. Returns %0 if the data node
- * was successfully written, and a negative error code in case of failure.
- */
-int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
- const union ubifs_key *key, const void *buf, int len)
-{
- struct ubifs_data_node *data;
- int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
- int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
- int write_len;
- struct ubifs_inode *ui = ubifs_inode(inode);
- bool encrypted = IS_ENCRYPTED(inode);
- u8 hash[UBIFS_HASH_ARR_SZ];
-
- dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
- (unsigned long)key_inum(c, key), key_block(c, key), len);
- ubifs_assert(c, len <= UBIFS_BLOCK_SIZE);
-
- if (encrypted)
- dlen += UBIFS_CIPHER_BLOCK_SIZE;
-
- auth_len = ubifs_auth_node_sz(c);
-
- data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
- if (!data) {
- /*
- * Fall-back to the write reserve buffer. Note, we might be
- * currently on the memory reclaim path, when the kernel is
- * trying to free some memory by writing out dirty pages. The
- * write reserve buffer helps us to guarantee that we are
- * always able to write the data.
- */
- allocated = 0;
- mutex_lock(&c->write_reserve_mutex);
- data = c->write_reserve_buf;
- }
-
- data->ch.node_type = UBIFS_DATA_NODE;
- key_write(c, key, &data->key);
- data->size = cpu_to_le32(len);
-
- if (!(ui->flags & UBIFS_COMPR_FL))
- /* Compression is disabled for this inode */
- compr_type = UBIFS_COMPR_NONE;
- else
- compr_type = ui->compr_type;
-
- out_len = compr_len = dlen - UBIFS_DATA_NODE_SZ;
- ubifs_compress(c, buf, len, &data->data, &compr_len, &compr_type);
- ubifs_assert(c, compr_len <= UBIFS_BLOCK_SIZE);
-
- if (encrypted) {
- err = ubifs_encrypt(inode, data, compr_len, &out_len, key_block(c, key));
- if (err)
- goto out_free;
-
- } else {
- data->compr_size = 0;
- out_len = compr_len;
- }
-
- dlen = UBIFS_DATA_NODE_SZ + out_len;
- if (ubifs_authenticated(c))
- write_len = ALIGN(dlen, 8) + auth_len;
- else
- write_len = dlen;
-
- data->compr_type = cpu_to_le16(compr_type);
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, DATAHD, write_len);
- if (err)
- goto out_free;
-
- ubifs_prepare_node(c, data, dlen, 0);
- err = write_head(c, DATAHD, data, write_len, &lnum, &offs, 0);
- if (err)
- goto out_release;
-
- err = ubifs_node_calc_hash(c, data, hash);
- if (err)
- goto out_release;
-
- ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
- release_head(c, DATAHD);
-
- ubifs_add_auth_dirt(c, lnum);
-
- err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
- if (err)
- goto out_ro;
-
- finish_reservation(c);
- if (!allocated)
- mutex_unlock(&c->write_reserve_mutex);
- else
- kfree(data);
- return 0;
-
-out_release:
- release_head(c, DATAHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
-out_free:
- if (!allocated)
- mutex_unlock(&c->write_reserve_mutex);
- else
- kfree(data);
- return err;
-}
-
-/**
- * ubifs_jnl_write_inode - flush inode to the journal.
- * @c: UBIFS file-system description object
- * @inode: inode to flush
- *
- * This function writes inode @inode to the journal. If the inode is
- * synchronous, it also synchronizes the write-buffer. Returns zero in case of
- * success and a negative error code in case of failure.
- */
-int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
-{
- int err, lnum, offs;
- struct ubifs_ino_node *ino, *ino_start;
- struct ubifs_inode *ui = ubifs_inode(inode);
- int sync = 0, write_len = 0, ilen = UBIFS_INO_NODE_SZ;
- int last_reference = !inode->i_nlink;
- int kill_xattrs = ui->xattr_cnt && last_reference;
- u8 hash[UBIFS_HASH_ARR_SZ];
-
- dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
-
- /*
- * If the inode is being deleted, do not write the attached data. No
- * need to synchronize the write-buffer either.
- */
- if (!last_reference) {
- ilen += ui->data_len;
- sync = IS_SYNC(inode);
- } else if (kill_xattrs) {
- write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt;
- }
-
- if (ubifs_authenticated(c))
- write_len += ALIGN(ilen, 8) + ubifs_auth_node_sz(c);
- else
- write_len += ilen;
-
- ino_start = ino = kmalloc(write_len, GFP_NOFS);
- if (!ino)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, write_len);
- if (err)
- goto out_free;
-
- if (kill_xattrs) {
- union ubifs_key key;
- struct fscrypt_name nm = {0};
- struct inode *xino;
- struct ubifs_dent_node *xent, *pxent = NULL;
-
- if (ui->xattr_cnt > ubifs_xattr_max_cnt(c)) {
- err = -EPERM;
- ubifs_err(c, "Cannot delete inode, it has too much xattrs!");
- goto out_release;
- }
-
- lowest_xent_key(c, &key, inode->i_ino);
- while (1) {
- xent = ubifs_tnc_next_ent(c, &key, &nm);
- if (IS_ERR(xent)) {
- err = PTR_ERR(xent);
- if (err == -ENOENT)
- break;
-
- kfree(pxent);
- goto out_release;
- }
-
- fname_name(&nm) = xent->name;
- fname_len(&nm) = le16_to_cpu(xent->nlen);
-
- xino = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum));
- if (IS_ERR(xino)) {
- err = PTR_ERR(xino);
- ubifs_err(c, "dead directory entry '%s', error %d",
- xent->name, err);
- ubifs_ro_mode(c, err);
- kfree(pxent);
- kfree(xent);
- goto out_release;
- }
- ubifs_assert(c, ubifs_inode(xino)->xattr);
-
- clear_nlink(xino);
- pack_inode(c, ino, xino, 0);
- ino = (void *)ino + UBIFS_INO_NODE_SZ;
- iput(xino);
-
- kfree(pxent);
- pxent = xent;
- key_read(c, &xent->key, &key);
- }
- kfree(pxent);
- }
-
- pack_inode(c, ino, inode, 1);
- err = ubifs_node_calc_hash(c, ino, hash);
- if (err)
- goto out_release;
-
- err = write_head(c, BASEHD, ino_start, write_len, &lnum, &offs, sync);
- if (err)
- goto out_release;
- if (!sync)
- ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
- inode->i_ino);
- release_head(c, BASEHD);
-
- if (last_reference) {
- err = ubifs_tnc_remove_ino(c, inode->i_ino);
- if (err)
- goto out_ro;
- ubifs_delete_orphan(c, inode->i_ino);
- err = ubifs_add_dirt(c, lnum, write_len);
- } else {
- union ubifs_key key;
-
- ubifs_add_auth_dirt(c, lnum);
-
- ino_key_init(c, &key, inode->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
- }
- if (err)
- goto out_ro;
-
- finish_reservation(c);
- spin_lock(&ui->ui_lock);
- ui->synced_i_size = ui->ui_size;
- spin_unlock(&ui->ui_lock);
- kfree(ino_start);
- return 0;
-
-out_release:
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
-out_free:
- kfree(ino_start);
- return err;
-}
-
-/**
- * ubifs_jnl_delete_inode - delete an inode.
- * @c: UBIFS file-system description object
- * @inode: inode to delete
- *
- * This function deletes inode @inode which includes removing it from orphans,
- * deleting it from TNC and, in some cases, writing a deletion inode to the
- * journal.
- *
- * When regular file inodes are unlinked or a directory inode is removed, the
- * 'ubifs_jnl_update()' function writes a corresponding deletion inode and
- * direntry to the media, and adds the inode to orphans. After this, when the
- * last reference to this inode has been dropped, this function is called. In
- * general, it has to write one more deletion inode to the media, because if
- * a commit happened between 'ubifs_jnl_update()' and
- * 'ubifs_jnl_delete_inode()', the deletion inode is not in the journal
- * anymore, and in fact it might not be on the flash anymore, because it might
- * have been garbage-collected already. And for optimization reasons UBIFS does
- * not read the orphan area if it has been unmounted cleanly, so it would have
- * no indication in the journal that there is a deleted inode which has to be
- * removed from TNC.
- *
- * However, if there was no commit between 'ubifs_jnl_update()' and
- * 'ubifs_jnl_delete_inode()', then there is no need to write the deletion
- * inode to the media for the second time. And this is quite a typical case.
- *
- * This function returns zero in case of success and a negative error code in
- * case of failure.
- */
-int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
-{
- int err;
- struct ubifs_inode *ui = ubifs_inode(inode);
-
- ubifs_assert(c, inode->i_nlink == 0);
-
- if (ui->xattr_cnt || ui->del_cmtno != c->cmt_no)
- /* A commit happened for sure or inode hosts xattrs */
- return ubifs_jnl_write_inode(c, inode);
-
- down_read(&c->commit_sem);
- /*
- * Check commit number again, because the first test has been done
- * without @c->commit_sem, so a commit might have happened.
- */
- if (ui->del_cmtno != c->cmt_no) {
- up_read(&c->commit_sem);
- return ubifs_jnl_write_inode(c, inode);
- }
-
- err = ubifs_tnc_remove_ino(c, inode->i_ino);
- if (err)
- ubifs_ro_mode(c, err);
- else
- ubifs_delete_orphan(c, inode->i_ino);
- up_read(&c->commit_sem);
- return err;
-}
-
-/**
- * ubifs_jnl_xrename - cross rename two directory entries.
- * @c: UBIFS file-system description object
- * @fst_dir: parent inode of 1st directory entry to exchange
- * @fst_inode: 1st inode to exchange
- * @fst_nm: name of 1st inode to exchange
- * @snd_dir: parent inode of 2nd directory entry to exchange
- * @snd_inode: 2nd inode to exchange
- * @snd_nm: name of 2nd inode to exchange
- * @sync: non-zero if the write-buffer has to be synchronized
- *
- * This function implements the cross rename operation which may involve
- * writing 2 inodes and 2 directory entries. It marks the written inodes as clean
- * and returns zero on success. In case of failure, a negative error code is
- * returned.
- */
-int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
- const struct inode *fst_inode,
- const struct fscrypt_name *fst_nm,
- const struct inode *snd_dir,
- const struct inode *snd_inode,
- const struct fscrypt_name *snd_nm, int sync)
-{
- union ubifs_key key;
- struct ubifs_dent_node *dent1, *dent2;
- int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ;
- int aligned_dlen1, aligned_dlen2;
- int twoparents = (fst_dir != snd_dir);
- void *p;
- u8 hash_dent1[UBIFS_HASH_ARR_SZ];
- u8 hash_dent2[UBIFS_HASH_ARR_SZ];
- u8 hash_p1[UBIFS_HASH_ARR_SZ];
- u8 hash_p2[UBIFS_HASH_ARR_SZ];
-
- ubifs_assert(c, ubifs_inode(fst_dir)->data_len == 0);
- ubifs_assert(c, ubifs_inode(snd_dir)->data_len == 0);
- ubifs_assert(c, mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex));
- ubifs_assert(c, mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex));
-
- dlen1 = UBIFS_DENT_NODE_SZ + fname_len(snd_nm) + 1;
- dlen2 = UBIFS_DENT_NODE_SZ + fname_len(fst_nm) + 1;
- aligned_dlen1 = ALIGN(dlen1, 8);
- aligned_dlen2 = ALIGN(dlen2, 8);
-
- len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8);
- if (twoparents)
- len += plen;
-
- len += ubifs_auth_node_sz(c);
-
- dent1 = kzalloc(len, GFP_NOFS);
- if (!dent1)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, len);
- if (err)
- goto out_free;
-
- /* Make new dent for 1st entry */
- dent1->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, snd_nm);
- dent1->inum = cpu_to_le64(fst_inode->i_ino);
- dent1->type = get_dent_type(fst_inode->i_mode);
- dent1->nlen = cpu_to_le16(fname_len(snd_nm));
- memcpy(dent1->name, fname_name(snd_nm), fname_len(snd_nm));
- dent1->name[fname_len(snd_nm)] = '\0';
- set_dent_cookie(c, dent1);
- zero_dent_node_unused(dent1);
- ubifs_prep_grp_node(c, dent1, dlen1, 0);
- err = ubifs_node_calc_hash(c, dent1, hash_dent1);
- if (err)
- goto out_release;
-
- /* Make new dent for 2nd entry */
- dent2 = (void *)dent1 + aligned_dlen1;
- dent2->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, fst_nm);
- dent2->inum = cpu_to_le64(snd_inode->i_ino);
- dent2->type = get_dent_type(snd_inode->i_mode);
- dent2->nlen = cpu_to_le16(fname_len(fst_nm));
- memcpy(dent2->name, fname_name(fst_nm), fname_len(fst_nm));
- dent2->name[fname_len(fst_nm)] = '\0';
- set_dent_cookie(c, dent2);
- zero_dent_node_unused(dent2);
- ubifs_prep_grp_node(c, dent2, dlen2, 0);
- err = ubifs_node_calc_hash(c, dent2, hash_dent2);
- if (err)
- goto out_release;
-
- p = (void *)dent2 + aligned_dlen2;
- if (!twoparents) {
- pack_inode(c, p, fst_dir, 1);
- err = ubifs_node_calc_hash(c, p, hash_p1);
- if (err)
- goto out_release;
- } else {
- pack_inode(c, p, fst_dir, 0);
- err = ubifs_node_calc_hash(c, p, hash_p1);
- if (err)
- goto out_release;
- p += ALIGN(plen, 8);
- pack_inode(c, p, snd_dir, 1);
- err = ubifs_node_calc_hash(c, p, hash_p2);
- if (err)
- goto out_release;
- }
-
- err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
- if (err)
- goto out_release;
- if (!sync) {
- struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
-
- ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino);
- ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino);
- }
- release_head(c, BASEHD);
-
- ubifs_add_auth_dirt(c, lnum);
-
- dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
- err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
- if (err)
- goto out_ro;
-
- offs += aligned_dlen1;
- dent_key_init(c, &key, fst_dir->i_ino, fst_nm);
- err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, fst_nm);
- if (err)
- goto out_ro;
-
- offs += aligned_dlen2;
-
- ino_key_init(c, &key, fst_dir->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p1);
- if (err)
- goto out_ro;
-
- if (twoparents) {
- offs += ALIGN(plen, 8);
- ino_key_init(c, &key, snd_dir->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p2);
- if (err)
- goto out_ro;
- }
-
- finish_reservation(c);
-
- mark_inode_clean(c, ubifs_inode(fst_dir));
- if (twoparents)
- mark_inode_clean(c, ubifs_inode(snd_dir));
- kfree(dent1);
- return 0;
-
-out_release:
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
-out_free:
- kfree(dent1);
- return err;
-}
-
-/**
- * ubifs_jnl_rename - rename a directory entry.
- * @c: UBIFS file-system description object
- * @old_dir: parent inode of directory entry to rename
- * @old_inode: directory entry's inode to rename
- * @old_nm: name of the old directory entry to rename
- * @new_dir: parent inode of directory entry to rename
- * @new_inode: new directory entry's inode (or directory entry's inode to
- * replace)
- * @new_nm: new name of the new directory entry
- * @whiteout: whiteout inode
- * @sync: non-zero if the write-buffer has to be synchronized
- * @delete_orphan: indicates an orphan entry deletion for @whiteout
- *
- * This function implements the re-name operation which may involve writing up
- * to 4 inodes(new inode, whiteout inode, old and new parent directory inodes)
- * and 2 directory entries. It marks the written inodes as clean and returns
- * zero on success. In case of failure, a negative error code is returned.
- */
-int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
- const struct inode *old_inode,
- const struct fscrypt_name *old_nm,
- const struct inode *new_dir,
- const struct inode *new_inode,
- const struct fscrypt_name *new_nm,
- const struct inode *whiteout, int sync, int delete_orphan)
-{
- void *p;
- union ubifs_key key;
- struct ubifs_dent_node *dent, *dent2;
- int err, dlen1, dlen2, ilen, wlen, lnum, offs, len, orphan_added = 0;
- int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ;
- int last_reference = !!(new_inode && new_inode->i_nlink == 0);
- int move = (old_dir != new_dir);
- struct ubifs_inode *new_ui, *whiteout_ui;
- u8 hash_old_dir[UBIFS_HASH_ARR_SZ];
- u8 hash_new_dir[UBIFS_HASH_ARR_SZ];
- u8 hash_new_inode[UBIFS_HASH_ARR_SZ];
- u8 hash_whiteout_inode[UBIFS_HASH_ARR_SZ];
- u8 hash_dent1[UBIFS_HASH_ARR_SZ];
- u8 hash_dent2[UBIFS_HASH_ARR_SZ];
-
- ubifs_assert(c, ubifs_inode(old_dir)->data_len == 0);
- ubifs_assert(c, ubifs_inode(new_dir)->data_len == 0);
- ubifs_assert(c, mutex_is_locked(&ubifs_inode(old_dir)->ui_mutex));
- ubifs_assert(c, mutex_is_locked(&ubifs_inode(new_dir)->ui_mutex));
-
- dlen1 = UBIFS_DENT_NODE_SZ + fname_len(new_nm) + 1;
- dlen2 = UBIFS_DENT_NODE_SZ + fname_len(old_nm) + 1;
- if (new_inode) {
- new_ui = ubifs_inode(new_inode);
- ubifs_assert(c, mutex_is_locked(&new_ui->ui_mutex));
- ilen = UBIFS_INO_NODE_SZ;
- if (!last_reference)
- ilen += new_ui->data_len;
- } else
- ilen = 0;
-
- if (whiteout) {
- whiteout_ui = ubifs_inode(whiteout);
- ubifs_assert(c, mutex_is_locked(&whiteout_ui->ui_mutex));
- ubifs_assert(c, whiteout->i_nlink == 1);
- ubifs_assert(c, !whiteout_ui->dirty);
- wlen = UBIFS_INO_NODE_SZ;
- wlen += whiteout_ui->data_len;
- } else
- wlen = 0;
-
- aligned_dlen1 = ALIGN(dlen1, 8);
- aligned_dlen2 = ALIGN(dlen2, 8);
- len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) +
- ALIGN(wlen, 8) + ALIGN(plen, 8);
- if (move)
- len += plen;
-
- len += ubifs_auth_node_sz(c);
-
- dent = kzalloc(len, GFP_NOFS);
- if (!dent)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, len);
- if (err)
- goto out_free;
-
- /* Make new dent */
- dent->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent->key, new_dir->i_ino, new_nm);
- dent->inum = cpu_to_le64(old_inode->i_ino);
- dent->type = get_dent_type(old_inode->i_mode);
- dent->nlen = cpu_to_le16(fname_len(new_nm));
- memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
- dent->name[fname_len(new_nm)] = '\0';
- set_dent_cookie(c, dent);
- zero_dent_node_unused(dent);
- ubifs_prep_grp_node(c, dent, dlen1, 0);
- err = ubifs_node_calc_hash(c, dent, hash_dent1);
- if (err)
- goto out_release;
-
- dent2 = (void *)dent + aligned_dlen1;
- dent2->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent2->key, old_dir->i_ino, old_nm);
-
- if (whiteout) {
- dent2->inum = cpu_to_le64(whiteout->i_ino);
- dent2->type = get_dent_type(whiteout->i_mode);
- } else {
- /* Make deletion dent */
- dent2->inum = 0;
- dent2->type = DT_UNKNOWN;
- }
- dent2->nlen = cpu_to_le16(fname_len(old_nm));
- memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
- dent2->name[fname_len(old_nm)] = '\0';
- set_dent_cookie(c, dent2);
- zero_dent_node_unused(dent2);
- ubifs_prep_grp_node(c, dent2, dlen2, 0);
- err = ubifs_node_calc_hash(c, dent2, hash_dent2);
- if (err)
- goto out_release;
-
- p = (void *)dent2 + aligned_dlen2;
- if (new_inode) {
- pack_inode(c, p, new_inode, 0);
- err = ubifs_node_calc_hash(c, p, hash_new_inode);
- if (err)
- goto out_release;
-
- p += ALIGN(ilen, 8);
- }
-
- if (whiteout) {
- pack_inode(c, p, whiteout, 0);
- err = ubifs_node_calc_hash(c, p, hash_whiteout_inode);
- if (err)
- goto out_release;
-
- p += ALIGN(wlen, 8);
- }
-
- if (!move) {
- pack_inode(c, p, old_dir, 1);
- err = ubifs_node_calc_hash(c, p, hash_old_dir);
- if (err)
- goto out_release;
- } else {
- pack_inode(c, p, old_dir, 0);
- err = ubifs_node_calc_hash(c, p, hash_old_dir);
- if (err)
- goto out_release;
-
- p += ALIGN(plen, 8);
- pack_inode(c, p, new_dir, 1);
- err = ubifs_node_calc_hash(c, p, hash_new_dir);
- if (err)
- goto out_release;
- }
-
- if (last_reference) {
- err = ubifs_add_orphan(c, new_inode->i_ino);
- if (err) {
- release_head(c, BASEHD);
- goto out_finish;
- }
- new_ui->del_cmtno = c->cmt_no;
- orphan_added = 1;
- }
-
- err = write_head(c, BASEHD, dent, len, &lnum, &offs, sync);
- if (err)
- goto out_release;
- if (!sync) {
- struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
-
- ubifs_wbuf_add_ino_nolock(wbuf, new_dir->i_ino);
- ubifs_wbuf_add_ino_nolock(wbuf, old_dir->i_ino);
- if (new_inode)
- ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
- new_inode->i_ino);
- if (whiteout)
- ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
- whiteout->i_ino);
- }
- release_head(c, BASEHD);
-
- ubifs_add_auth_dirt(c, lnum);
-
- dent_key_init(c, &key, new_dir->i_ino, new_nm);
- err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
- if (err)
- goto out_ro;
-
- offs += aligned_dlen1;
- if (whiteout) {
- dent_key_init(c, &key, old_dir->i_ino, old_nm);
- err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
- if (err)
- goto out_ro;
- } else {
- err = ubifs_add_dirt(c, lnum, dlen2);
- if (err)
- goto out_ro;
-
- dent_key_init(c, &key, old_dir->i_ino, old_nm);
- err = ubifs_tnc_remove_nm(c, &key, old_nm);
- if (err)
- goto out_ro;
- }
-
- offs += aligned_dlen2;
- if (new_inode) {
- ino_key_init(c, &key, new_inode->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash_new_inode);
- if (err)
- goto out_ro;
- offs += ALIGN(ilen, 8);
- }
-
- if (whiteout) {
- ino_key_init(c, &key, whiteout->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, wlen,
- hash_whiteout_inode);
- if (err)
- goto out_ro;
- offs += ALIGN(wlen, 8);
- }
-
- ino_key_init(c, &key, old_dir->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
- if (err)
- goto out_ro;
-
- if (move) {
- offs += ALIGN(plen, 8);
- ino_key_init(c, &key, new_dir->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_new_dir);
- if (err)
- goto out_ro;
- }
-
- if (delete_orphan)
- ubifs_delete_orphan(c, whiteout->i_ino);
-
- finish_reservation(c);
- if (new_inode) {
- mark_inode_clean(c, new_ui);
- spin_lock(&new_ui->ui_lock);
- new_ui->synced_i_size = new_ui->ui_size;
- spin_unlock(&new_ui->ui_lock);
- }
- /*
- * No need to mark whiteout inode clean.
- * Whiteout doesn't have non-zero size, no need to update
- * synced_i_size for whiteout_ui.
- */
- mark_inode_clean(c, ubifs_inode(old_dir));
- if (move)
- mark_inode_clean(c, ubifs_inode(new_dir));
- kfree(dent);
- return 0;
-
-out_release:
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- if (orphan_added)
- ubifs_delete_orphan(c, new_inode->i_ino);
-out_finish:
- finish_reservation(c);
-out_free:
- kfree(dent);
- return err;
-}
-
-/**
- * truncate_data_node - re-compress/encrypt a truncated data node.
- * @c: UBIFS file-system description object
- * @inode: inode which refers to the data node
- * @block: data block number
- * @dn: data node to re-compress
- * @new_len: new length
- * @dn_size: size of the data node @dn in memory
- *
- * This function is used when an inode is truncated and the last data node of
- * the inode has to be re-compressed/encrypted and re-written.
- */
-static int truncate_data_node(const struct ubifs_info *c, const struct inode *inode,
- unsigned int block, struct ubifs_data_node *dn,
- int *new_len, int dn_size)
-{
- void *buf;
- int err, dlen, compr_type, out_len, data_size;
-
- out_len = le32_to_cpu(dn->size);
- buf = kmalloc_array(out_len, WORST_COMPR_FACTOR, GFP_NOFS);
- if (!buf)
- return -ENOMEM;
-
- dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
- data_size = dn_size - UBIFS_DATA_NODE_SZ;
- compr_type = le16_to_cpu(dn->compr_type);
-
- if (IS_ENCRYPTED(inode)) {
- err = ubifs_decrypt(inode, dn, &dlen, block);
- if (err)
- goto out;
- }
-
- if (compr_type == UBIFS_COMPR_NONE) {
- out_len = *new_len;
- } else {
- err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
- if (err)
- goto out;
-
- ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
- }
-
- if (IS_ENCRYPTED(inode)) {
- err = ubifs_encrypt(inode, dn, out_len, &data_size, block);
- if (err)
- goto out;
-
- out_len = data_size;
- } else {
- dn->compr_size = 0;
- }
-
- ubifs_assert(c, out_len <= UBIFS_BLOCK_SIZE);
- dn->compr_type = cpu_to_le16(compr_type);
- dn->size = cpu_to_le32(*new_len);
- *new_len = UBIFS_DATA_NODE_SZ + out_len;
- err = 0;
-out:
- kfree(buf);
- return err;
-}
-
-/**
- * ubifs_jnl_truncate - update the journal for a truncation.
- * @c: UBIFS file-system description object
- * @inode: inode to truncate
- * @old_size: old size
- * @new_size: new size
- *
- * When the size of a file decreases due to truncation, a truncation node is
- * written, the journal tree is updated, and the last data block is re-written
- * if it has been affected. The inode is also updated in order to synchronize
- * the new inode size.
- *
- * This function marks the inode as clean and returns zero on success. In case
- * of failure, a negative error code is returned.
- */
-int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
- loff_t old_size, loff_t new_size)
-{
- union ubifs_key key, to_key;
- struct ubifs_ino_node *ino;
- struct ubifs_trun_node *trun;
- struct ubifs_data_node *dn;
- int err, dlen, len, lnum, offs, bit, sz, sync = IS_SYNC(inode);
- int dn_size;
- struct ubifs_inode *ui = ubifs_inode(inode);
- ino_t inum = inode->i_ino;
- unsigned int blk;
- u8 hash_ino[UBIFS_HASH_ARR_SZ];
- u8 hash_dn[UBIFS_HASH_ARR_SZ];
-
- dbg_jnl("ino %lu, size %lld -> %lld",
- (unsigned long)inum, old_size, new_size);
- ubifs_assert(c, !ui->data_len);
- ubifs_assert(c, S_ISREG(inode->i_mode));
- ubifs_assert(c, mutex_is_locked(&ui->ui_mutex));
-
- dn_size = COMPRESSED_DATA_NODE_BUF_SZ;
-
- if (IS_ENCRYPTED(inode))
- dn_size += UBIFS_CIPHER_BLOCK_SIZE;
-
- sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
- dn_size + ubifs_auth_node_sz(c);
-
- ino = kmalloc(sz, GFP_NOFS);
- if (!ino)
- return -ENOMEM;
-
- trun = (void *)ino + UBIFS_INO_NODE_SZ;
- trun->ch.node_type = UBIFS_TRUN_NODE;
- trun->inum = cpu_to_le32(inum);
- trun->old_size = cpu_to_le64(old_size);
- trun->new_size = cpu_to_le64(new_size);
- zero_trun_node_unused(trun);
-
- dlen = new_size & (UBIFS_BLOCK_SIZE - 1);
- if (dlen) {
- /* Get last data block so it can be truncated */
- dn = (void *)trun + UBIFS_TRUN_NODE_SZ;
- blk = new_size >> UBIFS_BLOCK_SHIFT;
- data_key_init(c, &key, inum, blk);
- dbg_jnlk(&key, "last block key ");
- err = ubifs_tnc_lookup(c, &key, dn);
- if (err == -ENOENT)
- dlen = 0; /* Not found (so it is a hole) */
- else if (err)
- goto out_free;
- else {
- int dn_len = le32_to_cpu(dn->size);
-
- if (dn_len <= 0 || dn_len > UBIFS_BLOCK_SIZE) {
- ubifs_err(c, "bad data node (block %u, inode %lu)",
- blk, inode->i_ino);
- ubifs_dump_node(c, dn, dn_size);
- err = -EUCLEAN;
- goto out_free;
- }
-
- if (dn_len <= dlen)
- dlen = 0; /* Nothing to do */
- else {
- err = truncate_data_node(c, inode, blk, dn,
- &dlen, dn_size);
- if (err)
- goto out_free;
- }
- }
- }
-
- /* Must make reservation before allocating sequence numbers */
- len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
-
- if (ubifs_authenticated(c))
- len += ALIGN(dlen, 8) + ubifs_auth_node_sz(c);
- else
- len += dlen;
-
- err = make_reservation(c, BASEHD, len);
- if (err)
- goto out_free;
-
- pack_inode(c, ino, inode, 0);
- err = ubifs_node_calc_hash(c, ino, hash_ino);
- if (err)
- goto out_release;
-
- ubifs_prep_grp_node(c, trun, UBIFS_TRUN_NODE_SZ, dlen ? 0 : 1);
- if (dlen) {
- ubifs_prep_grp_node(c, dn, dlen, 1);
- err = ubifs_node_calc_hash(c, dn, hash_dn);
- if (err)
- goto out_release;
- }
-
- err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
- if (err)
- goto out_release;
- if (!sync)
- ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
- release_head(c, BASEHD);
-
- ubifs_add_auth_dirt(c, lnum);
-
- if (dlen) {
- sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
- err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
- if (err)
- goto out_ro;
- }
-
- ino_key_init(c, &key, inum);
- err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ, hash_ino);
- if (err)
- goto out_ro;
-
- err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ);
- if (err)
- goto out_ro;
-
- bit = new_size & (UBIFS_BLOCK_SIZE - 1);
- blk = (new_size >> UBIFS_BLOCK_SHIFT) + (bit ? 1 : 0);
- data_key_init(c, &key, inum, blk);
-
- bit = old_size & (UBIFS_BLOCK_SIZE - 1);
- blk = (old_size >> UBIFS_BLOCK_SHIFT) - (bit ? 0 : 1);
- data_key_init(c, &to_key, inum, blk);
-
- err = ubifs_tnc_remove_range(c, &key, &to_key);
- if (err)
- goto out_ro;
-
- finish_reservation(c);
- spin_lock(&ui->ui_lock);
- ui->synced_i_size = ui->ui_size;
- spin_unlock(&ui->ui_lock);
- mark_inode_clean(c, ui);
- kfree(ino);
- return 0;
-
-out_release:
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
-out_free:
- kfree(ino);
- return err;
-}
-
-
-/**
- * ubifs_jnl_delete_xattr - delete an extended attribute.
- * @c: UBIFS file-system description object
- * @host: host inode
- * @inode: extended attribute inode
- * @nm: extended attribute entry name
- *
- * This function delete an extended attribute which is very similar to
- * un-linking regular files - it writes a deletion xentry, a deletion inode and
- * updates the target inode. Returns zero in case of success and a negative
- * error code in case of failure.
- */
-int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
- const struct inode *inode,
- const struct fscrypt_name *nm)
-{
- int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, write_len;
- struct ubifs_dent_node *xent;
- struct ubifs_ino_node *ino;
- union ubifs_key xent_key, key1, key2;
- int sync = IS_DIRSYNC(host);
- struct ubifs_inode *host_ui = ubifs_inode(host);
- u8 hash[UBIFS_HASH_ARR_SZ];
-
- ubifs_assert(c, inode->i_nlink == 0);
- ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
-
- /*
- * Since we are deleting the inode, we do not bother to attach any data
- * to it and assume its length is %UBIFS_INO_NODE_SZ.
- */
- xlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
- aligned_xlen = ALIGN(xlen, 8);
- hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
- len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
-
- write_len = len + ubifs_auth_node_sz(c);
-
- xent = kzalloc(write_len, GFP_NOFS);
- if (!xent)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, write_len);
- if (err) {
- kfree(xent);
- return err;
- }
-
- xent->ch.node_type = UBIFS_XENT_NODE;
- xent_key_init(c, &xent_key, host->i_ino, nm);
- key_write(c, &xent_key, xent->key);
- xent->inum = 0;
- xent->type = get_dent_type(inode->i_mode);
- xent->nlen = cpu_to_le16(fname_len(nm));
- memcpy(xent->name, fname_name(nm), fname_len(nm));
- xent->name[fname_len(nm)] = '\0';
- zero_dent_node_unused(xent);
- ubifs_prep_grp_node(c, xent, xlen, 0);
-
- ino = (void *)xent + aligned_xlen;
- pack_inode(c, ino, inode, 0);
- ino = (void *)ino + UBIFS_INO_NODE_SZ;
- pack_inode(c, ino, host, 1);
- err = ubifs_node_calc_hash(c, ino, hash);
- if (err)
- goto out_release;
-
- err = write_head(c, BASEHD, xent, write_len, &lnum, &xent_offs, sync);
- if (!sync && !err)
- ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
- release_head(c, BASEHD);
-
- ubifs_add_auth_dirt(c, lnum);
- kfree(xent);
- if (err)
- goto out_ro;
-
- /* Remove the extended attribute entry from TNC */
- err = ubifs_tnc_remove_nm(c, &xent_key, nm);
- if (err)
- goto out_ro;
- err = ubifs_add_dirt(c, lnum, xlen);
- if (err)
- goto out_ro;
-
- /*
- * Remove all nodes belonging to the extended attribute inode from TNC.
- * Well, there actually must be only one node - the inode itself.
- */
- lowest_ino_key(c, &key1, inode->i_ino);
- highest_ino_key(c, &key2, inode->i_ino);
- err = ubifs_tnc_remove_range(c, &key1, &key2);
- if (err)
- goto out_ro;
- err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ);
- if (err)
- goto out_ro;
-
- /* And update TNC with the new host inode position */
- ino_key_init(c, &key1, host->i_ino);
- err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen, hash);
- if (err)
- goto out_ro;
-
- finish_reservation(c);
- spin_lock(&host_ui->ui_lock);
- host_ui->synced_i_size = host_ui->ui_size;
- spin_unlock(&host_ui->ui_lock);
- mark_inode_clean(c, host_ui);
- return 0;
-
-out_release:
- kfree(xent);
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
- return err;
-}
-
-/**
- * ubifs_jnl_change_xattr - change an extended attribute.
- * @c: UBIFS file-system description object
- * @inode: extended attribute inode
- * @host: host inode
- *
- * This function writes the updated version of an extended attribute inode and
- * the host inode to the journal (to the base head). The host inode is written
- * after the extended attribute inode in order to guarantee that the extended
- * attribute will be flushed when the inode is synchronized by 'fsync()' and
- * consequently, the write-buffer is synchronized. This function returns zero
- * in case of success and a negative error code in case of failure.
- */
-int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
- const struct inode *host)
-{
- int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
- struct ubifs_inode *host_ui = ubifs_inode(host);
- struct ubifs_ino_node *ino;
- union ubifs_key key;
- int sync = IS_DIRSYNC(host);
- u8 hash_host[UBIFS_HASH_ARR_SZ];
- u8 hash[UBIFS_HASH_ARR_SZ];
-
- dbg_jnl("ino %lu, ino %lu", host->i_ino, inode->i_ino);
- ubifs_assert(c, inode->i_nlink > 0);
- ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
-
- len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
- len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
- aligned_len1 = ALIGN(len1, 8);
- aligned_len = aligned_len1 + ALIGN(len2, 8);
-
- aligned_len += ubifs_auth_node_sz(c);
-
- ino = kzalloc(aligned_len, GFP_NOFS);
- if (!ino)
- return -ENOMEM;
-
- /* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, aligned_len);
- if (err)
- goto out_free;
-
- pack_inode(c, ino, host, 0);
- err = ubifs_node_calc_hash(c, ino, hash_host);
- if (err)
- goto out_release;
- pack_inode(c, (void *)ino + aligned_len1, inode, 1);
- err = ubifs_node_calc_hash(c, (void *)ino + aligned_len1, hash);
- if (err)
- goto out_release;
-
- err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0);
- if (!sync && !err) {
- struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
-
- ubifs_wbuf_add_ino_nolock(wbuf, host->i_ino);
- ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
- }
- release_head(c, BASEHD);
- if (err)
- goto out_ro;
-
- ubifs_add_auth_dirt(c, lnum);
-
- ino_key_init(c, &key, host->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
- if (err)
- goto out_ro;
-
- ino_key_init(c, &key, inode->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2, hash);
- if (err)
- goto out_ro;
-
- finish_reservation(c);
- spin_lock(&host_ui->ui_lock);
- host_ui->synced_i_size = host_ui->ui_size;
- spin_unlock(&host_ui->ui_lock);
- mark_inode_clean(c, host_ui);
- kfree(ino);
- return 0;
-
-out_release:
- release_head(c, BASEHD);
-out_ro:
- ubifs_ro_mode(c, err);
- finish_reservation(c);
-out_free:
- kfree(ino);
- return err;
-}
-
--
2.13.6
This is a preparation for adding LPT checking support. Move some data
structures and functions into check_space.c, also factor out some common
functions in libubifs:
1. Move 'lpts' from rebuild module, make it resuable for non-rebuild_fs
modes.
2. Move function 'get_free_leb' from rebuild_fs.c, it could be reused in
building LPT.
3. Move function 'build_lpt' from rebuild_fs.c, it could be reused in
building LPT.
4. Factor out lpt nodes freeing into a new function ubifs_free_lpt_nodes.
5. Factor out nnode dirty marking implementations into a new function
ubifs_make_nnode_dirty.
5. Export the function of nnode number calculation, calc_nnode_num is
renamed as ubifs_calc_nnode_num.
6. Export the function of making pnode dirty, do_make_pnode_dirty is
renamed as ubifs_make_pnode_dirty.
7. Rename next_pnode_to_dirty to ubifs_find_next_pnode and export it.
8. Export free_buds and expend its parameters.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/check_space.c | 94 ++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 11 +++-
ubifs-utils/fsck.ubifs/rebuild_fs.c | 124 +++++++++--------------------------
ubifs-utils/libubifs/lpt.c | 6 +-
ubifs-utils/libubifs/lpt_commit.c | 97 ++++++++++++++-------------
ubifs-utils/libubifs/super.c | 9 ++-
ubifs-utils/libubifs/ubifs.h | 7 ++
8 files changed, 204 insertions(+), 147 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/check_space.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index e12052ce..f63ca7a2 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -84,7 +84,8 @@ fsck_ubifs_SOURCES = \
ubifs-utils/fsck.ubifs/load_fs.c \
ubifs-utils/fsck.ubifs/extract_files.c \
ubifs-utils/fsck.ubifs/rebuild_fs.c \
- ubifs-utils/fsck.ubifs/check_files.c
+ ubifs-utils/fsck.ubifs/check_files.c \
+ ubifs-utils/fsck.ubifs/check_space.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/check_space.c b/ubifs-utils/fsck.ubifs/check_space.c
new file mode 100644
index 00000000..f758bf1a
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/check_space.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+/**
+ * get_free_leb - get a free LEB according to @FSCK(c)->used_lebs.
+ * @c: UBIFS file-system description object
+ *
+ * This function tries to find a free LEB, lnum is returned if found, otherwise
+ * %-ENOSPC is returned.
+ */
+int get_free_leb(struct ubifs_info *c)
+{
+ int lnum;
+
+ lnum = find_next_zero_bit(FSCK(c)->used_lebs, c->main_lebs, 0);
+ if (lnum >= c->main_lebs) {
+ ubifs_err(c, "No space left.");
+ return -ENOSPC;
+ }
+ set_bit(lnum, FSCK(c)->used_lebs);
+ lnum += c->main_first;
+
+ return lnum;
+}
+
+/**
+ * build_lpt - construct LPT and write it into flash.
+ * @c: UBIFS file-system description object
+ * @calculate_lp_cb: callback function to calculate the properties for given LEB
+ *
+ * This function builds LPT according to the calculated results by
+ * @calculate_lp_cb and writes LPT into flash. Returns zero in case of success,
+ * a negative error code in case of failure.
+ */
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
+{
+ int i, err, lnum, free, dirty;
+ u8 hash_lpt[UBIFS_HASH_ARR_SZ];
+
+ memset(&c->lst, 0, sizeof(struct ubifs_lp_stats));
+ /* Set gc lnum. */
+ lnum = get_free_leb(c);
+ if (lnum < 0)
+ return lnum;
+ c->gc_lnum = lnum;
+
+ /* Update LPT. */
+ for (i = 0; i < c->main_lebs; i++) {
+ err = calculate_lp_cb(c, i, &free, &dirty);
+ if (err)
+ return err;
+
+ FSCK(c)->lpts[i].free = free;
+ FSCK(c)->lpts[i].dirty = dirty;
+ c->lst.total_free += free;
+ c->lst.total_dirty += dirty;
+
+ if (free == c->leb_size)
+ c->lst.empty_lebs++;
+
+ if (FSCK(c)->lpts[i].flags & LPROPS_INDEX) {
+ c->lst.idx_lebs += 1;
+ } else {
+ int spc;
+
+ spc = free + dirty;
+ if (spc < c->dead_wm)
+ c->lst.total_dead += spc;
+ else
+ c->lst.total_dark += ubifs_calc_dark(c, spc);
+ c->lst.total_used += c->leb_size - spc;
+ }
+ }
+
+ /* Write LPT. */
+ return ubifs_create_lpt(c, FSCK(c)->lpts, c->main_lebs, hash_lpt);
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 663d5dcf..011835ff 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -48,6 +48,9 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
+typedef int (*calculate_lp_callback)(struct ubifs_info *c,
+ int index, int *free, int *dirty);
+
struct scanned_file;
/**
@@ -199,14 +202,12 @@ struct invalid_file_problem {
/**
* ubifs_rebuild_info - UBIFS rebuilding information.
- * @lpts: lprops table
* @write_buf: write buffer for LEB @head_lnum
* @head_lnum: current writing LEB number
* @head_offs: current writing position in LEB @head_lnum
* @need_update_lpt: whether to update lpt while writing index nodes
*/
struct ubifs_rebuild_info {
- struct ubifs_lprops *lpts;
void *write_buf;
int head_lnum;
int head_offs;
@@ -222,6 +223,7 @@ struct ubifs_rebuild_info {
* @scanned_files: tree of all scanned files
* @used_lebs: a bitmap used for recording used lebs
* @disconnected_files: regular files without dentries
+ * @lpts: lprops table
* @try_rebuild: %true means that try to rebuild fs when fsck failed
* @rebuild: rebuilding-related information
*/
@@ -232,6 +234,7 @@ struct ubifs_fsck_info {
struct rb_root scanned_files;
unsigned long *used_lebs;
struct list_head disconnected_files;
+ struct ubifs_lprops *lpts;
bool try_rebuild;
struct ubifs_rebuild_info *rebuild;
};
@@ -322,4 +325,8 @@ int handle_invalid_files(struct ubifs_info *c);
int handle_dentry_tree(struct ubifs_info *c);
bool tnc_is_empty(struct ubifs_info *c);
+/* check_space.c */
+int get_free_leb(struct ubifs_info *c);
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 8fc78ce3..1161f5af 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -81,9 +81,9 @@ static int init_rebuild_info(struct ubifs_info *c)
log_err(c, errno, "can not allocate bitmap of used lebs");
goto free_rebuild;
}
- FSCK(c)->rebuild->lpts = kzalloc(sizeof(struct ubifs_lprops) * c->main_lebs,
- GFP_KERNEL);
- if (!FSCK(c)->rebuild->lpts) {
+ FSCK(c)->lpts = kzalloc(sizeof(struct ubifs_lprops) * c->main_lebs,
+ GFP_KERNEL);
+ if (!FSCK(c)->lpts) {
err = -ENOMEM;
log_err(c, errno, "can not allocate lpts");
goto free_used_lebs;
@@ -98,7 +98,7 @@ static int init_rebuild_info(struct ubifs_info *c)
return 0;
free_lpts:
- kfree(FSCK(c)->rebuild->lpts);
+ kfree(FSCK(c)->lpts);
free_used_lebs:
kfree(FSCK(c)->used_lebs);
free_rebuild:
@@ -111,7 +111,7 @@ free_sbuf:
static void destroy_rebuild_info(struct ubifs_info *c)
{
vfree(FSCK(c)->rebuild->write_buf);
- kfree(FSCK(c)->rebuild->lpts);
+ kfree(FSCK(c)->lpts);
kfree(FSCK(c)->used_lebs);
kfree(FSCK(c)->rebuild);
vfree(c->sbuf);
@@ -496,13 +496,12 @@ static void update_lpt(struct ubifs_info *c, struct scanned_node *sn,
int pos = sn->offs + ALIGN(sn->len, 8);
set_bit(index, FSCK(c)->used_lebs);
- FSCK(c)->rebuild->lpts[index].end = max_t(int,
- FSCK(c)->rebuild->lpts[index].end, pos);
+ FSCK(c)->lpts[index].end = max_t(int, FSCK(c)->lpts[index].end, pos);
if (deleted)
return;
- FSCK(c)->rebuild->lpts[index].used += ALIGN(sn->len, 8);
+ FSCK(c)->lpts[index].used += ALIGN(sn->len, 8);
}
/**
@@ -731,28 +730,6 @@ static void init_root_ino(struct ubifs_info *c, struct ubifs_ino_node *ino)
}
/**
- * get_free_leb - get a free LEB according to @FSCK(c)->used_lebs.
- * @c: UBIFS file-system description object
- *
- * This function tries to find a free LEB, lnum is returned if found, otherwise
- * %-ENOSPC is returned.
- */
-static int get_free_leb(struct ubifs_info *c)
-{
- int lnum;
-
- lnum = find_next_zero_bit(FSCK(c)->used_lebs, c->main_lebs, 0);
- if (lnum >= c->main_lebs) {
- ubifs_err(c, "No space left.");
- return -ENOSPC;
- }
- set_bit(lnum, FSCK(c)->used_lebs);
- lnum += c->main_first;
-
- return lnum;
-}
-
-/**
* flush_write_buf - flush write buffer.
* @c: UBIFS file-system description object
*
@@ -780,9 +757,9 @@ static int flush_write_buf(struct ubifs_info *c)
if (FSCK(c)->rebuild->need_update_lpt) {
int index = FSCK(c)->rebuild->head_lnum - c->main_first;
- FSCK(c)->rebuild->lpts[index].free = c->leb_size - len;
- FSCK(c)->rebuild->lpts[index].dirty = pad;
- FSCK(c)->rebuild->lpts[index].flags = LPROPS_INDEX;
+ FSCK(c)->lpts[index].free = c->leb_size - len;
+ FSCK(c)->lpts[index].dirty = pad;
+ FSCK(c)->lpts[index].flags = LPROPS_INDEX;
}
FSCK(c)->rebuild->head_lnum = -1;
@@ -1220,7 +1197,7 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
lnum = i + c->main_first;
dbg_fsck("re-write LEB %d, in %s", lnum, c->dev_name);
- end = FSCK(c)->rebuild->lpts[i].end;
+ end = FSCK(c)->lpts[i].end;
len = ALIGN(end, c->min_io_size);
err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 0);
@@ -1247,69 +1224,28 @@ out_idx_list:
return err;
}
-/**
- * build_lpt - construct LPT and write it into flash.
- * @c: UBIFS file-system description object
- *
- * This function builds LPT according to @FSCK(c)->rebuild->lpts and writes
- * LPT into flash.
- */
-static int build_lpt(struct ubifs_info *c)
+static int calculate_lp(struct ubifs_info *c, int index, int *free, int *dirty)
{
- int i, len, free, dirty, lnum;
- u8 hash_lpt[UBIFS_HASH_ARR_SZ];
-
- memset(&c->lst, 0, sizeof(struct ubifs_lp_stats));
- /* Set gc lnum. */
- lnum = get_free_leb(c);
- if (lnum < 0)
- return lnum;
- c->gc_lnum = lnum;
-
- /* Update LPT. */
- for (i = 0; i < c->main_lebs; i++) {
- if (!test_bit(i, FSCK(c)->used_lebs) ||
- c->gc_lnum == i + c->main_first) {
- free = c->leb_size;
- dirty = 0;
- } else if (FSCK(c)->rebuild->lpts[i].flags & LPROPS_INDEX) {
- free = FSCK(c)->rebuild->lpts[i].free;
- dirty = FSCK(c)->rebuild->lpts[i].dirty;
- } else {
- len = ALIGN(FSCK(c)->rebuild->lpts[i].end, c->min_io_size);
- free = c->leb_size - len;
- dirty = len - FSCK(c)->rebuild->lpts[i].used;
-
- if (dirty == c->leb_size) {
- free = c->leb_size;
- dirty = 0;
- }
- }
-
- FSCK(c)->rebuild->lpts[i].free = free;
- FSCK(c)->rebuild->lpts[i].dirty = dirty;
- c->lst.total_free += free;
- c->lst.total_dirty += dirty;
-
- if (free == c->leb_size)
- c->lst.empty_lebs++;
-
- if (!(FSCK(c)->rebuild->lpts[i].flags & LPROPS_INDEX)) {
- int spc;
-
- spc = free + dirty;
- if (spc < c->dead_wm)
- c->lst.total_dead += spc;
- else
- c->lst.total_dark += ubifs_calc_dark(c, spc);
- c->lst.total_used += c->leb_size - spc;
- } else {
- c->lst.idx_lebs += 1;
+ if (!test_bit(index, FSCK(c)->used_lebs) ||
+ c->gc_lnum == index + c->main_first) {
+ *free = c->leb_size;
+ *dirty = 0;
+ } else if (FSCK(c)->lpts[index].flags & LPROPS_INDEX) {
+ *free = FSCK(c)->lpts[index].free;
+ *dirty = FSCK(c)->lpts[index].dirty;
+ } else {
+ int len = ALIGN(FSCK(c)->lpts[index].end, c->min_io_size);
+
+ *free = c->leb_size - len;
+ *dirty = len - FSCK(c)->lpts[index].used;
+
+ if (*dirty == c->leb_size) {
+ *free = c->leb_size;
+ *dirty = 0;
}
}
- /* Write LPT. */
- return ubifs_create_lpt(c, FSCK(c)->rebuild->lpts, c->main_lebs, hash_lpt);
+ return 0;
}
/**
@@ -1485,7 +1421,7 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 10. Build LPT. */
log_out(c, "Build LPT");
- err = build_lpt(c);
+ err = build_lpt(c, calculate_lp);
if (err) {
exit_code |= FSCK_ERROR;
goto out;
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index b07f1f77..fc70cad5 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -523,7 +523,7 @@ static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
}
/**
- * calc_nnode_num - calculate nnode number.
+ * ubifs_calc_nnode_num - calculate nnode number.
* @row: the row in the tree (root is zero)
* @col: the column in the row (leftmost is zero)
*
@@ -533,7 +533,7 @@ static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
* This function calculates and returns the nnode number for the nnode at @row
* and @col.
*/
-static int calc_nnode_num(int row, int col)
+int ubifs_calc_nnode_num(int row, int col)
{
int num, bits;
@@ -779,7 +779,7 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
nnode->nbranch[j].offs = 0;
}
}
- nnode->num = calc_nnode_num(row, i);
+ nnode->num = ubifs_calc_nnode_num(row, i);
ubifs_pack_nnode(c, p, nnode);
p += c->nnode_sz;
len += c->nnode_sz;
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index 43eb7a6a..8a44546d 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -545,16 +545,15 @@ no_space:
}
/**
- * next_pnode_to_dirty - find next pnode to dirty.
+ * ubifs_find_next_pnode - find next pnode.
* @c: UBIFS file-system description object
* @pnode: pnode
*
- * This function returns the next pnode to dirty or %NULL if there are no more
- * pnodes. Note that pnodes that have never been written (lnum == 0) are
- * skipped.
+ * This function returns the next pnode or %NULL if there are no more pnodes.
+ * Note that pnodes that have never been written (lnum == 0) are skipped.
*/
-static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
- struct ubifs_pnode *pnode)
+struct ubifs_pnode *ubifs_find_next_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
{
struct ubifs_nnode *nnode;
int iip;
@@ -622,28 +621,35 @@ static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
}
/**
- * do_make_pnode_dirty - mark a pnode dirty.
+ * ubifs_make_nnode_dirty - mark a nnode dirty.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode to mark dirty
+ */
+void ubifs_make_nnode_dirty(struct ubifs_info *c, struct ubifs_nnode *nnode)
+{
+ while (nnode) {
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ nnode = nnode->parent;
+ } else
+ break;
+ }
+}
+
+/**
+ * ubifs_make_pnode_dirty - mark a pnode dirty.
* @c: UBIFS file-system description object
* @pnode: pnode to mark dirty
*/
-static void do_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode)
+void ubifs_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode)
{
/* Assumes cnext list is empty i.e. not called during commit */
if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
- struct ubifs_nnode *nnode;
-
c->dirty_pn_cnt += 1;
add_pnode_dirt(c, pnode);
/* Mark parent and ancestors dirty too */
- nnode = pnode->parent;
- while (nnode) {
- if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
- c->dirty_nn_cnt += 1;
- ubifs_add_nnode_dirt(c, nnode);
- nnode = nnode->parent;
- } else
- break;
- }
+ ubifs_make_nnode_dirty(c, pnode->parent);
}
}
@@ -667,8 +673,8 @@ static int make_tree_dirty(struct ubifs_info *c)
return PTR_ERR(pnode);
while (pnode) {
- do_make_pnode_dirty(c, pnode);
- pnode = next_pnode_to_dirty(c, pnode);
+ ubifs_make_pnode_dirty(c, pnode);
+ pnode = ubifs_find_next_pnode(c, pnode);
if (IS_ERR(pnode))
return PTR_ERR(pnode);
}
@@ -878,20 +884,7 @@ static int make_nnode_dirty(struct ubifs_info *c, int node_num, int lnum,
} else if (c->lpt_lnum != lnum || c->lpt_offs != offs)
return 0; /* nnode is obsolete */
/* Assumes cnext list is empty i.e. not called during commit */
- if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
- c->dirty_nn_cnt += 1;
- ubifs_add_nnode_dirt(c, nnode);
- /* Mark parent and ancestors dirty too */
- nnode = nnode->parent;
- while (nnode) {
- if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
- c->dirty_nn_cnt += 1;
- ubifs_add_nnode_dirt(c, nnode);
- nnode = nnode->parent;
- } else
- break;
- }
- }
+ ubifs_make_nnode_dirty(c, nnode);
return 0;
}
@@ -922,7 +915,7 @@ static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum,
branch = &pnode->parent->nbranch[pnode->iip];
if (branch->lnum != lnum || branch->offs != offs)
return 0;
- do_make_pnode_dirty(c, pnode);
+ ubifs_make_pnode_dirty(c, pnode);
return 0;
}
@@ -1414,14 +1407,33 @@ static struct ubifs_nnode *next_nnode(struct ubifs_info *c,
}
/**
+ * ubifs_free_lpt_nodes - free pnodes/nnodes in LPT.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_free_lpt_nodes(struct ubifs_info *c)
+{
+ int i, hght;
+ struct ubifs_nnode *nnode;
+
+ nnode = first_nnode(c, &hght);
+ while (nnode) {
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++)
+ kfree(nnode->nbranch[i].nnode);
+ nnode = next_nnode(c, nnode, &hght);
+ }
+
+ kfree(c->nroot);
+ c->nroot = NULL;
+}
+
+/**
* ubifs_lpt_free - free resources owned by the LPT.
* @c: UBIFS file-system description object
* @wr_only: free only resources used for writing
*/
void ubifs_lpt_free(struct ubifs_info *c, int wr_only)
{
- struct ubifs_nnode *nnode;
- int i, hght;
+ int i;
/* Free write-only things first */
@@ -1439,17 +1451,12 @@ void ubifs_lpt_free(struct ubifs_info *c, int wr_only)
/* Now free the rest */
- nnode = first_nnode(c, &hght);
- while (nnode) {
- for (i = 0; i < UBIFS_LPT_FANOUT; i++)
- kfree(nnode->nbranch[i].nnode);
- nnode = next_nnode(c, nnode, &hght);
- }
+ ubifs_free_lpt_nodes(c);
for (i = 0; i < LPROPS_HEAP_CNT; i++)
kfree(c->lpt_heap[i].arr);
kfree(c->dirty_idx.arr);
- kfree(c->nroot);
vfree(c->ltab);
+ c->ltab = NULL;
kfree(c->lpt_nod_buf);
}
diff --git a/ubifs-utils/libubifs/super.c b/ubifs-utils/libubifs/super.c
index 155489d9..559623f9 100644
--- a/ubifs-utils/libubifs/super.c
+++ b/ubifs-utils/libubifs/super.c
@@ -654,15 +654,20 @@ void free_orphans(struct ubifs_info *c)
/**
* free_buds - free per-bud objects.
* @c: UBIFS file-system description object
+ * @delete_from_list: whether to delete the bud from list
*/
-static void free_buds(struct ubifs_info *c)
+void free_buds(struct ubifs_info *c, bool delete_from_list)
{
struct ubifs_bud *bud, *n;
rbtree_postorder_for_each_entry_safe(bud, n, &c->buds, rb) {
+ if (delete_from_list)
+ list_del(&bud->list);
kfree(bud->log_hash);
kfree(bud);
}
+
+ c->buds = RB_ROOT;
}
/**
@@ -693,5 +698,5 @@ void destroy_journal(struct ubifs_info *c)
ubifs_destroy_idx_gc(c);
ubifs_destroy_size_tree(c);
ubifs_tnc_close(c);
- free_buds(c);
+ free_buds(c, false);
}
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 72497cd9..45c4105c 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1778,6 +1778,7 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip);
void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty);
void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode);
uint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, int nrbits);
+int ubifs_calc_nnode_num(int row, int col);
struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght);
/* Needed only in debugging code in lpt_commit.c */
int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
@@ -1785,9 +1786,14 @@ int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash);
/* lpt_commit.c */
+struct ubifs_pnode *ubifs_find_next_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode);
+void ubifs_make_nnode_dirty(struct ubifs_info *c, struct ubifs_nnode *nnode);
+void ubifs_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode);
int ubifs_lpt_start_commit(struct ubifs_info *c);
int ubifs_lpt_end_commit(struct ubifs_info *c);
int ubifs_lpt_post_commit(struct ubifs_info *c);
+void ubifs_free_lpt_nodes(struct ubifs_info *c);
void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
/* lprops.c */
@@ -1830,6 +1836,7 @@ int take_gc_lnum(struct ubifs_info *c);
int alloc_wbufs(struct ubifs_info *c);
void free_wbufs(struct ubifs_info *c);
void free_orphans(struct ubifs_info *c);
+void free_buds(struct ubifs_info *c, bool delete_from_list);
void destroy_journal(struct ubifs_info *c);
/* recovery.c */
--
2.13.6
There are many linux kernel source files have been adapted into
ubifs-utils, add descriptions for these source files in README.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 2 +-
ubifs-utils/libubifs/README | 30 ++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
create mode 100644 ubifs-utils/libubifs/README
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index d297f7a2..c02ec313 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -49,7 +49,7 @@ mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUI
mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
-I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs -rdynamic
-EXTRA_DIST += ubifs-utils/common/README
+EXTRA_DIST += ubifs-utils/common/README ubifs-utils/libubifs/README
dist_sbin_SCRIPTS = ubifs-utils/mount.ubifs
diff --git a/ubifs-utils/libubifs/README b/ubifs-utils/libubifs/README
new file mode 100644
index 00000000..551ed8e6
--- /dev/null
+++ b/ubifs-utils/libubifs/README
@@ -0,0 +1,30 @@
+UBIFS Library (Imported from linux kernel 6.8-rc2 41bccc98fb7931d63)
+
+* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel.
+* key.h is copied from fs/ubifs/key.h from the linux kernel.
+* ubifs-media.h is copied from fs/ubifs/ubifs-media.h from the linux kernel.
+* find.c is copied from fs/ubifs/find.c from the linux kernel.
+* scan.c is copied from fs/ubifs/scan.c from the linux kernel.
+* gc.c is copied from fs/ubifs/gc.c from the linux kernel.
+* log.c is copied from fs/ubifs/log.c from the linux kernel, and amended.
+* tnc_commit.c is copied from fs/ubifs/tnc_commit.c from the linux kernel, and amended.
+* master.c is copied from fs/ubifs/master.c from the linux kernel, and amended.
+* recovery.c is copied from fs/ubifs/recovery.c from the linux kernel, and amended.
+* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended.
+* auth.c is a selection of functions copied from fs/ubifs/auth.c from the linux kernel, and amended.
+* budget.c is a selection of functions copied from fs/ubifs/budget.c from the linux kernel, and amended.
+* commit.c is a selection of functions copied from fs/ubifs/commit.c from the linux kernel, and amended.
+* debug.c is a selection of functions copied from fs/ubifs/debug.c from the linux kernel, and amended.
+* debug.h is a selection of functions copied from fs/ubifs/debug.h from the linux kernel, and amended.
+* io.c is a selection of functions copied from fs/ubifs/io.c from the linux kernel, and amended.
+* lprops.c is a selection of functions copied from fs/ubifs/lprops.c from the linux kernel, and amended.
+* lpt_commit.c is a selection of functions copied from fs/ubifs/lpt_commit.c from the linux kernel, and amended.
+* misc.h is a selection of functions copied from fs/ubifs/misc.h from the linux kernel, and amended.
+* orphan.c is a selection of functions copied from fs/ubifs/orphan.c from the linux kernel, and amended.
+* replay.c is a selection of functions copied from fs/ubifs/replay.c from the linux kernel, and amended.
+* sb.c is a selection of functions copied from fs/ubifs/sb.c from the linux kernel, and amended.
+* super.c is a selection of functions copied from fs/ubifs/super.c from the linux kernel, and amended.
+* tnc.c is a selection of functions copied from fs/ubifs/tnc.c from the linux kernel, and amended.
+* tnc_misc.c is a selection of functions copied from fs/ubifs/tnc_misc.c from the linux kernel, and amended.
+* journal.c is a selection of functions copied from fs/ubifs/journal.c from the linux kernel, and amended.
+* dir.c is a selection of functions copied from fs/ubifs/dir.c from the linux kernel, and amended.
--
2.13.6
This is the 17/18 step of fsck. Recover disconnected files into
lost+found. If there is no free space left to recover the disconnected
files, fsck may delete the files to make filesystem be consistent.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 8 ++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 +-
ubifs-utils/fsck.ubifs/handle_disconnected.c | 107 +++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/problem.c | 10 +++
4 files changed, 127 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 0910676c..831a13db 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -541,6 +541,13 @@ static int do_fsck(void)
goto free_disconnected_files_2;
}
+ log_out(c, "Handle disconnected files");
+ err = handle_disonnected_files(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
+
free_disconnected_files_2:
destroy_file_list(c, &FSCK(c)->disconnected_files);
return err;
@@ -596,6 +603,7 @@ int main(int argc, char *argv[])
* Step 14: Check and correct the index size
* Step 15: Check and create root dir
* Step 16: Check and create lost+found
+ * Step 17: Handle disconnected files
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index dc24e83c..84159430 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -46,7 +46,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT, INCORRECT_IDX_SZ,
- ROOT_DIR_NOT_FOUND };
+ ROOT_DIR_NOT_FOUND, DISCONNECTED_FILE_CANNOT_BE_RECOVERED };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -388,5 +388,6 @@ int check_and_correct_index_size(struct ubifs_info *c);
/* handle_disconnected.c */
int check_and_create_lost_found(struct ubifs_info *c);
+int handle_disonnected_files(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/handle_disconnected.c b/ubifs-utils/fsck.ubifs/handle_disconnected.c
index b9a380f9..5ca00c0c 100644
--- a/ubifs-utils/fsck.ubifs/handle_disconnected.c
+++ b/ubifs-utils/fsck.ubifs/handle_disconnected.c
@@ -19,6 +19,7 @@
#include "fsck.ubifs.h"
#define LOST_FOUND_DIR_NAME "lost+found"
+#define MAX_REPEAT_NAME_RETRY_TIMES 10000000
/**
* check_and_create_lost_found - Check and create the lost+found directory.
@@ -87,3 +88,109 @@ free_root:
kfree(root_ui);
return err;
}
+
+static int handle_disonnected_file(struct ubifs_info *c,
+ struct scanned_file *file)
+{
+ int err = 0;
+
+ if (FSCK(c)->lost_and_found) {
+ unsigned int index = 0;
+ char file_name[UBIFS_MAX_NLEN + 1];
+ struct fscrypt_name nm;
+ struct ubifs_inode *ui = NULL, *lost_found_ui = NULL;
+
+ lost_found_ui = ubifs_lookup_by_inum(c, FSCK(c)->lost_and_found);
+ if (IS_ERR(lost_found_ui)) {
+ err = PTR_ERR(lost_found_ui);
+ ubifs_assert(c, err != -ENOENT);
+ return err;
+ }
+ ui = ubifs_lookup_by_inum(c, file->inum);
+ if (IS_ERR(ui)) {
+ err = PTR_ERR(ui);
+ ubifs_assert(c, err != -ENOENT);
+ goto free_lost_found_ui;
+ }
+
+ while (index < MAX_REPEAT_NAME_RETRY_TIMES) {
+ struct ubifs_inode *target_ui;
+
+ err = snprintf(file_name, sizeof(file_name),
+ "INO_%lu_%u", file->inum, index);
+ if (err < 0)
+ goto free_ui;
+ fname_name(&nm) = file_name;
+ fname_len(&nm) = strlen(file_name);
+ target_ui = ubifs_lookup(c, lost_found_ui, &nm);
+ if (IS_ERR(target_ui)) {
+ err = PTR_ERR(target_ui);
+ if (err == -ENOENT)
+ break;
+ goto free_ui;
+ }
+ kfree(target_ui);
+ index++;
+ }
+
+ if (err != -ENOENT) {
+ err = 0;
+ kfree(ui);
+ kfree(lost_found_ui);
+ log_out(c, "Too many duplicated names(%u) in lost+found for inum %lu",
+ index, file->inum);
+ goto delete_file;
+ }
+
+ /* Try to recover disconnected file into lost+found. */
+ err = ubifs_link_recovery(c, lost_found_ui, ui, &nm);
+ if (err && err == -ENOSPC) {
+ err = 0;
+ log_out(c, "No free space to recover disconnected file");
+ goto delete_file;
+ }
+ dbg_fsck("recover disconnected file %lu, in %s",
+ file->inum, c->dev_name);
+
+free_ui:
+ kfree(ui);
+free_lost_found_ui:
+ kfree(lost_found_ui);
+ return err;
+ } else
+ log_out(c, "No valid lost+found");
+
+delete_file:
+ if (fix_problem(c, DISCONNECTED_FILE_CANNOT_BE_RECOVERED, file))
+ err = delete_file(c, file);
+ return err;
+}
+
+/**
+ * handle_disonnected_files - Handle disconnected files.
+ * @c: UBIFS file-system description object
+ *
+ * This function tries to recover disonnected files into lost+found directory.
+ * If there is no free space left to recover the disconnected files, fsck may
+ * delete the files to make filesystem be consistent. Returns zero in case of
+ * success, a negative error code in case of failure.
+ */
+int handle_disonnected_files(struct ubifs_info *c)
+{
+ int err, ret = 0;
+ struct scanned_file *file;
+
+ while (!list_empty(&FSCK(c)->disconnected_files)) {
+ file = list_entry(FSCK(c)->disconnected_files.next,
+ struct scanned_file, list);
+
+ list_del(&file->list);
+ err = handle_disonnected_file(c, file);
+ if (err)
+ ret = ret ? ret : err;
+ destroy_file_content(c, file);
+ kfree(file);
+ }
+
+ return ret;
+}
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 8e7e1e15..916c9762 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -69,6 +69,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect index size"}, // INCORRECT_IDX_SZ
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Root dir is lost"}, // ROOT_DIR_NOT_FOUND
+ {PROBLEM_FIXABLE | PROBLEM_DROP_DATA, "Disconnected file cannot be recovered"}, // DISCONNECTED_FILE_CANNOT_BE_RECOVERED
};
static const char *get_question(const struct fsck_problem *problem,
@@ -96,6 +97,7 @@ static const char *get_question(const struct fsck_problem *problem,
case FILE_HAS_NO_ENCRYPT:
case FILE_ROOT_HAS_DENT:
case DENTRY_IS_UNREACHABLE:
+ case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
return "Delete it?";
case FILE_HAS_INCONSIST_TYPE:
case FILE_HAS_TOO_MANY_DENT:
@@ -292,6 +294,14 @@ static void print_problem(const struct ubifs_info *c,
problem->desc, c->calc_idx_sz, *calc_sz);
break;
}
+ case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
+ {
+ const struct scanned_file *file = (const struct scanned_file *)priv;
+
+ log_out(c, "problem: %s, ino %lu, size %llu", problem->desc,
+ file->inum, file->ino.size);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6
This is the 2/12 step of rebuilding. Traverse nodes from del_inos and
del_dents trees, remove inode nodes and dentry nodes with smaller sqnum
from valid trees.
This step handles deleting case, for example, file A is deleted, deleted
inode node and deleted dentry node are written, if we ignore the deleted
nodes, file A can be recovered after rebuilding because undeleted inode
node and undeleted dentry node can be scanned. There's an exception, if
deleted inode node and deleted dentry node are reclaimed(by gc) after
deletion, file A is recovered. UBIFS rebuild_fs cannot solve it, because
the real existence information of nodes depends on TNC, but TNC should
not be depended for UBIFS rebuild_fs.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/rebuild_fs.c | 120 +++++++++++++++++++++++++++++++++++-
1 file changed, 119 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 3ca94869..dbb0f3bc 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -364,6 +364,117 @@ out:
return err;
}
+static struct scanned_ino_node *
+lookup_valid_ino_node(struct ubifs_info *c, struct scanned_info *si,
+ struct scanned_ino_node *target)
+{
+ int cmp;
+ struct scanned_ino_node *ino_node;
+ struct rb_node *p;
+
+ p = si->valid_inos.rb_node;
+ while (p) {
+ ino_node = rb_entry(p, struct scanned_ino_node, rb);
+ cmp = keys_cmp(c, &target->key, &ino_node->key);
+ if (cmp < 0) {
+ p = p->rb_left;
+ } else if (cmp > 0) {
+ p = p->rb_right;
+ } else {
+ if (target->header.sqnum > ino_node->header.sqnum)
+ return ino_node;
+ else
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static struct scanned_dent_node *
+lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si,
+ struct scanned_dent_node *target)
+{
+ int cmp, nlen;
+ struct scanned_dent_node *dent_node;
+ struct rb_node *p;
+
+ p = si->valid_dents.rb_node;
+ while (p) {
+ dent_node = rb_entry(p, struct scanned_dent_node, rb);
+ cmp = keys_cmp(c, &target->key, &dent_node->key);
+ if (cmp < 0) {
+ p = p->rb_left;
+ } else if (cmp > 0) {
+ p = p->rb_right;
+ } else {
+ nlen = min(target->nlen, dent_node->nlen);
+ cmp = strncmp(target->name, dent_node->name, nlen) ? :
+ target->nlen - dent_node->nlen;
+ if (cmp < 0) {
+ p = p->rb_left;
+ } else if (cmp > 0) {
+ p = p->rb_right;
+ } else {
+ if (target->header.sqnum >
+ dent_node->header.sqnum)
+ return dent_node;
+ else
+ return NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * remove_del_nodes - remove deleted nodes from valid node tree.
+ * @c: UBIFS file-system description object
+ * @si: records nodes and files information during scanning
+ *
+ * This function compares sqnum between deleted node and corresponding valid
+ * node, removes valid node from tree if the sqnum of deleted node is bigger.
+ * Deleted ino/dent nodes will be removed from @si->del_inos/@si->del_dents
+ * after this function finished.
+ */
+static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
+{
+ struct scanned_ino_node *del_ino_node, *valid_ino_node;
+ struct scanned_dent_node *del_dent_node, *valid_dent_node;
+ struct rb_node *this;
+
+ this = rb_first(&si->del_inos);
+ while (this) {
+ del_ino_node = rb_entry(this, struct scanned_ino_node, rb);
+ this = rb_next(this);
+
+ valid_ino_node = lookup_valid_ino_node(c, si, del_ino_node);
+ if (valid_ino_node) {
+ rb_erase(&valid_ino_node->rb, &si->valid_inos);
+ kfree(valid_ino_node);
+ }
+
+ rb_erase(&del_ino_node->rb, &si->del_inos);
+ kfree(del_ino_node);
+ }
+
+ this = rb_first(&si->del_dents);
+ while (this) {
+ del_dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ valid_dent_node = lookup_valid_dent_node(c, si, del_dent_node);
+ if (valid_dent_node) {
+ rb_erase(&valid_dent_node->rb, &si->valid_dents);
+ kfree(valid_dent_node);
+ }
+
+ rb_erase(&del_dent_node->rb, &si->del_dents);
+ kfree(del_dent_node);
+ }
+}
+
/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
@@ -389,9 +500,16 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 1: Scan valid/deleted nodes from volume. */
log_out(c, "Scan nodes");
err = scan_nodes(c, &si);
- if (err)
+ if (err) {
exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 2: Remove deleted nodes from valid node tree. */
+ log_out(c, "Remove deleted nodes");
+ remove_del_nodes(c, &si);
+out:
destroy_scanned_info(c, &si);
destroy_rebuild_info(c);
--
2.13.6
Import UBIFS libs from linux kernel. Next patches will replace ubifs
related source code with implementation of linux kernel, which makes
userspace implementation be same with linux kernel, then fsck.ubifs
can resuse the code.
Notice: lpt.c is modified with [1] applied. ubifs.h and orphan.c are
modified with [2] applied, journal.c is modified with [3] reverted(
because fsck runs in a single thread, so waitqueue is not needed to
be implemented in userspace.).
[1] https://lore.kernel.org/linux-mtd/[email protected]/
[2] https://lore.kernel.org/linux-mtd/[email protected]/
[3] https://lore.kernel.org/linux-mtd/[email protected]/
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/libubifs/auth.c | 545 ++++++
ubifs-utils/libubifs/budget.c | 714 ++++++++
ubifs-utils/libubifs/commit.c | 733 ++++++++
ubifs-utils/libubifs/debug.c | 3051 +++++++++++++++++++++++++++++++
ubifs-utils/libubifs/debug.h | 304 ++++
ubifs-utils/libubifs/dir.c | 1744 ++++++++++++++++++
ubifs-utils/libubifs/find.c | 963 ++++++++++
ubifs-utils/libubifs/gc.c | 1017 +++++++++++
ubifs-utils/libubifs/io.c | 1268 +++++++++++++
ubifs-utils/libubifs/journal.c | 1928 ++++++++++++++++++++
ubifs-utils/libubifs/key.h | 543 ++++++
ubifs-utils/libubifs/log.c | 762 ++++++++
ubifs-utils/libubifs/lprops.c | 1307 ++++++++++++++
ubifs-utils/libubifs/lpt.c | 2451 +++++++++++++++++++++++++
ubifs-utils/libubifs/lpt_commit.c | 1997 +++++++++++++++++++++
ubifs-utils/libubifs/master.c | 473 +++++
ubifs-utils/libubifs/misc.h | 289 +++
ubifs-utils/libubifs/orphan.c | 947 ++++++++++
ubifs-utils/libubifs/recovery.c | 1588 +++++++++++++++++
ubifs-utils/libubifs/replay.c | 1250 +++++++++++++
ubifs-utils/libubifs/sb.c | 956 ++++++++++
ubifs-utils/libubifs/scan.c | 366 ++++
ubifs-utils/libubifs/super.c | 2505 ++++++++++++++++++++++++++
ubifs-utils/libubifs/tnc.c | 3553 +++++++++++++++++++++++++++++++++++++
ubifs-utils/libubifs/tnc_commit.c | 1111 ++++++++++++
ubifs-utils/libubifs/tnc_misc.c | 524 ++++++
ubifs-utils/libubifs/ubifs.h | 2164 ++++++++++++++++++++++
27 files changed, 35053 insertions(+)
create mode 100644 ubifs-utils/libubifs/auth.c
create mode 100644 ubifs-utils/libubifs/budget.c
create mode 100644 ubifs-utils/libubifs/commit.c
create mode 100644 ubifs-utils/libubifs/debug.c
create mode 100644 ubifs-utils/libubifs/debug.h
create mode 100644 ubifs-utils/libubifs/dir.c
create mode 100644 ubifs-utils/libubifs/find.c
create mode 100644 ubifs-utils/libubifs/gc.c
create mode 100644 ubifs-utils/libubifs/io.c
create mode 100644 ubifs-utils/libubifs/journal.c
create mode 100644 ubifs-utils/libubifs/key.h
create mode 100644 ubifs-utils/libubifs/log.c
create mode 100644 ubifs-utils/libubifs/lprops.c
create mode 100644 ubifs-utils/libubifs/lpt.c
create mode 100644 ubifs-utils/libubifs/lpt_commit.c
create mode 100644 ubifs-utils/libubifs/master.c
create mode 100644 ubifs-utils/libubifs/misc.h
create mode 100644 ubifs-utils/libubifs/orphan.c
create mode 100644 ubifs-utils/libubifs/recovery.c
create mode 100644 ubifs-utils/libubifs/replay.c
create mode 100644 ubifs-utils/libubifs/sb.c
create mode 100644 ubifs-utils/libubifs/scan.c
create mode 100644 ubifs-utils/libubifs/super.c
create mode 100644 ubifs-utils/libubifs/tnc.c
create mode 100644 ubifs-utils/libubifs/tnc_commit.c
create mode 100644 ubifs-utils/libubifs/tnc_misc.c
create mode 100644 ubifs-utils/libubifs/ubifs.h
diff --git a/ubifs-utils/libubifs/auth.c b/ubifs-utils/libubifs/auth.c
new file mode 100644
index 00000000..0d561ecb
--- /dev/null
+++ b/ubifs-utils/libubifs/auth.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2018 Pengutronix, Sascha Hauer <[email protected]>
+ */
+
+/*
+ * This file implements various helper functions for UBIFS authentication support
+ */
+
+#include <linux/verification.h>
+#include <crypto/hash.h>
+#include <crypto/utils.h>
+#include <keys/user-type.h>
+#include <keys/asymmetric-type.h>
+
+#include "ubifs.h"
+
+/**
+ * ubifs_node_calc_hash - calculate the hash of a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @hash: the returned hash
+ *
+ * Returns 0 for success or a negative error code otherwise.
+ */
+int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
+ u8 *hash)
+{
+ const struct ubifs_ch *ch = node;
+
+ return crypto_shash_tfm_digest(c->hash_tfm, node, le32_to_cpu(ch->len),
+ hash);
+}
+
+/**
+ * ubifs_hash_calc_hmac - calculate a HMAC from a hash
+ * @c: UBIFS file-system description object
+ * @hash: the node to calculate a HMAC for
+ * @hmac: the returned HMAC
+ *
+ * Returns 0 for success or a negative error code otherwise.
+ */
+static int ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
+ u8 *hmac)
+{
+ return crypto_shash_tfm_digest(c->hmac_tfm, hash, c->hash_len, hmac);
+}
+
+/**
+ * ubifs_prepare_auth_node - Prepare an authentication node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @inhash: input hash of previous nodes
+ *
+ * This function prepares an authentication node for writing onto flash.
+ * It creates a HMAC from the given input hash and writes it to the node.
+ *
+ * Returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
+ struct shash_desc *inhash)
+{
+ struct ubifs_auth_node *auth = node;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ int err;
+
+ {
+ SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
+
+ hash_desc->tfm = c->hash_tfm;
+ ubifs_shash_copy_state(c, inhash, hash_desc);
+
+ err = crypto_shash_final(hash_desc, hash);
+ if (err)
+ return err;
+ }
+
+ err = ubifs_hash_calc_hmac(c, hash, auth->hmac);
+ if (err)
+ return err;
+
+ auth->ch.node_type = UBIFS_AUTH_NODE;
+ ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
+ return 0;
+}
+
+static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
+ struct crypto_shash *tfm)
+{
+ struct shash_desc *desc;
+ int err;
+
+ if (!ubifs_authenticated(c))
+ return NULL;
+
+ desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!desc)
+ return ERR_PTR(-ENOMEM);
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err) {
+ kfree(desc);
+ return ERR_PTR(err);
+ }
+
+ return desc;
+}
+
+/**
+ * __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
+ * @c: UBIFS file-system description object
+ *
+ * This function returns a descriptor suitable for hashing a node. Free after use
+ * with kfree.
+ */
+struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
+{
+ return ubifs_get_desc(c, c->hash_tfm);
+}
+
+/**
+ * ubifs_bad_hash - Report hash mismatches
+ * @c: UBIFS file-system description object
+ * @node: the node
+ * @hash: the expected hash
+ * @lnum: the LEB @node was read from
+ * @offs: offset in LEB @node was read from
+ *
+ * This function reports a hash mismatch when a node has a different hash than
+ * expected.
+ */
+void ubifs_bad_hash(const struct ubifs_info *c, const void *node, const u8 *hash,
+ int lnum, int offs)
+{
+ int len = min(c->hash_len, 20);
+ int cropped = len != c->hash_len;
+ const char *cont = cropped ? "..." : "";
+
+ u8 calc[UBIFS_HASH_ARR_SZ];
+
+ __ubifs_node_calc_hash(c, node, calc);
+
+ ubifs_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
+ ubifs_err(c, "hash expected: %*ph%s", len, hash, cont);
+ ubifs_err(c, "hash calculated: %*ph%s", len, calc, cont);
+}
+
+/**
+ * __ubifs_node_check_hash - check the hash of a node against given hash
+ * @c: UBIFS file-system description object
+ * @node: the node
+ * @expected: the expected hash
+ *
+ * This function calculates a hash over a node and compares it to the given hash.
+ * Returns 0 if both hashes are equal or authentication is disabled, otherwise a
+ * negative error code is returned.
+ */
+int __ubifs_node_check_hash(const struct ubifs_info *c, const void *node,
+ const u8 *expected)
+{
+ u8 calc[UBIFS_HASH_ARR_SZ];
+ int err;
+
+ err = __ubifs_node_calc_hash(c, node, calc);
+ if (err)
+ return err;
+
+ if (ubifs_check_hash(c, expected, calc))
+ return -EPERM;
+
+ return 0;
+}
+
+/**
+ * ubifs_sb_verify_signature - verify the signature of a superblock
+ * @c: UBIFS file-system description object
+ * @sup: The superblock node
+ *
+ * To support offline signed images the superblock can be signed with a
+ * PKCS#7 signature. The signature is placed directly behind the superblock
+ * node in an ubifs_sig_node.
+ *
+ * Returns 0 when the signature can be successfully verified or a negative
+ * error code if not.
+ */
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+ const struct ubifs_sb_node *sup)
+{
+ int err;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ const struct ubifs_sig_node *signode;
+
+ sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ return err;
+ }
+
+ if (sleb->nodes_cnt == 0) {
+ ubifs_err(c, "Unable to find signature node");
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ snod = list_first_entry(&sleb->nodes, struct ubifs_scan_node, list);
+
+ if (snod->type != UBIFS_SIG_NODE) {
+ ubifs_err(c, "Signature node is of wrong type");
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ signode = snod->node;
+
+ if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) {
+ ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len));
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ if (le32_to_cpu(signode->type) != UBIFS_SIGNATURE_TYPE_PKCS7) {
+ ubifs_err(c, "Signature type %d is not supported\n",
+ le32_to_cpu(signode->type));
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node),
+ signode->sig, le32_to_cpu(signode->len),
+ NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
+ NULL, NULL);
+
+ if (err)
+ ubifs_err(c, "Failed to verify signature");
+ else
+ ubifs_msg(c, "Successfully verified super block signature");
+
+out_destroy:
+ ubifs_scan_destroy(sleb);
+
+ return err;
+}
+
+/**
+ * ubifs_init_authentication - initialize UBIFS authentication support
+ * @c: UBIFS file-system description object
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_init_authentication(struct ubifs_info *c)
+{
+ struct key *keyring_key;
+ const struct user_key_payload *ukp;
+ int err;
+ char hmac_name[CRYPTO_MAX_ALG_NAME];
+
+ if (!c->auth_hash_name) {
+ ubifs_err(c, "authentication hash name needed with authentication");
+ return -EINVAL;
+ }
+
+ c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
+ c->auth_hash_name);
+ if ((int)c->auth_hash_algo < 0) {
+ ubifs_err(c, "Unknown hash algo %s specified",
+ c->auth_hash_name);
+ return -EINVAL;
+ }
+
+ snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
+ c->auth_hash_name);
+
+ keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
+
+ if (IS_ERR(keyring_key)) {
+ ubifs_err(c, "Failed to request key: %ld",
+ PTR_ERR(keyring_key));
+ return PTR_ERR(keyring_key);
+ }
+
+ down_read(&keyring_key->sem);
+
+ if (keyring_key->type != &key_type_logon) {
+ ubifs_err(c, "key type must be logon");
+ err = -ENOKEY;
+ goto out;
+ }
+
+ ukp = user_key_payload_locked(keyring_key);
+ if (!ukp) {
+ /* key was revoked before we acquired its semaphore */
+ err = -EKEYREVOKED;
+ goto out;
+ }
+
+ c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0, 0);
+ if (IS_ERR(c->hash_tfm)) {
+ err = PTR_ERR(c->hash_tfm);
+ ubifs_err(c, "Can not allocate %s: %d",
+ c->auth_hash_name, err);
+ goto out;
+ }
+
+ c->hash_len = crypto_shash_digestsize(c->hash_tfm);
+ if (c->hash_len > UBIFS_HASH_ARR_SZ) {
+ ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
+ c->auth_hash_name, c->hash_len, UBIFS_HASH_ARR_SZ);
+ err = -EINVAL;
+ goto out_free_hash;
+ }
+
+ c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
+ if (IS_ERR(c->hmac_tfm)) {
+ err = PTR_ERR(c->hmac_tfm);
+ ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
+ goto out_free_hash;
+ }
+
+ c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
+ if (c->hmac_desc_len > UBIFS_HMAC_ARR_SZ) {
+ ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
+ hmac_name, c->hmac_desc_len, UBIFS_HMAC_ARR_SZ);
+ err = -EINVAL;
+ goto out_free_hmac;
+ }
+
+ err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
+ if (err)
+ goto out_free_hmac;
+
+ c->authenticated = true;
+
+ c->log_hash = ubifs_hash_get_desc(c);
+ if (IS_ERR(c->log_hash)) {
+ err = PTR_ERR(c->log_hash);
+ goto out_free_hmac;
+ }
+
+ err = 0;
+
+out_free_hmac:
+ if (err)
+ crypto_free_shash(c->hmac_tfm);
+out_free_hash:
+ if (err)
+ crypto_free_shash(c->hash_tfm);
+out:
+ up_read(&keyring_key->sem);
+ key_put(keyring_key);
+
+ return err;
+}
+
+/**
+ * __ubifs_exit_authentication - release resource
+ * @c: UBIFS file-system description object
+ *
+ * This function releases the authentication related resources.
+ */
+void __ubifs_exit_authentication(struct ubifs_info *c)
+{
+ if (!ubifs_authenticated(c))
+ return;
+
+ crypto_free_shash(c->hmac_tfm);
+ crypto_free_shash(c->hash_tfm);
+ kfree(c->log_hash);
+}
+
+/**
+ * ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ * @hmac: returned HMAC
+ *
+ * This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
+ * embedded into the node, so this area is not covered by the HMAC. Also not
+ * covered is the UBIFS_NODE_MAGIC and the CRC of the node.
+ */
+static int ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
+ int len, int ofs_hmac, void *hmac)
+{
+ SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
+ int hmac_len = c->hmac_desc_len;
+ int err;
+
+ ubifs_assert(c, ofs_hmac > 8);
+ ubifs_assert(c, ofs_hmac + hmac_len < len);
+
+ shash->tfm = c->hmac_tfm;
+
+ err = crypto_shash_init(shash);
+ if (err)
+ return err;
+
+ /* behind common node header CRC up to HMAC begin */
+ err = crypto_shash_update(shash, node + 8, ofs_hmac - 8);
+ if (err < 0)
+ return err;
+
+ /* behind HMAC, if any */
+ if (len - ofs_hmac - hmac_len > 0) {
+ err = crypto_shash_update(shash, node + ofs_hmac + hmac_len,
+ len - ofs_hmac - hmac_len);
+ if (err < 0)
+ return err;
+ }
+
+ return crypto_shash_final(shash, hmac);
+}
+
+/**
+ * __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ *
+ * This function inserts a HMAC at offset @ofs_hmac into the node given in
+ * @node.
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
+ int ofs_hmac)
+{
+ return ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
+}
+
+/**
+ * __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ *
+ * This function verifies the HMAC at offset @ofs_hmac of the node given in
+ * @node. Returns 0 if successful or a negative error code otherwise.
+ */
+int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
+ int len, int ofs_hmac)
+{
+ int hmac_len = c->hmac_desc_len;
+ u8 *hmac;
+ int err;
+
+ hmac = kmalloc(hmac_len, GFP_NOFS);
+ if (!hmac)
+ return -ENOMEM;
+
+ err = ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
+ if (err) {
+ kfree(hmac);
+ return err;
+ }
+
+ err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
+
+ kfree(hmac);
+
+ if (!err)
+ return 0;
+
+ return -EPERM;
+}
+
+int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
+ struct shash_desc *target)
+{
+ u8 *state;
+ int err;
+
+ state = kmalloc(crypto_shash_descsize(src->tfm), GFP_NOFS);
+ if (!state)
+ return -ENOMEM;
+
+ err = crypto_shash_export(src, state);
+ if (err)
+ goto out;
+
+ err = crypto_shash_import(target, state);
+
+out:
+ kfree(state);
+
+ return err;
+}
+
+/**
+ * ubifs_hmac_wkm - Create a HMAC of the well known message
+ * @c: UBIFS file-system description object
+ * @hmac: The HMAC of the well known message
+ *
+ * This function creates a HMAC of a well known message. This is used
+ * to check if the provided key is suitable to authenticate a UBIFS
+ * image. This is only a convenience to the user to provide a better
+ * error message when the wrong key is provided.
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
+{
+ SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
+ int err;
+ const char well_known_message[] = "UBIFS";
+
+ if (!ubifs_authenticated(c))
+ return 0;
+
+ shash->tfm = c->hmac_tfm;
+
+ err = crypto_shash_init(shash);
+ if (err)
+ return err;
+
+ err = crypto_shash_update(shash, well_known_message,
+ sizeof(well_known_message) - 1);
+ if (err < 0)
+ return err;
+
+ err = crypto_shash_final(shash, hmac);
+ if (err)
+ return err;
+ return 0;
+}
+
+/*
+ * ubifs_hmac_zero - test if a HMAC is zero
+ * @c: UBIFS file-system description object
+ * @hmac: the HMAC to test
+ *
+ * This function tests if a HMAC is zero and returns true if it is
+ * and false otherwise.
+ */
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac)
+{
+ return !memchr_inv(hmac, 0, c->hmac_desc_len);
+}
diff --git a/ubifs-utils/libubifs/budget.c b/ubifs-utils/libubifs/budget.c
new file mode 100644
index 00000000..d76eb7b3
--- /dev/null
+++ b/ubifs-utils/libubifs/budget.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the budgeting sub-system which is responsible for UBIFS
+ * space management.
+ *
+ * Factors such as compression, wasted space at the ends of LEBs, space in other
+ * journal heads, the effect of updates on the index, and so on, make it
+ * impossible to accurately predict the amount of space needed. Consequently
+ * approximations are used.
+ */
+
+#include "ubifs.h"
+#include <linux/writeback.h>
+#include <linux/math64.h>
+
+/*
+ * When pessimistic budget calculations say that there is no enough space,
+ * UBIFS starts writing back dirty inodes and pages, doing garbage collection,
+ * or committing. The below constant defines maximum number of times UBIFS
+ * repeats the operations.
+ */
+#define MAX_MKSPC_RETRIES 3
+
+/*
+ * The below constant defines amount of dirty pages which should be written
+ * back at when trying to shrink the liability.
+ */
+#define NR_TO_WRITE 16
+
+/**
+ * shrink_liability - write-back some dirty pages/inodes.
+ * @c: UBIFS file-system description object
+ * @nr_to_write: how many dirty pages to write-back
+ *
+ * This function shrinks UBIFS liability by means of writing back some amount
+ * of dirty inodes and their pages.
+ *
+ * Note, this function synchronizes even VFS inodes which are locked
+ * (@i_mutex) by the caller of the budgeting function, because write-back does
+ * not touch @i_mutex.
+ */
+static void shrink_liability(struct ubifs_info *c, int nr_to_write)
+{
+ down_read(&c->vfs_sb->s_umount);
+ writeback_inodes_sb_nr(c->vfs_sb, nr_to_write, WB_REASON_FS_FREE_SPACE);
+ up_read(&c->vfs_sb->s_umount);
+}
+
+/**
+ * run_gc - run garbage collector.
+ * @c: UBIFS file-system description object
+ *
+ * This function runs garbage collector to make some more free space. Returns
+ * zero if a free LEB has been produced, %-EAGAIN if commit is required, and a
+ * negative error code in case of failure.
+ */
+static int run_gc(struct ubifs_info *c)
+{
+ int lnum;
+
+ /* Make some free space by garbage-collecting dirty space */
+ down_read(&c->commit_sem);
+ lnum = ubifs_garbage_collect(c, 1);
+ up_read(&c->commit_sem);
+ if (lnum < 0)
+ return lnum;
+
+ /* GC freed one LEB, return it to lprops */
+ dbg_budg("GC freed LEB %d", lnum);
+ return ubifs_return_leb(c, lnum);
+}
+
+/**
+ * get_liability - calculate current liability.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns current UBIFS liability, i.e. the
+ * amount of bytes UBIFS has "promised" to write to the media.
+ */
+static long long get_liability(struct ubifs_info *c)
+{
+ long long liab;
+
+ spin_lock(&c->space_lock);
+ liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth;
+ spin_unlock(&c->space_lock);
+ return liab;
+}
+
+/**
+ * make_free_space - make more free space on the file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called when an operation cannot be budgeted because there
+ * is supposedly no free space. But in most cases there is some free space:
+ * o budgeting is pessimistic, so it always budgets more than it is actually
+ * needed, so shrinking the liability is one way to make free space - the
+ * cached data will take less space then it was budgeted for;
+ * o GC may turn some dark space into free space (budgeting treats dark space
+ * as not available);
+ * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs.
+ *
+ * So this function tries to do the above. Returns %-EAGAIN if some free space
+ * was presumably made and the caller has to re-try budgeting the operation.
+ * Returns %-ENOSPC if it couldn't do more free space, and other negative error
+ * codes on failures.
+ */
+static int make_free_space(struct ubifs_info *c)
+{
+ int err, retries = 0;
+ long long liab1, liab2;
+
+ do {
+ liab1 = get_liability(c);
+ /*
+ * We probably have some dirty pages or inodes (liability), try
+ * to write them back.
+ */
+ dbg_budg("liability %lld, run write-back", liab1);
+ shrink_liability(c, NR_TO_WRITE);
+
+ liab2 = get_liability(c);
+ if (liab2 < liab1)
+ return -EAGAIN;
+
+ dbg_budg("new liability %lld (not shrunk)", liab2);
+
+ /* Liability did not shrink again, try GC */
+ dbg_budg("Run GC");
+ err = run_gc(c);
+ if (!err)
+ return -EAGAIN;
+
+ if (err != -EAGAIN && err != -ENOSPC)
+ /* Some real error happened */
+ return err;
+
+ dbg_budg("Run commit (retries %d)", retries);
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+ } while (retries++ < MAX_MKSPC_RETRIES);
+
+ return -ENOSPC;
+}
+
+/**
+ * ubifs_calc_min_idx_lebs - calculate amount of LEBs for the index.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns the number of LEBs which should be kept
+ * for index usage.
+ */
+int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
+{
+ int idx_lebs;
+ long long idx_size;
+
+ idx_size = c->bi.old_idx_sz + c->bi.idx_growth + c->bi.uncommitted_idx;
+ /* And make sure we have thrice the index size of space reserved */
+ idx_size += idx_size << 1;
+ /*
+ * We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes'
+ * pair, nor similarly the two variables for the new index size, so we
+ * have to do this costly 64-bit division on fast-path.
+ */
+ idx_lebs = div_u64(idx_size + c->idx_leb_size - 1, c->idx_leb_size);
+ /*
+ * The index head is not available for the in-the-gaps method, so add an
+ * extra LEB to compensate.
+ */
+ idx_lebs += 1;
+ if (idx_lebs < MIN_INDEX_LEBS)
+ idx_lebs = MIN_INDEX_LEBS;
+ return idx_lebs;
+}
+
+/**
+ * ubifs_calc_available - calculate available FS space.
+ * @c: UBIFS file-system description object
+ * @min_idx_lebs: minimum number of LEBs reserved for the index
+ *
+ * This function calculates and returns amount of FS space available for use.
+ */
+long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
+{
+ int subtract_lebs;
+ long long available;
+
+ available = c->main_bytes - c->lst.total_used;
+
+ /*
+ * Now 'available' contains theoretically available flash space
+ * assuming there is no index, so we have to subtract the space which
+ * is reserved for the index.
+ */
+ subtract_lebs = min_idx_lebs;
+
+ /* Take into account that GC reserves one LEB for its own needs */
+ subtract_lebs += 1;
+
+ /*
+ * Since different write types go to different heads, we should
+ * reserve one leb for each head.
+ */
+ subtract_lebs += c->jhead_cnt;
+
+ /* We also reserve one LEB for deletions, which bypass budgeting */
+ subtract_lebs += 1;
+
+ available -= (long long)subtract_lebs * c->leb_size;
+
+ /* Subtract the dead space which is not available for use */
+ available -= c->lst.total_dead;
+
+ /*
+ * Subtract dark space, which might or might not be usable - it depends
+ * on the data which we have on the media and which will be written. If
+ * this is a lot of uncompressed or not-compressible data, the dark
+ * space cannot be used.
+ */
+ available -= c->lst.total_dark;
+
+ /*
+ * However, there is more dark space. The index may be bigger than
+ * @min_idx_lebs. Those extra LEBs are assumed to be available, but
+ * their dark space is not included in total_dark, so it is subtracted
+ * here.
+ */
+ if (c->lst.idx_lebs > min_idx_lebs) {
+ subtract_lebs = c->lst.idx_lebs - min_idx_lebs;
+ available -= subtract_lebs * c->dark_wm;
+ }
+
+ /* The calculations are rough and may end up with a negative number */
+ return available > 0 ? available : 0;
+}
+
+/**
+ * can_use_rp - check whether the user is allowed to use reserved pool.
+ * @c: UBIFS file-system description object
+ *
+ * UBIFS has so-called "reserved pool" which is flash space reserved
+ * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock.
+ * This function checks whether current user is allowed to use reserved pool.
+ * Returns %1 current user is allowed to use reserved pool and %0 otherwise.
+ */
+static int can_use_rp(struct ubifs_info *c)
+{
+ if (uid_eq(current_fsuid(), c->rp_uid) || capable(CAP_SYS_RESOURCE) ||
+ (!gid_eq(c->rp_gid, GLOBAL_ROOT_GID) && in_group_p(c->rp_gid)))
+ return 1;
+ return 0;
+}
+
+/**
+ * do_budget_space - reserve flash space for index and data growth.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure UBIFS has enough free LEBs for index growth and
+ * data.
+ *
+ * When budgeting index space, UBIFS reserves thrice as many LEBs as the index
+ * would take if it was consolidated and written to the flash. This guarantees
+ * that the "in-the-gaps" commit method always succeeds and UBIFS will always
+ * be able to commit dirty index. So this function basically adds amount of
+ * budgeted index space to the size of the current index, multiplies this by 3,
+ * and makes sure this does not exceed the amount of free LEBs.
+ *
+ * Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables:
+ * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
+ * be large, because UBIFS does not do any index consolidation as long as
+ * there is free space. IOW, the index may take a lot of LEBs, but the LEBs
+ * will contain a lot of dirt.
+ * o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW,
+ * the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs.
+ *
+ * This function returns zero in case of success, and %-ENOSPC in case of
+ * failure.
+ */
+static int do_budget_space(struct ubifs_info *c)
+{
+ long long outstanding, available;
+ int lebs, rsvd_idx_lebs, min_idx_lebs;
+
+ /* First budget index space */
+ min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ /* Now 'min_idx_lebs' contains number of LEBs to reserve */
+ if (min_idx_lebs > c->lst.idx_lebs)
+ rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs;
+ else
+ rsvd_idx_lebs = 0;
+
+ /*
+ * The number of LEBs that are available to be used by the index is:
+ *
+ * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt -
+ * @c->lst.taken_empty_lebs
+ *
+ * @c->lst.empty_lebs are available because they are empty.
+ * @c->freeable_cnt are available because they contain only free and
+ * dirty space, @c->idx_gc_cnt are available because they are index
+ * LEBs that have been garbage collected and are awaiting the commit
+ * before they can be used. And the in-the-gaps method will grab these
+ * if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have
+ * already been allocated for some purpose.
+ *
+ * Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because
+ * these LEBs are empty) and to @c->lst.taken_empty_lebs (because they
+ * are taken until after the commit).
+ *
+ * Note, @c->lst.taken_empty_lebs may temporarily be higher by one
+ * because of the way we serialize LEB allocations and budgeting. See a
+ * comment in 'ubifs_find_free_space()'.
+ */
+ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+ c->lst.taken_empty_lebs;
+ if (unlikely(rsvd_idx_lebs > lebs)) {
+ dbg_budg("out of indexing space: min_idx_lebs %d (old %d), rsvd_idx_lebs %d",
+ min_idx_lebs, c->bi.min_idx_lebs, rsvd_idx_lebs);
+ return -ENOSPC;
+ }
+
+ available = ubifs_calc_available(c, min_idx_lebs);
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+
+ if (unlikely(available < outstanding)) {
+ dbg_budg("out of data space: available %lld, outstanding %lld",
+ available, outstanding);
+ return -ENOSPC;
+ }
+
+ if (available - outstanding <= c->rp_size && !can_use_rp(c))
+ return -ENOSPC;
+
+ c->bi.min_idx_lebs = min_idx_lebs;
+ return 0;
+}
+
+/**
+ * calc_idx_growth - calculate approximate index growth from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ *
+ * For now we assume each new node adds one znode. But this is rather poor
+ * approximation, though.
+ */
+static int calc_idx_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int znodes;
+
+ znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) +
+ req->new_dent;
+ return znodes * c->max_idx_node_sz;
+}
+
+/**
+ * calc_data_growth - calculate approximate amount of new data from budgeting
+ * request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_data_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int data_growth;
+
+ data_growth = req->new_ino ? c->bi.inode_budget : 0;
+ if (req->new_page)
+ data_growth += c->bi.page_budget;
+ if (req->new_dent)
+ data_growth += c->bi.dent_budget;
+ data_growth += req->new_ino_d;
+ return data_growth;
+}
+
+/**
+ * calc_dd_growth - calculate approximate amount of data which makes other data
+ * dirty from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_dd_growth(const struct ubifs_info *c,
+ const struct ubifs_budget_req *req)
+{
+ int dd_growth;
+
+ dd_growth = req->dirtied_page ? c->bi.page_budget : 0;
+
+ if (req->dirtied_ino)
+ dd_growth += c->bi.inode_budget * req->dirtied_ino;
+ if (req->mod_dent)
+ dd_growth += c->bi.dent_budget;
+ dd_growth += req->dirtied_ino_d;
+ return dd_growth;
+}
+
+/**
+ * ubifs_budget_space - ensure there is enough space to complete an operation.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function allocates budget for an operation. It uses pessimistic
+ * approximation of how much flash space the operation needs. The goal of this
+ * function is to make sure UBIFS always has flash space to flush all dirty
+ * pages, dirty inodes, and dirty znodes (liability). This function may force
+ * commit, garbage-collection or write-back. Returns zero in case of success,
+ * %-ENOSPC if there is no free space and other negative error codes in case of
+ * failures.
+ */
+int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+ int err, idx_growth, data_growth, dd_growth, retried = 0;
+
+ ubifs_assert(c, req->new_page <= 1);
+ ubifs_assert(c, req->dirtied_page <= 1);
+ ubifs_assert(c, req->new_dent <= 1);
+ ubifs_assert(c, req->mod_dent <= 1);
+ ubifs_assert(c, req->new_ino <= 1);
+ ubifs_assert(c, req->new_ino_d <= UBIFS_MAX_INO_DATA);
+ ubifs_assert(c, req->dirtied_ino <= 4);
+ ubifs_assert(c, req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+ ubifs_assert(c, !(req->new_ino_d & 7));
+ ubifs_assert(c, !(req->dirtied_ino_d & 7));
+
+ data_growth = calc_data_growth(c, req);
+ dd_growth = calc_dd_growth(c, req);
+ if (!data_growth && !dd_growth)
+ return 0;
+ idx_growth = calc_idx_growth(c, req);
+
+again:
+ spin_lock(&c->space_lock);
+ ubifs_assert(c, c->bi.idx_growth >= 0);
+ ubifs_assert(c, c->bi.data_growth >= 0);
+ ubifs_assert(c, c->bi.dd_growth >= 0);
+
+ if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) {
+ dbg_budg("no space");
+ spin_unlock(&c->space_lock);
+ return -ENOSPC;
+ }
+
+ c->bi.idx_growth += idx_growth;
+ c->bi.data_growth += data_growth;
+ c->bi.dd_growth += dd_growth;
+
+ err = do_budget_space(c);
+ if (likely(!err)) {
+ req->idx_growth = idx_growth;
+ req->data_growth = data_growth;
+ req->dd_growth = dd_growth;
+ spin_unlock(&c->space_lock);
+ return 0;
+ }
+
+ /* Restore the old values */
+ c->bi.idx_growth -= idx_growth;
+ c->bi.data_growth -= data_growth;
+ c->bi.dd_growth -= dd_growth;
+ spin_unlock(&c->space_lock);
+
+ if (req->fast) {
+ dbg_budg("no space for fast budgeting");
+ return err;
+ }
+
+ err = make_free_space(c);
+ cond_resched();
+ if (err == -EAGAIN) {
+ dbg_budg("try again");
+ goto again;
+ } else if (err == -ENOSPC) {
+ if (!retried) {
+ retried = 1;
+ dbg_budg("-ENOSPC, but anyway try once again");
+ goto again;
+ }
+ dbg_budg("FS is full, -ENOSPC");
+ c->bi.nospace = 1;
+ if (can_use_rp(c) || c->rp_size == 0)
+ c->bi.nospace_rp = 1;
+ smp_wmb();
+ } else
+ ubifs_err(c, "cannot budget space, error %d", err);
+ return err;
+}
+
+/**
+ * ubifs_release_budget - release budgeted free space.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function releases the space budgeted by 'ubifs_budget_space()'. Note,
+ * since the index changes (which were budgeted for in @req->idx_growth) will
+ * only be written to the media on commit, this function moves the index budget
+ * from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed
+ * by the commit operation.
+ */
+void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+ ubifs_assert(c, req->new_page <= 1);
+ ubifs_assert(c, req->dirtied_page <= 1);
+ ubifs_assert(c, req->new_dent <= 1);
+ ubifs_assert(c, req->mod_dent <= 1);
+ ubifs_assert(c, req->new_ino <= 1);
+ ubifs_assert(c, req->new_ino_d <= UBIFS_MAX_INO_DATA);
+ ubifs_assert(c, req->dirtied_ino <= 4);
+ ubifs_assert(c, req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+ ubifs_assert(c, !(req->new_ino_d & 7));
+ ubifs_assert(c, !(req->dirtied_ino_d & 7));
+ if (!req->recalculate) {
+ ubifs_assert(c, req->idx_growth >= 0);
+ ubifs_assert(c, req->data_growth >= 0);
+ ubifs_assert(c, req->dd_growth >= 0);
+ }
+
+ if (req->recalculate) {
+ req->data_growth = calc_data_growth(c, req);
+ req->dd_growth = calc_dd_growth(c, req);
+ req->idx_growth = calc_idx_growth(c, req);
+ }
+
+ if (!req->data_growth && !req->dd_growth)
+ return;
+
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+
+ spin_lock(&c->space_lock);
+ c->bi.idx_growth -= req->idx_growth;
+ c->bi.uncommitted_idx += req->idx_growth;
+ c->bi.data_growth -= req->data_growth;
+ c->bi.dd_growth -= req->dd_growth;
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ ubifs_assert(c, c->bi.idx_growth >= 0);
+ ubifs_assert(c, c->bi.data_growth >= 0);
+ ubifs_assert(c, c->bi.dd_growth >= 0);
+ ubifs_assert(c, c->bi.min_idx_lebs < c->main_lebs);
+ ubifs_assert(c, !(c->bi.idx_growth & 7));
+ ubifs_assert(c, !(c->bi.data_growth & 7));
+ ubifs_assert(c, !(c->bi.dd_growth & 7));
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_convert_page_budget - convert budget of a new page.
+ * @c: UBIFS file-system description object
+ *
+ * This function converts budget which was allocated for a new page of data to
+ * the budget of changing an existing page of data. The latter is smaller than
+ * the former, so this function only does simple re-calculation and does not
+ * involve any write-back.
+ */
+void ubifs_convert_page_budget(struct ubifs_info *c)
+{
+ spin_lock(&c->space_lock);
+ /* Release the index growth reservation */
+ c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+ /* Release the data growth reservation */
+ c->bi.data_growth -= c->bi.page_budget;
+ /* Increase the dirty data growth reservation instead */
+ c->bi.dd_growth += c->bi.page_budget;
+ /* And re-calculate the indexing space reservation */
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_release_dirty_inode_budget - release dirty inode budget.
+ * @c: UBIFS file-system description object
+ * @ui: UBIFS inode to release the budget for
+ *
+ * This function releases budget corresponding to a dirty inode. It is usually
+ * called when after the inode has been written to the media and marked as
+ * clean. It also causes the "no space" flags to be cleared.
+ */
+void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
+ struct ubifs_inode *ui)
+{
+ struct ubifs_budget_req req;
+
+ memset(&req, 0, sizeof(struct ubifs_budget_req));
+ /* The "no space" flags will be cleared because dd_growth is > 0 */
+ req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8);
+ ubifs_release_budget(c, &req);
+}
+
+/**
+ * ubifs_reported_space - calculate reported free space.
+ * @c: the UBIFS file-system description object
+ * @free: amount of free space
+ *
+ * This function calculates amount of free space which will be reported to
+ * user-space. User-space application tend to expect that if the file-system
+ * (e.g., via the 'statfs()' call) reports that it has N bytes available, they
+ * are able to write a file of size N. UBIFS attaches node headers to each data
+ * node and it has to write indexing nodes as well. This introduces additional
+ * overhead, and UBIFS has to report slightly less free space to meet the above
+ * expectations.
+ *
+ * This function assumes free space is made up of uncompressed data nodes and
+ * full index nodes (one per data node, tripled because we always allow enough
+ * space to write the index thrice).
+ *
+ * Note, the calculation is pessimistic, which means that most of the time
+ * UBIFS reports less space than it actually has.
+ */
+long long ubifs_reported_space(const struct ubifs_info *c, long long free)
+{
+ int divisor, factor, f;
+
+ /*
+ * Reported space size is @free * X, where X is UBIFS block size
+ * divided by UBIFS block size + all overhead one data block
+ * introduces. The overhead is the node header + indexing overhead.
+ *
+ * Indexing overhead calculations are based on the following formula:
+ * I = N/(f - 1) + 1, where I - number of indexing nodes, N - number
+ * of data nodes, f - fanout. Because effective UBIFS fanout is twice
+ * as less than maximum fanout, we assume that each data node
+ * introduces 3 * @c->max_idx_node_sz / (@c->fanout/2 - 1) bytes.
+ * Note, the multiplier 3 is because UBIFS reserves thrice as more space
+ * for the index.
+ */
+ f = c->fanout > 3 ? c->fanout >> 1 : 2;
+ factor = UBIFS_BLOCK_SIZE;
+ divisor = UBIFS_MAX_DATA_NODE_SZ;
+ divisor += (c->max_idx_node_sz * 3) / (f - 1);
+ free *= factor;
+ return div_u64(free, divisor);
+}
+
+/**
+ * ubifs_get_free_space_nolock - return amount of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates amount of free space to report to user-space.
+ *
+ * Because UBIFS may introduce substantial overhead (the index, node headers,
+ * alignment, wastage at the end of LEBs, etc), it cannot report real amount of
+ * free flash space it has (well, because not all dirty space is reclaimable,
+ * UBIFS does not actually know the real amount). If UBIFS did so, it would
+ * bread user expectations about what free space is. Users seem to accustomed
+ * to assume that if the file-system reports N bytes of free space, they would
+ * be able to fit a file of N bytes to the FS. This almost works for
+ * traditional file-systems, because they have way less overhead than UBIFS.
+ * So, to keep users happy, UBIFS tries to take the overhead into account.
+ */
+long long ubifs_get_free_space_nolock(struct ubifs_info *c)
+{
+ int rsvd_idx_lebs, lebs;
+ long long available, outstanding, free;
+
+ ubifs_assert(c, c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+ available = ubifs_calc_available(c, c->bi.min_idx_lebs);
+
+ /*
+ * When reporting free space to user-space, UBIFS guarantees that it is
+ * possible to write a file of free space size. This means that for
+ * empty LEBs we may use more precise calculations than
+ * 'ubifs_calc_available()' is using. Namely, we know that in empty
+ * LEBs we would waste only @c->leb_overhead bytes, not @c->dark_wm.
+ * Thus, amend the available space.
+ *
+ * Note, the calculations below are similar to what we have in
+ * 'do_budget_space()', so refer there for comments.
+ */
+ if (c->bi.min_idx_lebs > c->lst.idx_lebs)
+ rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
+ else
+ rsvd_idx_lebs = 0;
+ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+ c->lst.taken_empty_lebs;
+ lebs -= rsvd_idx_lebs;
+ available += lebs * (c->dark_wm - c->leb_overhead);
+
+ if (available > outstanding)
+ free = ubifs_reported_space(c, available - outstanding);
+ else
+ free = 0;
+ return free;
+}
+
+/**
+ * ubifs_get_free_space - return amount of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates and returns amount of free space to report to
+ * user-space.
+ */
+long long ubifs_get_free_space(struct ubifs_info *c)
+{
+ long long free;
+
+ spin_lock(&c->space_lock);
+ free = ubifs_get_free_space_nolock(c);
+ spin_unlock(&c->space_lock);
+
+ return free;
+}
diff --git a/ubifs-utils/libubifs/commit.c b/ubifs-utils/libubifs/commit.c
new file mode 100644
index 00000000..5b3a8400
--- /dev/null
+++ b/ubifs-utils/libubifs/commit.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements functions that manage the running of the commit process.
+ * Each affected module has its own functions to accomplish their part in the
+ * commit and those functions are called here.
+ *
+ * The commit is the process whereby all updates to the index and LEB properties
+ * are written out together and the journal becomes empty. This keeps the
+ * file system consistent - at all times the state can be recreated by reading
+ * the index and LEB properties and then replaying the journal.
+ *
+ * The commit is split into two parts named "commit start" and "commit end".
+ * During commit start, the commit process has exclusive access to the journal
+ * by holding the commit semaphore down for writing. As few I/O operations as
+ * possible are performed during commit start, instead the nodes that are to be
+ * written are merely identified. During commit end, the commit semaphore is no
+ * longer held and the journal is again in operation, allowing users to continue
+ * to use the file system while the bulk of the commit I/O is performed. The
+ * purpose of this two-step approach is to prevent the commit from causing any
+ * latency blips. Note that in any case, the commit does not prevent lookups
+ * (as permitted by the TNC mutex), or access to VFS data structures e.g. page
+ * cache.
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include "ubifs.h"
+
+/*
+ * nothing_to_commit - check if there is nothing to commit.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which checks if there is anything to commit. It is
+ * used as an optimization to avoid starting the commit if it is not really
+ * necessary. Indeed, the commit operation always assumes flash I/O (e.g.,
+ * writing the commit start node to the log), and it is better to avoid doing
+ * this unnecessarily. E.g., 'ubifs_sync_fs()' runs the commit, but if there is
+ * nothing to commit, it is more optimal to avoid any flash I/O.
+ *
+ * This function has to be called with @c->commit_sem locked for writing -
+ * this function does not take LPT/TNC locks because the @c->commit_sem
+ * guarantees that we have exclusive access to the TNC and LPT data structures.
+ *
+ * This function returns %1 if there is nothing to commit and %0 otherwise.
+ */
+static int nothing_to_commit(struct ubifs_info *c)
+{
+ /*
+ * During mounting or remounting from R/O mode to R/W mode we may
+ * commit for various recovery-related reasons.
+ */
+ if (c->mounting || c->remounting_rw)
+ return 0;
+
+ /*
+ * If the root TNC node is dirty, we definitely have something to
+ * commit.
+ */
+ if (c->zroot.znode && ubifs_zn_dirty(c->zroot.znode))
+ return 0;
+
+ /*
+ * Increasing @c->dirty_pn_cnt/@c->dirty_nn_cnt and marking
+ * nnodes/pnodes as dirty in run_gc() could race with following
+ * checking, which leads inconsistent states between @c->nroot
+ * and @c->dirty_pn_cnt/@c->dirty_nn_cnt, holding @c->lp_mutex
+ * to avoid that.
+ */
+ mutex_lock(&c->lp_mutex);
+ /*
+ * Even though the TNC is clean, the LPT tree may have dirty nodes. For
+ * example, this may happen if the budgeting subsystem invoked GC to
+ * make some free space, and the GC found an LEB with only dirty and
+ * free space. In this case GC would just change the lprops of this
+ * LEB (by turning all space into free space) and unmap it.
+ */
+ if (c->nroot && test_bit(DIRTY_CNODE, &c->nroot->flags)) {
+ mutex_unlock(&c->lp_mutex);
+ return 0;
+ }
+
+ ubifs_assert(c, atomic_long_read(&c->dirty_zn_cnt) == 0);
+ ubifs_assert(c, c->dirty_pn_cnt == 0);
+ ubifs_assert(c, c->dirty_nn_cnt == 0);
+ mutex_unlock(&c->lp_mutex);
+
+ return 1;
+}
+
+/**
+ * do_commit - commit the journal.
+ * @c: UBIFS file-system description object
+ *
+ * This function implements UBIFS commit. It has to be called with commit lock
+ * locked. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int do_commit(struct ubifs_info *c)
+{
+ int err, new_ltail_lnum, old_ltail_lnum, i;
+ struct ubifs_zbranch zroot;
+ struct ubifs_lp_stats lst;
+
+ dbg_cmt("start");
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_up;
+ }
+
+ if (nothing_to_commit(c)) {
+ up_write(&c->commit_sem);
+ err = 0;
+ goto out_cancel;
+ }
+
+ /* Sync all write buffers (necessary for recovery) */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ goto out_up;
+ }
+
+ c->cmt_no += 1;
+ err = ubifs_gc_start_commit(c);
+ if (err)
+ goto out_up;
+ err = dbg_check_lprops(c);
+ if (err)
+ goto out_up;
+ err = ubifs_log_start_commit(c, &new_ltail_lnum);
+ if (err)
+ goto out_up;
+ err = ubifs_tnc_start_commit(c, &zroot);
+ if (err)
+ goto out_up;
+ err = ubifs_lpt_start_commit(c);
+ if (err)
+ goto out_up;
+ err = ubifs_orphan_start_commit(c);
+ if (err)
+ goto out_up;
+
+ ubifs_get_lp_stats(c, &lst);
+
+ up_write(&c->commit_sem);
+
+ err = ubifs_tnc_end_commit(c);
+ if (err)
+ goto out;
+ err = ubifs_lpt_end_commit(c);
+ if (err)
+ goto out;
+ err = ubifs_orphan_end_commit(c);
+ if (err)
+ goto out;
+ err = dbg_check_old_index(c, &zroot);
+ if (err)
+ goto out;
+
+ c->mst_node->cmt_no = cpu_to_le64(c->cmt_no);
+ c->mst_node->log_lnum = cpu_to_le32(new_ltail_lnum);
+ c->mst_node->root_lnum = cpu_to_le32(zroot.lnum);
+ c->mst_node->root_offs = cpu_to_le32(zroot.offs);
+ c->mst_node->root_len = cpu_to_le32(zroot.len);
+ c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum);
+ c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs);
+ c->mst_node->index_size = cpu_to_le64(c->bi.old_idx_sz);
+ c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum);
+ c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs);
+ c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum);
+ c->mst_node->nhead_offs = cpu_to_le32(c->nhead_offs);
+ c->mst_node->ltab_lnum = cpu_to_le32(c->ltab_lnum);
+ c->mst_node->ltab_offs = cpu_to_le32(c->ltab_offs);
+ c->mst_node->lsave_lnum = cpu_to_le32(c->lsave_lnum);
+ c->mst_node->lsave_offs = cpu_to_le32(c->lsave_offs);
+ c->mst_node->lscan_lnum = cpu_to_le32(c->lscan_lnum);
+ c->mst_node->empty_lebs = cpu_to_le32(lst.empty_lebs);
+ c->mst_node->idx_lebs = cpu_to_le32(lst.idx_lebs);
+ c->mst_node->total_free = cpu_to_le64(lst.total_free);
+ c->mst_node->total_dirty = cpu_to_le64(lst.total_dirty);
+ c->mst_node->total_used = cpu_to_le64(lst.total_used);
+ c->mst_node->total_dead = cpu_to_le64(lst.total_dead);
+ c->mst_node->total_dark = cpu_to_le64(lst.total_dark);
+ if (c->no_orphs)
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+ else
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_NO_ORPHS);
+
+ old_ltail_lnum = c->ltail_lnum;
+ err = ubifs_log_end_commit(c, new_ltail_lnum);
+ if (err)
+ goto out;
+
+ err = ubifs_log_post_commit(c, old_ltail_lnum);
+ if (err)
+ goto out;
+ err = ubifs_gc_end_commit(c);
+ if (err)
+ goto out;
+ err = ubifs_lpt_post_commit(c);
+ if (err)
+ goto out;
+
+out_cancel:
+ spin_lock(&c->cs_lock);
+ c->cmt_state = COMMIT_RESTING;
+ wake_up(&c->cmt_wq);
+ dbg_cmt("commit end");
+ spin_unlock(&c->cs_lock);
+ return 0;
+
+out_up:
+ up_write(&c->commit_sem);
+out:
+ ubifs_err(c, "commit failed, error %d", err);
+ spin_lock(&c->cs_lock);
+ c->cmt_state = COMMIT_BROKEN;
+ wake_up(&c->cmt_wq);
+ spin_unlock(&c->cs_lock);
+ ubifs_ro_mode(c, err);
+ return err;
+}
+
+/**
+ * run_bg_commit - run background commit if it is needed.
+ * @c: UBIFS file-system description object
+ *
+ * This function runs background commit if it is needed. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int run_bg_commit(struct ubifs_info *c)
+{
+ spin_lock(&c->cs_lock);
+ /*
+ * Run background commit only if background commit was requested or if
+ * commit is required.
+ */
+ if (c->cmt_state != COMMIT_BACKGROUND &&
+ c->cmt_state != COMMIT_REQUIRED)
+ goto out;
+ spin_unlock(&c->cs_lock);
+
+ down_write(&c->commit_sem);
+ spin_lock(&c->cs_lock);
+ if (c->cmt_state == COMMIT_REQUIRED)
+ c->cmt_state = COMMIT_RUNNING_REQUIRED;
+ else if (c->cmt_state == COMMIT_BACKGROUND)
+ c->cmt_state = COMMIT_RUNNING_BACKGROUND;
+ else
+ goto out_cmt_unlock;
+ spin_unlock(&c->cs_lock);
+
+ return do_commit(c);
+
+out_cmt_unlock:
+ up_write(&c->commit_sem);
+out:
+ spin_unlock(&c->cs_lock);
+ return 0;
+}
+
+/**
+ * ubifs_bg_thread - UBIFS background thread function.
+ * @info: points to the file-system description object
+ *
+ * This function implements various file-system background activities:
+ * o when a write-buffer timer expires it synchronizes the appropriate
+ * write-buffer;
+ * o when the journal is about to be full, it starts in-advance commit.
+ *
+ * Note, other stuff like background garbage collection may be added here in
+ * future.
+ */
+int ubifs_bg_thread(void *info)
+{
+ int err;
+ struct ubifs_info *c = info;
+
+ ubifs_msg(c, "background thread \"%s\" started, PID %d",
+ c->bgt_name, current->pid);
+ set_freezable();
+
+ while (1) {
+ if (kthread_should_stop())
+ break;
+
+ if (try_to_freeze())
+ continue;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ /* Check if there is something to do */
+ if (!c->need_bgt) {
+ /*
+ * Nothing prevents us from going sleep now and
+ * be never woken up and block the task which
+ * could wait in 'kthread_stop()' forever.
+ */
+ if (kthread_should_stop())
+ break;
+ schedule();
+ continue;
+ } else
+ __set_current_state(TASK_RUNNING);
+
+ c->need_bgt = 0;
+ err = ubifs_bg_wbufs_sync(c);
+ if (err)
+ ubifs_ro_mode(c, err);
+
+ run_bg_commit(c);
+ cond_resched();
+ }
+
+ ubifs_msg(c, "background thread \"%s\" stops", c->bgt_name);
+ return 0;
+}
+
+/**
+ * ubifs_commit_required - set commit state to "required".
+ * @c: UBIFS file-system description object
+ *
+ * This function is called if a commit is required but cannot be done from the
+ * calling function, so it is just flagged instead.
+ */
+void ubifs_commit_required(struct ubifs_info *c)
+{
+ spin_lock(&c->cs_lock);
+ switch (c->cmt_state) {
+ case COMMIT_RESTING:
+ case COMMIT_BACKGROUND:
+ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
+ dbg_cstate(COMMIT_REQUIRED));
+ c->cmt_state = COMMIT_REQUIRED;
+ break;
+ case COMMIT_RUNNING_BACKGROUND:
+ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
+ dbg_cstate(COMMIT_RUNNING_REQUIRED));
+ c->cmt_state = COMMIT_RUNNING_REQUIRED;
+ break;
+ case COMMIT_REQUIRED:
+ case COMMIT_RUNNING_REQUIRED:
+ case COMMIT_BROKEN:
+ break;
+ }
+ spin_unlock(&c->cs_lock);
+}
+
+/**
+ * ubifs_request_bg_commit - notify the background thread to do a commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called if the journal is full enough to make a commit
+ * worthwhile, so background thread is kicked to start it.
+ */
+void ubifs_request_bg_commit(struct ubifs_info *c)
+{
+ spin_lock(&c->cs_lock);
+ if (c->cmt_state == COMMIT_RESTING) {
+ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
+ dbg_cstate(COMMIT_BACKGROUND));
+ c->cmt_state = COMMIT_BACKGROUND;
+ spin_unlock(&c->cs_lock);
+ ubifs_wake_up_bgt(c);
+ } else
+ spin_unlock(&c->cs_lock);
+}
+
+/**
+ * wait_for_commit - wait for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function sleeps until the commit operation is no longer running.
+ */
+static int wait_for_commit(struct ubifs_info *c)
+{
+ dbg_cmt("pid %d goes sleep", current->pid);
+
+ /*
+ * The following sleeps if the condition is false, and will be woken
+ * when the commit ends. It is possible, although very unlikely, that we
+ * will wake up and see the subsequent commit running, rather than the
+ * one we were waiting for, and go back to sleep. However, we will be
+ * woken again, so there is no danger of sleeping forever.
+ */
+ wait_event(c->cmt_wq, c->cmt_state != COMMIT_RUNNING_BACKGROUND &&
+ c->cmt_state != COMMIT_RUNNING_REQUIRED);
+ dbg_cmt("commit finished, pid %d woke up", current->pid);
+ return 0;
+}
+
+/**
+ * ubifs_run_commit - run or wait for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function runs commit and returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubifs_run_commit(struct ubifs_info *c)
+{
+ int err = 0;
+
+ spin_lock(&c->cs_lock);
+ if (c->cmt_state == COMMIT_BROKEN) {
+ err = -EROFS;
+ goto out;
+ }
+
+ if (c->cmt_state == COMMIT_RUNNING_BACKGROUND)
+ /*
+ * We set the commit state to 'running required' to indicate
+ * that we want it to complete as quickly as possible.
+ */
+ c->cmt_state = COMMIT_RUNNING_REQUIRED;
+
+ if (c->cmt_state == COMMIT_RUNNING_REQUIRED) {
+ spin_unlock(&c->cs_lock);
+ return wait_for_commit(c);
+ }
+ spin_unlock(&c->cs_lock);
+
+ /* Ok, the commit is indeed needed */
+
+ down_write(&c->commit_sem);
+ spin_lock(&c->cs_lock);
+ /*
+ * Since we unlocked 'c->cs_lock', the state may have changed, so
+ * re-check it.
+ */
+ if (c->cmt_state == COMMIT_BROKEN) {
+ err = -EROFS;
+ goto out_cmt_unlock;
+ }
+
+ if (c->cmt_state == COMMIT_RUNNING_BACKGROUND)
+ c->cmt_state = COMMIT_RUNNING_REQUIRED;
+
+ if (c->cmt_state == COMMIT_RUNNING_REQUIRED) {
+ up_write(&c->commit_sem);
+ spin_unlock(&c->cs_lock);
+ return wait_for_commit(c);
+ }
+ c->cmt_state = COMMIT_RUNNING_REQUIRED;
+ spin_unlock(&c->cs_lock);
+
+ err = do_commit(c);
+ return err;
+
+out_cmt_unlock:
+ up_write(&c->commit_sem);
+out:
+ spin_unlock(&c->cs_lock);
+ return err;
+}
+
+/**
+ * ubifs_gc_should_commit - determine if it is time for GC to run commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called by garbage collection to determine if commit should
+ * be run. If commit state is @COMMIT_BACKGROUND, which means that the journal
+ * is full enough to start commit, this function returns true. It is not
+ * absolutely necessary to commit yet, but it feels like this should be better
+ * then to keep doing GC. This function returns %1 if GC has to initiate commit
+ * and %0 if not.
+ */
+int ubifs_gc_should_commit(struct ubifs_info *c)
+{
+ int ret = 0;
+
+ spin_lock(&c->cs_lock);
+ if (c->cmt_state == COMMIT_BACKGROUND) {
+ dbg_cmt("commit required now");
+ c->cmt_state = COMMIT_REQUIRED;
+ } else
+ dbg_cmt("commit not requested");
+ if (c->cmt_state == COMMIT_REQUIRED)
+ ret = 1;
+ spin_unlock(&c->cs_lock);
+ return ret;
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+/**
+ * struct idx_node - hold index nodes during index tree traversal.
+ * @list: list
+ * @iip: index in parent (slot number of this indexing node in the parent
+ * indexing node)
+ * @upper_key: all keys in this indexing node have to be less or equivalent to
+ * this key
+ * @idx: index node (8-byte aligned because all node structures must be 8-byte
+ * aligned)
+ */
+struct idx_node {
+ struct list_head list;
+ int iip;
+ union ubifs_key upper_key;
+ struct ubifs_idx_node idx __aligned(8);
+};
+
+/**
+ * dbg_old_index_check_init - get information for the next old index check.
+ * @c: UBIFS file-system description object
+ * @zroot: root of the index
+ *
+ * This function records information about the index that will be needed for the
+ * next old index check i.e. 'dbg_check_old_index()'.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot)
+{
+ struct ubifs_idx_node *idx;
+ int lnum, offs, len, err = 0;
+ struct ubifs_debug_info *d = c->dbg;
+
+ d->old_zroot = *zroot;
+ lnum = d->old_zroot.lnum;
+ offs = d->old_zroot.offs;
+ len = d->old_zroot.len;
+
+ idx = kmalloc(c->max_idx_node_sz, GFP_NOFS);
+ if (!idx)
+ return -ENOMEM;
+
+ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
+ if (err)
+ goto out;
+
+ d->old_zroot_level = le16_to_cpu(idx->level);
+ d->old_zroot_sqnum = le64_to_cpu(idx->ch.sqnum);
+out:
+ kfree(idx);
+ return err;
+}
+
+/**
+ * dbg_check_old_index - check the old copy of the index.
+ * @c: UBIFS file-system description object
+ * @zroot: root of the new index
+ *
+ * In order to be able to recover from an unclean unmount, a complete copy of
+ * the index must exist on flash. This is the "old" index. The commit process
+ * must write the "new" index to flash without overwriting or destroying any
+ * part of the old index. This function is run at commit end in order to check
+ * that the old index does indeed exist completely intact.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot)
+{
+ int lnum, offs, len, err = 0, last_level, child_cnt;
+ int first = 1, iip;
+ struct ubifs_debug_info *d = c->dbg;
+ union ubifs_key lower_key, upper_key, l_key, u_key;
+ unsigned long long last_sqnum;
+ struct ubifs_idx_node *idx;
+ struct list_head list;
+ struct idx_node *i;
+ size_t sz;
+
+ if (!dbg_is_chk_index(c))
+ return 0;
+
+ INIT_LIST_HEAD(&list);
+
+ sz = sizeof(struct idx_node) + ubifs_idx_node_sz(c, c->fanout) -
+ UBIFS_IDX_NODE_SZ;
+
+ /* Start at the old zroot */
+ lnum = d->old_zroot.lnum;
+ offs = d->old_zroot.offs;
+ len = d->old_zroot.len;
+ iip = 0;
+
+ /*
+ * Traverse the index tree preorder depth-first i.e. do a node and then
+ * its subtrees from left to right.
+ */
+ while (1) {
+ struct ubifs_branch *br;
+
+ /* Get the next index node */
+ i = kmalloc(sz, GFP_NOFS);
+ if (!i) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ i->iip = iip;
+ /* Keep the index nodes on our path in a linked list */
+ list_add_tail(&i->list, &list);
+ /* Read the index node */
+ idx = &i->idx;
+ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
+ if (err)
+ goto out_free;
+ /* Validate index node */
+ child_cnt = le16_to_cpu(idx->child_cnt);
+ if (child_cnt < 1 || child_cnt > c->fanout) {
+ err = 1;
+ goto out_dump;
+ }
+ if (first) {
+ first = 0;
+ /* Check root level and sqnum */
+ if (le16_to_cpu(idx->level) != d->old_zroot_level) {
+ err = 2;
+ goto out_dump;
+ }
+ if (le64_to_cpu(idx->ch.sqnum) != d->old_zroot_sqnum) {
+ err = 3;
+ goto out_dump;
+ }
+ /* Set last values as though root had a parent */
+ last_level = le16_to_cpu(idx->level) + 1;
+ last_sqnum = le64_to_cpu(idx->ch.sqnum) + 1;
+ key_read(c, ubifs_idx_key(c, idx), &lower_key);
+ highest_ino_key(c, &upper_key, INUM_WATERMARK);
+ }
+ key_copy(c, &upper_key, &i->upper_key);
+ if (le16_to_cpu(idx->level) != last_level - 1) {
+ err = 3;
+ goto out_dump;
+ }
+ /*
+ * The index is always written bottom up hence a child's sqnum
+ * is always less than the parents.
+ */
+ if (le64_to_cpu(idx->ch.sqnum) >= last_sqnum) {
+ err = 4;
+ goto out_dump;
+ }
+ /* Check key range */
+ key_read(c, ubifs_idx_key(c, idx), &l_key);
+ br = ubifs_idx_branch(c, idx, child_cnt - 1);
+ key_read(c, &br->key, &u_key);
+ if (keys_cmp(c, &lower_key, &l_key) > 0) {
+ err = 5;
+ goto out_dump;
+ }
+ if (keys_cmp(c, &upper_key, &u_key) < 0) {
+ err = 6;
+ goto out_dump;
+ }
+ if (keys_cmp(c, &upper_key, &u_key) == 0)
+ if (!is_hash_key(c, &u_key)) {
+ err = 7;
+ goto out_dump;
+ }
+ /* Go to next index node */
+ if (le16_to_cpu(idx->level) == 0) {
+ /* At the bottom, so go up until can go right */
+ while (1) {
+ /* Drop the bottom of the list */
+ list_del(&i->list);
+ kfree(i);
+ /* No more list means we are done */
+ if (list_empty(&list))
+ goto out;
+ /* Look at the new bottom */
+ i = list_entry(list.prev, struct idx_node,
+ list);
+ idx = &i->idx;
+ /* Can we go right */
+ if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
+ iip = iip + 1;
+ break;
+ } else
+ /* Nope, so go up again */
+ iip = i->iip;
+ }
+ } else
+ /* Go down left */
+ iip = 0;
+ /*
+ * We have the parent in 'idx' and now we set up for reading the
+ * child pointed to by slot 'iip'.
+ */
+ last_level = le16_to_cpu(idx->level);
+ last_sqnum = le64_to_cpu(idx->ch.sqnum);
+ br = ubifs_idx_branch(c, idx, iip);
+ lnum = le32_to_cpu(br->lnum);
+ offs = le32_to_cpu(br->offs);
+ len = le32_to_cpu(br->len);
+ key_read(c, &br->key, &lower_key);
+ if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
+ br = ubifs_idx_branch(c, idx, iip + 1);
+ key_read(c, &br->key, &upper_key);
+ } else
+ key_copy(c, &i->upper_key, &upper_key);
+ }
+out:
+ err = dbg_old_index_check_init(c, zroot);
+ if (err)
+ goto out_free;
+
+ return 0;
+
+out_dump:
+ ubifs_err(c, "dumping index node (iip=%d)", i->iip);
+ ubifs_dump_node(c, idx, ubifs_idx_node_sz(c, c->fanout));
+ list_del(&i->list);
+ kfree(i);
+ if (!list_empty(&list)) {
+ i = list_entry(list.prev, struct idx_node, list);
+ ubifs_err(c, "dumping parent index node");
+ ubifs_dump_node(c, &i->idx, ubifs_idx_node_sz(c, c->fanout));
+ }
+out_free:
+ while (!list_empty(&list)) {
+ i = list_entry(list.next, struct idx_node, list);
+ list_del(&i->list);
+ kfree(i);
+ }
+ ubifs_err(c, "failed, error %d", err);
+ if (err > 0)
+ err = -EINVAL;
+ return err;
+}
diff --git a/ubifs-utils/libubifs/debug.c b/ubifs-utils/libubifs/debug.c
new file mode 100644
index 00000000..ac77ac1f
--- /dev/null
+++ b/ubifs-utils/libubifs/debug.c
@@ -0,0 +1,3051 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements most of the debugging stuff which is compiled in only
+ * when it is enabled. But some debugging check functions are implemented in
+ * corresponding subsystem, just because they are closely related and utilize
+ * various local functions of those subsystems.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/math64.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+#include "ubifs.h"
+
+static DEFINE_SPINLOCK(dbg_lock);
+
+static const char *get_key_fmt(int fmt)
+{
+ switch (fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ return "simple";
+ default:
+ return "unknown/invalid format";
+ }
+}
+
+static const char *get_key_hash(int hash)
+{
+ switch (hash) {
+ case UBIFS_KEY_HASH_R5:
+ return "R5";
+ case UBIFS_KEY_HASH_TEST:
+ return "test";
+ default:
+ return "unknown/invalid name hash";
+ }
+}
+
+static const char *get_key_type(int type)
+{
+ switch (type) {
+ case UBIFS_INO_KEY:
+ return "inode";
+ case UBIFS_DENT_KEY:
+ return "direntry";
+ case UBIFS_XENT_KEY:
+ return "xentry";
+ case UBIFS_DATA_KEY:
+ return "data";
+ case UBIFS_TRUN_KEY:
+ return "truncate";
+ default:
+ return "unknown/invalid key";
+ }
+}
+
+static const char *get_dent_type(int type)
+{
+ switch (type) {
+ case UBIFS_ITYPE_REG:
+ return "file";
+ case UBIFS_ITYPE_DIR:
+ return "dir";
+ case UBIFS_ITYPE_LNK:
+ return "symlink";
+ case UBIFS_ITYPE_BLK:
+ return "blkdev";
+ case UBIFS_ITYPE_CHR:
+ return "char dev";
+ case UBIFS_ITYPE_FIFO:
+ return "fifo";
+ case UBIFS_ITYPE_SOCK:
+ return "socket";
+ default:
+ return "unknown/invalid type";
+ }
+}
+
+const char *dbg_snprintf_key(const struct ubifs_info *c,
+ const union ubifs_key *key, char *buffer, int len)
+{
+ char *p = buffer;
+ int type = key_type(c, key);
+
+ if (c->key_fmt == UBIFS_SIMPLE_KEY_FMT) {
+ switch (type) {
+ case UBIFS_INO_KEY:
+ len -= snprintf(p, len, "(%lu, %s)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type));
+ break;
+ case UBIFS_DENT_KEY:
+ case UBIFS_XENT_KEY:
+ len -= snprintf(p, len, "(%lu, %s, %#08x)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type), key_hash(c, key));
+ break;
+ case UBIFS_DATA_KEY:
+ len -= snprintf(p, len, "(%lu, %s, %u)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type), key_block(c, key));
+ break;
+ case UBIFS_TRUN_KEY:
+ len -= snprintf(p, len, "(%lu, %s)",
+ (unsigned long)key_inum(c, key),
+ get_key_type(type));
+ break;
+ default:
+ len -= snprintf(p, len, "(bad key type: %#08x, %#08x)",
+ key->u32[0], key->u32[1]);
+ }
+ } else
+ len -= snprintf(p, len, "bad key format %d", c->key_fmt);
+ ubifs_assert(c, len > 0);
+ return p;
+}
+
+const char *dbg_ntype(int type)
+{
+ switch (type) {
+ case UBIFS_PAD_NODE:
+ return "padding node";
+ case UBIFS_SB_NODE:
+ return "superblock node";
+ case UBIFS_MST_NODE:
+ return "master node";
+ case UBIFS_REF_NODE:
+ return "reference node";
+ case UBIFS_INO_NODE:
+ return "inode node";
+ case UBIFS_DENT_NODE:
+ return "direntry node";
+ case UBIFS_XENT_NODE:
+ return "xentry node";
+ case UBIFS_DATA_NODE:
+ return "data node";
+ case UBIFS_TRUN_NODE:
+ return "truncate node";
+ case UBIFS_IDX_NODE:
+ return "indexing node";
+ case UBIFS_CS_NODE:
+ return "commit start node";
+ case UBIFS_ORPH_NODE:
+ return "orphan node";
+ case UBIFS_AUTH_NODE:
+ return "auth node";
+ default:
+ return "unknown node";
+ }
+}
+
+static const char *dbg_gtype(int type)
+{
+ switch (type) {
+ case UBIFS_NO_NODE_GROUP:
+ return "no node group";
+ case UBIFS_IN_NODE_GROUP:
+ return "in node group";
+ case UBIFS_LAST_OF_NODE_GROUP:
+ return "last of node group";
+ default:
+ return "unknown";
+ }
+}
+
+const char *dbg_cstate(int cmt_state)
+{
+ switch (cmt_state) {
+ case COMMIT_RESTING:
+ return "commit resting";
+ case COMMIT_BACKGROUND:
+ return "background commit requested";
+ case COMMIT_REQUIRED:
+ return "commit required";
+ case COMMIT_RUNNING_BACKGROUND:
+ return "BACKGROUND commit running";
+ case COMMIT_RUNNING_REQUIRED:
+ return "commit running and required";
+ case COMMIT_BROKEN:
+ return "broken commit";
+ default:
+ return "unknown commit state";
+ }
+}
+
+const char *dbg_jhead(int jhead)
+{
+ switch (jhead) {
+ case GCHD:
+ return "0 (GC)";
+ case BASEHD:
+ return "1 (base)";
+ case DATAHD:
+ return "2 (data)";
+ default:
+ return "unknown journal head";
+ }
+}
+
+static void dump_ch(const struct ubifs_ch *ch)
+{
+ pr_err("\tmagic %#x\n", le32_to_cpu(ch->magic));
+ pr_err("\tcrc %#x\n", le32_to_cpu(ch->crc));
+ pr_err("\tnode_type %d (%s)\n", ch->node_type,
+ dbg_ntype(ch->node_type));
+ pr_err("\tgroup_type %d (%s)\n", ch->group_type,
+ dbg_gtype(ch->group_type));
+ pr_err("\tsqnum %llu\n",
+ (unsigned long long)le64_to_cpu(ch->sqnum));
+ pr_err("\tlen %u\n", le32_to_cpu(ch->len));
+}
+
+void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
+{
+ const struct ubifs_inode *ui = ubifs_inode(inode);
+ struct fscrypt_name nm = {0};
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *pdent = NULL;
+ int count = 2;
+
+ pr_err("Dump in-memory inode:");
+ pr_err("\tinode %lu\n", inode->i_ino);
+ pr_err("\tsize %llu\n",
+ (unsigned long long)i_size_read(inode));
+ pr_err("\tnlink %u\n", inode->i_nlink);
+ pr_err("\tuid %u\n", (unsigned int)i_uid_read(inode));
+ pr_err("\tgid %u\n", (unsigned int)i_gid_read(inode));
+ pr_err("\tatime %u.%u\n",
+ (unsigned int) inode_get_atime_sec(inode),
+ (unsigned int) inode_get_atime_nsec(inode));
+ pr_err("\tmtime %u.%u\n",
+ (unsigned int) inode_get_mtime_sec(inode),
+ (unsigned int) inode_get_mtime_nsec(inode));
+ pr_err("\tctime %u.%u\n",
+ (unsigned int) inode_get_ctime_sec(inode),
+ (unsigned int) inode_get_ctime_nsec(inode));
+ pr_err("\tcreat_sqnum %llu\n", ui->creat_sqnum);
+ pr_err("\txattr_size %u\n", ui->xattr_size);
+ pr_err("\txattr_cnt %u\n", ui->xattr_cnt);
+ pr_err("\txattr_names %u\n", ui->xattr_names);
+ pr_err("\tdirty %u\n", ui->dirty);
+ pr_err("\txattr %u\n", ui->xattr);
+ pr_err("\tbulk_read %u\n", ui->bulk_read);
+ pr_err("\tsynced_i_size %llu\n",
+ (unsigned long long)ui->synced_i_size);
+ pr_err("\tui_size %llu\n",
+ (unsigned long long)ui->ui_size);
+ pr_err("\tflags %d\n", ui->flags);
+ pr_err("\tcompr_type %d\n", ui->compr_type);
+ pr_err("\tlast_page_read %lu\n", ui->last_page_read);
+ pr_err("\tread_in_a_row %lu\n", ui->read_in_a_row);
+ pr_err("\tdata_len %d\n", ui->data_len);
+
+ if (!S_ISDIR(inode->i_mode))
+ return;
+
+ pr_err("List of directory entries:\n");
+ ubifs_assert(c, !mutex_is_locked(&c->tnc_mutex));
+
+ lowest_dent_key(c, &key, inode->i_ino);
+ while (1) {
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ if (PTR_ERR(dent) != -ENOENT)
+ pr_err("error %ld\n", PTR_ERR(dent));
+ break;
+ }
+
+ pr_err("\t%d: inode %llu, type %s, len %d\n",
+ count++, (unsigned long long) le64_to_cpu(dent->inum),
+ get_dent_type(dent->type),
+ le16_to_cpu(dent->nlen));
+
+ fname_name(&nm) = dent->name;
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
+ kfree(pdent);
+ pdent = dent;
+ key_read(c, &dent->key, &key);
+ }
+ kfree(pdent);
+}
+
+void ubifs_dump_node(const struct ubifs_info *c, const void *node, int node_len)
+{
+ int i, n, type, safe_len, max_node_len, min_node_len;
+ union ubifs_key key;
+ const struct ubifs_ch *ch = node;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ /* If the magic is incorrect, just hexdump the first bytes */
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) {
+ pr_err("Not a node, first %zu bytes:", UBIFS_CH_SZ);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 32, 1,
+ (void *)node, UBIFS_CH_SZ, 1);
+ return;
+ }
+
+ /* Skip dumping unknown type node */
+ type = ch->node_type;
+ if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
+ pr_err("node type %d was not recognized\n", type);
+ return;
+ }
+
+ spin_lock(&dbg_lock);
+ dump_ch(node);
+
+ if (c->ranges[type].max_len == 0) {
+ max_node_len = min_node_len = c->ranges[type].len;
+ } else {
+ max_node_len = c->ranges[type].max_len;
+ min_node_len = c->ranges[type].min_len;
+ }
+ safe_len = le32_to_cpu(ch->len);
+ safe_len = safe_len > 0 ? safe_len : 0;
+ safe_len = min3(safe_len, max_node_len, node_len);
+ if (safe_len < min_node_len) {
+ pr_err("node len(%d) is too short for %s, left %d bytes:\n",
+ safe_len, dbg_ntype(type),
+ safe_len > UBIFS_CH_SZ ?
+ safe_len - (int)UBIFS_CH_SZ : 0);
+ if (safe_len > UBIFS_CH_SZ)
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 32, 1,
+ (void *)node + UBIFS_CH_SZ,
+ safe_len - UBIFS_CH_SZ, 0);
+ goto out_unlock;
+ }
+ if (safe_len != le32_to_cpu(ch->len))
+ pr_err("\ttruncated node length %d\n", safe_len);
+
+ switch (type) {
+ case UBIFS_PAD_NODE:
+ {
+ const struct ubifs_pad_node *pad = node;
+
+ pr_err("\tpad_len %u\n", le32_to_cpu(pad->pad_len));
+ break;
+ }
+ case UBIFS_SB_NODE:
+ {
+ const struct ubifs_sb_node *sup = node;
+ unsigned int sup_flags = le32_to_cpu(sup->flags);
+
+ pr_err("\tkey_hash %d (%s)\n",
+ (int)sup->key_hash, get_key_hash(sup->key_hash));
+ pr_err("\tkey_fmt %d (%s)\n",
+ (int)sup->key_fmt, get_key_fmt(sup->key_fmt));
+ pr_err("\tflags %#x\n", sup_flags);
+ pr_err("\tbig_lpt %u\n",
+ !!(sup_flags & UBIFS_FLG_BIGLPT));
+ pr_err("\tspace_fixup %u\n",
+ !!(sup_flags & UBIFS_FLG_SPACE_FIXUP));
+ pr_err("\tmin_io_size %u\n", le32_to_cpu(sup->min_io_size));
+ pr_err("\tleb_size %u\n", le32_to_cpu(sup->leb_size));
+ pr_err("\tleb_cnt %u\n", le32_to_cpu(sup->leb_cnt));
+ pr_err("\tmax_leb_cnt %u\n", le32_to_cpu(sup->max_leb_cnt));
+ pr_err("\tmax_bud_bytes %llu\n",
+ (unsigned long long)le64_to_cpu(sup->max_bud_bytes));
+ pr_err("\tlog_lebs %u\n", le32_to_cpu(sup->log_lebs));
+ pr_err("\tlpt_lebs %u\n", le32_to_cpu(sup->lpt_lebs));
+ pr_err("\torph_lebs %u\n", le32_to_cpu(sup->orph_lebs));
+ pr_err("\tjhead_cnt %u\n", le32_to_cpu(sup->jhead_cnt));
+ pr_err("\tfanout %u\n", le32_to_cpu(sup->fanout));
+ pr_err("\tlsave_cnt %u\n", le32_to_cpu(sup->lsave_cnt));
+ pr_err("\tdefault_compr %u\n",
+ (int)le16_to_cpu(sup->default_compr));
+ pr_err("\trp_size %llu\n",
+ (unsigned long long)le64_to_cpu(sup->rp_size));
+ pr_err("\trp_uid %u\n", le32_to_cpu(sup->rp_uid));
+ pr_err("\trp_gid %u\n", le32_to_cpu(sup->rp_gid));
+ pr_err("\tfmt_version %u\n", le32_to_cpu(sup->fmt_version));
+ pr_err("\ttime_gran %u\n", le32_to_cpu(sup->time_gran));
+ pr_err("\tUUID %pUB\n", sup->uuid);
+ break;
+ }
+ case UBIFS_MST_NODE:
+ {
+ const struct ubifs_mst_node *mst = node;
+
+ pr_err("\thighest_inum %llu\n",
+ (unsigned long long)le64_to_cpu(mst->highest_inum));
+ pr_err("\tcommit number %llu\n",
+ (unsigned long long)le64_to_cpu(mst->cmt_no));
+ pr_err("\tflags %#x\n", le32_to_cpu(mst->flags));
+ pr_err("\tlog_lnum %u\n", le32_to_cpu(mst->log_lnum));
+ pr_err("\troot_lnum %u\n", le32_to_cpu(mst->root_lnum));
+ pr_err("\troot_offs %u\n", le32_to_cpu(mst->root_offs));
+ pr_err("\troot_len %u\n", le32_to_cpu(mst->root_len));
+ pr_err("\tgc_lnum %u\n", le32_to_cpu(mst->gc_lnum));
+ pr_err("\tihead_lnum %u\n", le32_to_cpu(mst->ihead_lnum));
+ pr_err("\tihead_offs %u\n", le32_to_cpu(mst->ihead_offs));
+ pr_err("\tindex_size %llu\n",
+ (unsigned long long)le64_to_cpu(mst->index_size));
+ pr_err("\tlpt_lnum %u\n", le32_to_cpu(mst->lpt_lnum));
+ pr_err("\tlpt_offs %u\n", le32_to_cpu(mst->lpt_offs));
+ pr_err("\tnhead_lnum %u\n", le32_to_cpu(mst->nhead_lnum));
+ pr_err("\tnhead_offs %u\n", le32_to_cpu(mst->nhead_offs));
+ pr_err("\tltab_lnum %u\n", le32_to_cpu(mst->ltab_lnum));
+ pr_err("\tltab_offs %u\n", le32_to_cpu(mst->ltab_offs));
+ pr_err("\tlsave_lnum %u\n", le32_to_cpu(mst->lsave_lnum));
+ pr_err("\tlsave_offs %u\n", le32_to_cpu(mst->lsave_offs));
+ pr_err("\tlscan_lnum %u\n", le32_to_cpu(mst->lscan_lnum));
+ pr_err("\tleb_cnt %u\n", le32_to_cpu(mst->leb_cnt));
+ pr_err("\tempty_lebs %u\n", le32_to_cpu(mst->empty_lebs));
+ pr_err("\tidx_lebs %u\n", le32_to_cpu(mst->idx_lebs));
+ pr_err("\ttotal_free %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_free));
+ pr_err("\ttotal_dirty %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dirty));
+ pr_err("\ttotal_used %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_used));
+ pr_err("\ttotal_dead %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dead));
+ pr_err("\ttotal_dark %llu\n",
+ (unsigned long long)le64_to_cpu(mst->total_dark));
+ break;
+ }
+ case UBIFS_REF_NODE:
+ {
+ const struct ubifs_ref_node *ref = node;
+
+ pr_err("\tlnum %u\n", le32_to_cpu(ref->lnum));
+ pr_err("\toffs %u\n", le32_to_cpu(ref->offs));
+ pr_err("\tjhead %u\n", le32_to_cpu(ref->jhead));
+ break;
+ }
+ case UBIFS_INO_NODE:
+ {
+ const struct ubifs_ino_node *ino = node;
+
+ key_read(c, &ino->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tcreat_sqnum %llu\n",
+ (unsigned long long)le64_to_cpu(ino->creat_sqnum));
+ pr_err("\tsize %llu\n",
+ (unsigned long long)le64_to_cpu(ino->size));
+ pr_err("\tnlink %u\n", le32_to_cpu(ino->nlink));
+ pr_err("\tatime %lld.%u\n",
+ (long long)le64_to_cpu(ino->atime_sec),
+ le32_to_cpu(ino->atime_nsec));
+ pr_err("\tmtime %lld.%u\n",
+ (long long)le64_to_cpu(ino->mtime_sec),
+ le32_to_cpu(ino->mtime_nsec));
+ pr_err("\tctime %lld.%u\n",
+ (long long)le64_to_cpu(ino->ctime_sec),
+ le32_to_cpu(ino->ctime_nsec));
+ pr_err("\tuid %u\n", le32_to_cpu(ino->uid));
+ pr_err("\tgid %u\n", le32_to_cpu(ino->gid));
+ pr_err("\tmode %u\n", le32_to_cpu(ino->mode));
+ pr_err("\tflags %#x\n", le32_to_cpu(ino->flags));
+ pr_err("\txattr_cnt %u\n", le32_to_cpu(ino->xattr_cnt));
+ pr_err("\txattr_size %u\n", le32_to_cpu(ino->xattr_size));
+ pr_err("\txattr_names %u\n", le32_to_cpu(ino->xattr_names));
+ pr_err("\tcompr_type %#x\n",
+ (int)le16_to_cpu(ino->compr_type));
+ pr_err("\tdata len %u\n", le32_to_cpu(ino->data_len));
+ break;
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ const struct ubifs_dent_node *dent = node;
+ int nlen = le16_to_cpu(dent->nlen);
+
+ key_read(c, &dent->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tinum %llu\n",
+ (unsigned long long)le64_to_cpu(dent->inum));
+ pr_err("\ttype %d\n", (int)dent->type);
+ pr_err("\tnlen %d\n", nlen);
+ pr_err("\tname ");
+
+ if (nlen > UBIFS_MAX_NLEN ||
+ nlen > safe_len - UBIFS_DENT_NODE_SZ)
+ pr_err("(bad name length, not printing, bad or corrupted node)");
+ else {
+ for (i = 0; i < nlen && dent->name[i]; i++)
+ pr_cont("%c", isprint(dent->name[i]) ?
+ dent->name[i] : '?');
+ }
+ pr_cont("\n");
+
+ break;
+ }
+ case UBIFS_DATA_NODE:
+ {
+ const struct ubifs_data_node *dn = node;
+
+ key_read(c, &dn->key, &key);
+ pr_err("\tkey %s\n",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+ pr_err("\tsize %u\n", le32_to_cpu(dn->size));
+ pr_err("\tcompr_typ %d\n",
+ (int)le16_to_cpu(dn->compr_type));
+ pr_err("\tdata size %u\n",
+ le32_to_cpu(ch->len) - (unsigned int)UBIFS_DATA_NODE_SZ);
+ pr_err("\tdata (length = %d):\n",
+ safe_len - (int)UBIFS_DATA_NODE_SZ);
+ print_hex_dump(KERN_ERR, "\t", DUMP_PREFIX_OFFSET, 32, 1,
+ (void *)&dn->data,
+ safe_len - (int)UBIFS_DATA_NODE_SZ, 0);
+ break;
+ }
+ case UBIFS_TRUN_NODE:
+ {
+ const struct ubifs_trun_node *trun = node;
+
+ pr_err("\tinum %u\n", le32_to_cpu(trun->inum));
+ pr_err("\told_size %llu\n",
+ (unsigned long long)le64_to_cpu(trun->old_size));
+ pr_err("\tnew_size %llu\n",
+ (unsigned long long)le64_to_cpu(trun->new_size));
+ break;
+ }
+ case UBIFS_IDX_NODE:
+ {
+ const struct ubifs_idx_node *idx = node;
+ int max_child_cnt = (safe_len - UBIFS_IDX_NODE_SZ) /
+ (ubifs_idx_node_sz(c, 1) -
+ UBIFS_IDX_NODE_SZ);
+
+ n = min_t(int, le16_to_cpu(idx->child_cnt), max_child_cnt);
+ pr_err("\tchild_cnt %d\n", (int)le16_to_cpu(idx->child_cnt));
+ pr_err("\tlevel %d\n", (int)le16_to_cpu(idx->level));
+ pr_err("\tBranches:\n");
+
+ for (i = 0; i < n && i < c->fanout; i++) {
+ const struct ubifs_branch *br;
+
+ br = ubifs_idx_branch(c, idx, i);
+ key_read(c, &br->key, &key);
+ pr_err("\t%d: LEB %d:%d len %d key %s\n",
+ i, le32_to_cpu(br->lnum), le32_to_cpu(br->offs),
+ le32_to_cpu(br->len),
+ dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ }
+ break;
+ }
+ case UBIFS_CS_NODE:
+ break;
+ case UBIFS_ORPH_NODE:
+ {
+ const struct ubifs_orph_node *orph = node;
+
+ pr_err("\tcommit number %llu\n",
+ (unsigned long long)
+ le64_to_cpu(orph->cmt_no) & LLONG_MAX);
+ pr_err("\tlast node flag %llu\n",
+ (unsigned long long)(le64_to_cpu(orph->cmt_no)) >> 63);
+ n = (safe_len - UBIFS_ORPH_NODE_SZ) >> 3;
+ pr_err("\t%d orphan inode numbers:\n", n);
+ for (i = 0; i < n; i++)
+ pr_err("\t ino %llu\n",
+ (unsigned long long)le64_to_cpu(orph->inos[i]));
+ break;
+ }
+ case UBIFS_AUTH_NODE:
+ {
+ break;
+ }
+ default:
+ pr_err("node type %d was not recognized\n", type);
+ }
+
+out_unlock:
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_budget_req(const struct ubifs_budget_req *req)
+{
+ spin_lock(&dbg_lock);
+ pr_err("Budgeting request: new_ino %d, dirtied_ino %d\n",
+ req->new_ino, req->dirtied_ino);
+ pr_err("\tnew_ino_d %d, dirtied_ino_d %d\n",
+ req->new_ino_d, req->dirtied_ino_d);
+ pr_err("\tnew_page %d, dirtied_page %d\n",
+ req->new_page, req->dirtied_page);
+ pr_err("\tnew_dent %d, mod_dent %d\n",
+ req->new_dent, req->mod_dent);
+ pr_err("\tidx_growth %d\n", req->idx_growth);
+ pr_err("\tdata_growth %d dd_growth %d\n",
+ req->data_growth, req->dd_growth);
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_lstats(const struct ubifs_lp_stats *lst)
+{
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) Lprops statistics: empty_lebs %d, idx_lebs %d\n",
+ current->pid, lst->empty_lebs, lst->idx_lebs);
+ pr_err("\ttaken_empty_lebs %d, total_free %lld, total_dirty %lld\n",
+ lst->taken_empty_lebs, lst->total_free, lst->total_dirty);
+ pr_err("\ttotal_used %lld, total_dark %lld, total_dead %lld\n",
+ lst->total_used, lst->total_dark, lst->total_dead);
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
+{
+ int i;
+ struct rb_node *rb;
+ struct ubifs_bud *bud;
+ struct ubifs_gced_idx_leb *idx_gc;
+ long long available, outstanding, free;
+
+ spin_lock(&c->space_lock);
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) Budgeting info: data budget sum %lld, total budget sum %lld\n",
+ current->pid, bi->data_growth + bi->dd_growth,
+ bi->data_growth + bi->dd_growth + bi->idx_growth);
+ pr_err("\tbudg_data_growth %lld, budg_dd_growth %lld, budg_idx_growth %lld\n",
+ bi->data_growth, bi->dd_growth, bi->idx_growth);
+ pr_err("\tmin_idx_lebs %d, old_idx_sz %llu, uncommitted_idx %lld\n",
+ bi->min_idx_lebs, bi->old_idx_sz, bi->uncommitted_idx);
+ pr_err("\tpage_budget %d, inode_budget %d, dent_budget %d\n",
+ bi->page_budget, bi->inode_budget, bi->dent_budget);
+ pr_err("\tnospace %u, nospace_rp %u\n", bi->nospace, bi->nospace_rp);
+ pr_err("\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
+ c->dark_wm, c->dead_wm, c->max_idx_node_sz);
+
+ if (bi != &c->bi)
+ /*
+ * If we are dumping saved budgeting data, do not print
+ * additional information which is about the current state, not
+ * the old one which corresponded to the saved budgeting data.
+ */
+ goto out_unlock;
+
+ pr_err("\tfreeable_cnt %d, calc_idx_sz %lld, idx_gc_cnt %d\n",
+ c->freeable_cnt, c->calc_idx_sz, c->idx_gc_cnt);
+ pr_err("\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, clean_zn_cnt %ld\n",
+ atomic_long_read(&c->dirty_pg_cnt),
+ atomic_long_read(&c->dirty_zn_cnt),
+ atomic_long_read(&c->clean_zn_cnt));
+ pr_err("\tgc_lnum %d, ihead_lnum %d\n", c->gc_lnum, c->ihead_lnum);
+
+ /* If we are in R/O mode, journal heads do not exist */
+ if (c->jheads)
+ for (i = 0; i < c->jhead_cnt; i++)
+ pr_err("\tjhead %s\t LEB %d\n",
+ dbg_jhead(c->jheads[i].wbuf.jhead),
+ c->jheads[i].wbuf.lnum);
+ for (rb = rb_first(&c->buds); rb; rb = rb_next(rb)) {
+ bud = rb_entry(rb, struct ubifs_bud, rb);
+ pr_err("\tbud LEB %d\n", bud->lnum);
+ }
+ list_for_each_entry(bud, &c->old_buds, list)
+ pr_err("\told bud LEB %d\n", bud->lnum);
+ list_for_each_entry(idx_gc, &c->idx_gc, list)
+ pr_err("\tGC'ed idx LEB %d unmap %d\n",
+ idx_gc->lnum, idx_gc->unmap);
+ pr_err("\tcommit state %d\n", c->cmt_state);
+
+ /* Print budgeting predictions */
+ available = ubifs_calc_available(c, c->bi.min_idx_lebs);
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
+ free = ubifs_get_free_space_nolock(c);
+ pr_err("Budgeting predictions:\n");
+ pr_err("\tavailable: %lld, outstanding %lld, free %lld\n",
+ available, outstanding, free);
+out_unlock:
+ spin_unlock(&dbg_lock);
+ spin_unlock(&c->space_lock);
+}
+
+void ubifs_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
+{
+ int i, spc, dark = 0, dead = 0;
+ struct rb_node *rb;
+ struct ubifs_bud *bud;
+
+ spc = lp->free + lp->dirty;
+ if (spc < c->dead_wm)
+ dead = spc;
+ else
+ dark = ubifs_calc_dark(c, spc);
+
+ if (lp->flags & LPROPS_INDEX)
+ pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d flags %#x (",
+ lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc,
+ lp->flags);
+ else
+ pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d dark %-4d dead %-4d nodes fit %-3d flags %#-4x (",
+ lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc,
+ dark, dead, (int)(spc / UBIFS_MAX_NODE_SZ), lp->flags);
+
+ if (lp->flags & LPROPS_TAKEN) {
+ if (lp->flags & LPROPS_INDEX)
+ pr_cont("index, taken");
+ else
+ pr_cont("taken");
+ } else {
+ const char *s;
+
+ if (lp->flags & LPROPS_INDEX) {
+ switch (lp->flags & LPROPS_CAT_MASK) {
+ case LPROPS_DIRTY_IDX:
+ s = "dirty index";
+ break;
+ case LPROPS_FRDI_IDX:
+ s = "freeable index";
+ break;
+ default:
+ s = "index";
+ }
+ } else {
+ switch (lp->flags & LPROPS_CAT_MASK) {
+ case LPROPS_UNCAT:
+ s = "not categorized";
+ break;
+ case LPROPS_DIRTY:
+ s = "dirty";
+ break;
+ case LPROPS_FREE:
+ s = "free";
+ break;
+ case LPROPS_EMPTY:
+ s = "empty";
+ break;
+ case LPROPS_FREEABLE:
+ s = "freeable";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ }
+ pr_cont("%s", s);
+ }
+
+ for (rb = rb_first((struct rb_root *)&c->buds); rb; rb = rb_next(rb)) {
+ bud = rb_entry(rb, struct ubifs_bud, rb);
+ if (bud->lnum == lp->lnum) {
+ int head = 0;
+ for (i = 0; i < c->jhead_cnt; i++) {
+ /*
+ * Note, if we are in R/O mode or in the middle
+ * of mounting/re-mounting, the write-buffers do
+ * not exist.
+ */
+ if (c->jheads &&
+ lp->lnum == c->jheads[i].wbuf.lnum) {
+ pr_cont(", jhead %s", dbg_jhead(i));
+ head = 1;
+ }
+ }
+ if (!head)
+ pr_cont(", bud of jhead %s",
+ dbg_jhead(bud->jhead));
+ }
+ }
+ if (lp->lnum == c->gc_lnum)
+ pr_cont(", GC LEB");
+ pr_cont(")\n");
+}
+
+void ubifs_dump_lprops(struct ubifs_info *c)
+{
+ int lnum, err;
+ struct ubifs_lprops lp;
+ struct ubifs_lp_stats lst;
+
+ pr_err("(pid %d) start dumping LEB properties\n", current->pid);
+ ubifs_get_lp_stats(c, &lst);
+ ubifs_dump_lstats(&lst);
+
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ err = ubifs_read_one_lp(c, lnum, &lp);
+ if (err) {
+ ubifs_err(c, "cannot read lprops for LEB %d", lnum);
+ continue;
+ }
+
+ ubifs_dump_lprop(c, &lp);
+ }
+ pr_err("(pid %d) finish dumping LEB properties\n", current->pid);
+}
+
+void ubifs_dump_lpt_info(struct ubifs_info *c)
+{
+ int i;
+
+ spin_lock(&dbg_lock);
+ pr_err("(pid %d) dumping LPT information\n", current->pid);
+ pr_err("\tlpt_sz: %lld\n", c->lpt_sz);
+ pr_err("\tpnode_sz: %d\n", c->pnode_sz);
+ pr_err("\tnnode_sz: %d\n", c->nnode_sz);
+ pr_err("\tltab_sz: %d\n", c->ltab_sz);
+ pr_err("\tlsave_sz: %d\n", c->lsave_sz);
+ pr_err("\tbig_lpt: %u\n", c->big_lpt);
+ pr_err("\tlpt_hght: %d\n", c->lpt_hght);
+ pr_err("\tpnode_cnt: %d\n", c->pnode_cnt);
+ pr_err("\tnnode_cnt: %d\n", c->nnode_cnt);
+ pr_err("\tdirty_pn_cnt: %d\n", c->dirty_pn_cnt);
+ pr_err("\tdirty_nn_cnt: %d\n", c->dirty_nn_cnt);
+ pr_err("\tlsave_cnt: %d\n", c->lsave_cnt);
+ pr_err("\tspace_bits: %d\n", c->space_bits);
+ pr_err("\tlpt_lnum_bits: %d\n", c->lpt_lnum_bits);
+ pr_err("\tlpt_offs_bits: %d\n", c->lpt_offs_bits);
+ pr_err("\tlpt_spc_bits: %d\n", c->lpt_spc_bits);
+ pr_err("\tpcnt_bits: %d\n", c->pcnt_bits);
+ pr_err("\tlnum_bits: %d\n", c->lnum_bits);
+ pr_err("\tLPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs);
+ pr_err("\tLPT head is at %d:%d\n",
+ c->nhead_lnum, c->nhead_offs);
+ pr_err("\tLPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ pr_err("\tLPT lsave is at %d:%d\n",
+ c->lsave_lnum, c->lsave_offs);
+ for (i = 0; i < c->lpt_lebs; i++)
+ pr_err("\tLPT LEB %d free %d dirty %d tgc %d cmt %d\n",
+ i + c->lpt_first, c->ltab[i].free, c->ltab[i].dirty,
+ c->ltab[i].tgc, c->ltab[i].cmt);
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ void *buf;
+
+ pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory for dumping LEB %d", lnum);
+ return;
+ }
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ ubifs_err(c, "scan error %d", (int)PTR_ERR(sleb));
+ goto out;
+ }
+
+ pr_err("LEB %d has %d nodes ending at %d\n", lnum,
+ sleb->nodes_cnt, sleb->endpt);
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+ pr_err("Dumping node at LEB %d:%d len %d\n", lnum,
+ snod->offs, snod->len);
+ ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
+ }
+
+ pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+ ubifs_scan_destroy(sleb);
+
+out:
+ vfree(buf);
+ return;
+}
+
+void ubifs_dump_znode(const struct ubifs_info *c,
+ const struct ubifs_znode *znode)
+{
+ int n;
+ const struct ubifs_zbranch *zbr;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ spin_lock(&dbg_lock);
+ if (znode->parent)
+ zbr = &znode->parent->zbranch[znode->iip];
+ else
+ zbr = &c->zroot;
+
+ pr_err("znode %p, LEB %d:%d len %d parent %p iip %d level %d child_cnt %d flags %lx\n",
+ znode, zbr->lnum, zbr->offs, zbr->len, znode->parent, znode->iip,
+ znode->level, znode->child_cnt, znode->flags);
+
+ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+ spin_unlock(&dbg_lock);
+ return;
+ }
+
+ pr_err("zbranches:\n");
+ for (n = 0; n < znode->child_cnt; n++) {
+ zbr = &znode->zbranch[n];
+ if (znode->level > 0)
+ pr_err("\t%d: znode %p LEB %d:%d len %d key %s\n",
+ n, zbr->znode, zbr->lnum, zbr->offs, zbr->len,
+ dbg_snprintf_key(c, &zbr->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ else
+ pr_err("\t%d: LNC %p LEB %d:%d len %d key %s\n",
+ n, zbr->znode, zbr->lnum, zbr->offs, zbr->len,
+ dbg_snprintf_key(c, &zbr->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ }
+ spin_unlock(&dbg_lock);
+}
+
+void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
+{
+ int i;
+
+ pr_err("(pid %d) start dumping heap cat %d (%d elements)\n",
+ current->pid, cat, heap->cnt);
+ for (i = 0; i < heap->cnt; i++) {
+ struct ubifs_lprops *lprops = heap->arr[i];
+
+ pr_err("\t%d. LEB %d hpos %d free %d dirty %d flags %d\n",
+ i, lprops->lnum, lprops->hpos, lprops->free,
+ lprops->dirty, lprops->flags);
+ }
+ pr_err("(pid %d) finish dumping heap\n", current->pid);
+}
+
+void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i;
+
+ pr_err("(pid %d) dumping pnode:\n", current->pid);
+ pr_err("\taddress %zx parent %zx cnext %zx\n",
+ (size_t)pnode, (size_t)parent, (size_t)pnode->cnext);
+ pr_err("\tflags %lu iip %d level %d num %d\n",
+ pnode->flags, iip, pnode->level, pnode->num);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops *lp = &pnode->lprops[i];
+
+ pr_err("\t%d: free %d dirty %d flags %d lnum %d\n",
+ i, lp->free, lp->dirty, lp->flags, lp->lnum);
+ }
+}
+
+void ubifs_dump_tnc(struct ubifs_info *c)
+{
+ struct ubifs_znode *znode;
+ int level;
+
+ pr_err("\n");
+ pr_err("(pid %d) start dumping TNC tree\n", current->pid);
+ znode = ubifs_tnc_levelorder_next(c, c->zroot.znode, NULL);
+ level = znode->level;
+ pr_err("== Level %d ==\n", level);
+ while (znode) {
+ if (level != znode->level) {
+ level = znode->level;
+ pr_err("== Level %d ==\n", level);
+ }
+ ubifs_dump_znode(c, znode);
+ znode = ubifs_tnc_levelorder_next(c, c->zroot.znode, znode);
+ }
+ pr_err("(pid %d) finish dumping TNC tree\n", current->pid);
+}
+
+static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode,
+ void *priv)
+{
+ ubifs_dump_znode(c, znode);
+ return 0;
+}
+
+/**
+ * ubifs_dump_index - dump the on-flash index.
+ * @c: UBIFS file-system description object
+ *
+ * This function dumps whole UBIFS indexing B-tree, unlike 'ubifs_dump_tnc()'
+ * which dumps only in-memory znodes and does not read znodes which from flash.
+ */
+void ubifs_dump_index(struct ubifs_info *c)
+{
+ dbg_walk_index(c, NULL, dump_znode, NULL);
+}
+
+/**
+ * dbg_save_space_info - save information about flash space.
+ * @c: UBIFS file-system description object
+ *
+ * This function saves information about UBIFS free space, dirty space, etc, in
+ * order to check it later.
+ */
+void dbg_save_space_info(struct ubifs_info *c)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ int freeable_cnt;
+
+ spin_lock(&c->space_lock);
+ memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
+ memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info));
+ d->saved_idx_gc_cnt = c->idx_gc_cnt;
+
+ /*
+ * We use a dirty hack here and zero out @c->freeable_cnt, because it
+ * affects the free space calculations, and UBIFS might not know about
+ * all freeable eraseblocks. Indeed, we know about freeable eraseblocks
+ * only when we read their lprops, and we do this only lazily, upon the
+ * need. So at any given point of time @c->freeable_cnt might be not
+ * exactly accurate.
+ *
+ * Just one example about the issue we hit when we did not zero
+ * @c->freeable_cnt.
+ * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the
+ * amount of free space in @d->saved_free
+ * 2. We re-mount R/W, which makes UBIFS to read the "lsave"
+ * information from flash, where we cache LEBs from various
+ * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()'
+ * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()'
+ * -> 'ubifs_get_pnode()' -> 'update_cats()'
+ * -> 'ubifs_add_to_cat()').
+ * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt
+ * becomes %1.
+ * 4. We calculate the amount of free space when the re-mount is
+ * finished in 'dbg_check_space_info()' and it does not match
+ * @d->saved_free.
+ */
+ freeable_cnt = c->freeable_cnt;
+ c->freeable_cnt = 0;
+ d->saved_free = ubifs_get_free_space_nolock(c);
+ c->freeable_cnt = freeable_cnt;
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * dbg_check_space_info - check flash space information.
+ * @c: UBIFS file-system description object
+ *
+ * This function compares current flash space information with the information
+ * which was saved when the 'dbg_save_space_info()' function was called.
+ * Returns zero if the information has not changed, and %-EINVAL if it has
+ * changed.
+ */
+int dbg_check_space_info(struct ubifs_info *c)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ struct ubifs_lp_stats lst;
+ long long free;
+ int freeable_cnt;
+
+ spin_lock(&c->space_lock);
+ freeable_cnt = c->freeable_cnt;
+ c->freeable_cnt = 0;
+ free = ubifs_get_free_space_nolock(c);
+ c->freeable_cnt = freeable_cnt;
+ spin_unlock(&c->space_lock);
+
+ if (free != d->saved_free) {
+ ubifs_err(c, "free space changed from %lld to %lld",
+ d->saved_free, free);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_msg(c, "saved lprops statistics dump");
+ ubifs_dump_lstats(&d->saved_lst);
+ ubifs_msg(c, "saved budgeting info dump");
+ ubifs_dump_budg(c, &d->saved_bi);
+ ubifs_msg(c, "saved idx_gc_cnt %d", d->saved_idx_gc_cnt);
+ ubifs_msg(c, "current lprops statistics dump");
+ ubifs_get_lp_stats(c, &lst);
+ ubifs_dump_lstats(&lst);
+ ubifs_msg(c, "current budgeting info dump");
+ ubifs_dump_budg(c, &c->bi);
+ dump_stack();
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_synced_i_size - check synchronized inode size.
+ * @c: UBIFS file-system description object
+ * @inode: inode to check
+ *
+ * If inode is clean, synchronized inode size has to be equivalent to current
+ * inode size. This function has to be called only for locked inodes (@i_mutex
+ * has to be locked). Returns %0 if synchronized inode size if correct, and
+ * %-EINVAL if not.
+ */
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
+{
+ int err = 0;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+
+ mutex_lock(&ui->ui_mutex);
+ spin_lock(&ui->ui_lock);
+ if (ui->ui_size != ui->synced_i_size && !ui->dirty) {
+ ubifs_err(c, "ui_size is %lld, synced_i_size is %lld, but inode is clean",
+ ui->ui_size, ui->synced_i_size);
+ ubifs_err(c, "i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino,
+ inode->i_mode, i_size_read(inode));
+ dump_stack();
+ err = -EINVAL;
+ }
+ spin_unlock(&ui->ui_lock);
+ mutex_unlock(&ui->ui_mutex);
+ return err;
+}
+
+/*
+ * dbg_check_dir - check directory inode size and link count.
+ * @c: UBIFS file-system description object
+ * @dir: the directory to calculate size for
+ * @size: the result is returned here
+ *
+ * This function makes sure that directory size and link count are correct.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ *
+ * Note, it is good idea to make sure the @dir->i_mutex is locked before
+ * calling this function.
+ */
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
+{
+ unsigned int nlink = 2;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *pdent = NULL;
+ struct fscrypt_name nm = {0};
+ loff_t size = UBIFS_INO_NODE_SZ;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ if (!S_ISDIR(dir->i_mode))
+ return 0;
+
+ lowest_dent_key(c, &key, dir->i_ino);
+ while (1) {
+ int err;
+
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ if (err == -ENOENT)
+ break;
+ kfree(pdent);
+ return err;
+ }
+
+ fname_name(&nm) = dent->name;
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
+ size += CALC_DENT_SIZE(fname_len(&nm));
+ if (dent->type == UBIFS_ITYPE_DIR)
+ nlink += 1;
+ kfree(pdent);
+ pdent = dent;
+ key_read(c, &dent->key, &key);
+ }
+ kfree(pdent);
+
+ if (i_size_read(dir) != size) {
+ ubifs_err(c, "directory inode %lu has size %llu, but calculated size is %llu",
+ dir->i_ino, (unsigned long long)i_size_read(dir),
+ (unsigned long long)size);
+ ubifs_dump_inode(c, dir);
+ dump_stack();
+ return -EINVAL;
+ }
+ if (dir->i_nlink != nlink) {
+ ubifs_err(c, "directory inode %lu has nlink %u, but calculated nlink is %u",
+ dir->i_ino, dir->i_nlink, nlink);
+ ubifs_dump_inode(c, dir);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * dbg_check_key_order - make sure that colliding keys are properly ordered.
+ * @c: UBIFS file-system description object
+ * @zbr1: first zbranch
+ * @zbr2: following zbranch
+ *
+ * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of
+ * names of the direntries/xentries which are referred by the keys. This
+ * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes
+ * sure the name of direntry/xentry referred by @zbr1 is less than
+ * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not,
+ * and a negative error code in case of failure.
+ */
+static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
+ struct ubifs_zbranch *zbr2)
+{
+ int err, nlen1, nlen2, cmp;
+ struct ubifs_dent_node *dent1, *dent2;
+ union ubifs_key key;
+ char key_buf[DBG_KEY_BUF_LEN];
+
+ ubifs_assert(c, !keys_cmp(c, &zbr1->key, &zbr2->key));
+ dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent1)
+ return -ENOMEM;
+ dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent2) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ err = ubifs_tnc_read_node(c, zbr1, dent1);
+ if (err)
+ goto out_free;
+ err = ubifs_validate_entry(c, dent1);
+ if (err)
+ goto out_free;
+
+ err = ubifs_tnc_read_node(c, zbr2, dent2);
+ if (err)
+ goto out_free;
+ err = ubifs_validate_entry(c, dent2);
+ if (err)
+ goto out_free;
+
+ /* Make sure node keys are the same as in zbranch */
+ err = 1;
+ key_read(c, &dent1->key, &key);
+ if (keys_cmp(c, &zbr1->key, &key)) {
+ ubifs_err(c, "1st entry at %d:%d has key %s", zbr1->lnum,
+ zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_err(c, "but it should have key %s according to tnc",
+ dbg_snprintf_key(c, &zbr1->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_dump_node(c, dent1, UBIFS_MAX_DENT_NODE_SZ);
+ goto out_free;
+ }
+
+ key_read(c, &dent2->key, &key);
+ if (keys_cmp(c, &zbr2->key, &key)) {
+ ubifs_err(c, "2nd entry at %d:%d has key %s", zbr1->lnum,
+ zbr1->offs, dbg_snprintf_key(c, &key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_err(c, "but it should have key %s according to tnc",
+ dbg_snprintf_key(c, &zbr2->key, key_buf,
+ DBG_KEY_BUF_LEN));
+ ubifs_dump_node(c, dent2, UBIFS_MAX_DENT_NODE_SZ);
+ goto out_free;
+ }
+
+ nlen1 = le16_to_cpu(dent1->nlen);
+ nlen2 = le16_to_cpu(dent2->nlen);
+
+ cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2));
+ if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) {
+ err = 0;
+ goto out_free;
+ }
+ if (cmp == 0 && nlen1 == nlen2)
+ ubifs_err(c, "2 xent/dent nodes with the same name");
+ else
+ ubifs_err(c, "bad order of colliding key %s",
+ dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN));
+
+ ubifs_msg(c, "first node at %d:%d\n", zbr1->lnum, zbr1->offs);
+ ubifs_dump_node(c, dent1, UBIFS_MAX_DENT_NODE_SZ);
+ ubifs_msg(c, "second node at %d:%d\n", zbr2->lnum, zbr2->offs);
+ ubifs_dump_node(c, dent2, UBIFS_MAX_DENT_NODE_SZ);
+
+out_free:
+ kfree(dent2);
+ kfree(dent1);
+ return err;
+}
+
+/**
+ * dbg_check_znode - check if znode is all right.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch which points to this znode
+ *
+ * This function makes sure that znode referred to by @zbr is all right.
+ * Returns zero if it is, and %-EINVAL if it is not.
+ */
+static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr)
+{
+ struct ubifs_znode *znode = zbr->znode;
+ struct ubifs_znode *zp = znode->parent;
+ int n, err, cmp;
+
+ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+ err = 1;
+ goto out;
+ }
+ if (znode->level < 0) {
+ err = 2;
+ goto out;
+ }
+ if (znode->iip < 0 || znode->iip >= c->fanout) {
+ err = 3;
+ goto out;
+ }
+
+ if (zbr->len == 0)
+ /* Only dirty zbranch may have no on-flash nodes */
+ if (!ubifs_zn_dirty(znode)) {
+ err = 4;
+ goto out;
+ }
+
+ if (ubifs_zn_dirty(znode)) {
+ /*
+ * If znode is dirty, its parent has to be dirty as well. The
+ * order of the operation is important, so we have to have
+ * memory barriers.
+ */
+ smp_mb();
+ if (zp && !ubifs_zn_dirty(zp)) {
+ /*
+ * The dirty flag is atomic and is cleared outside the
+ * TNC mutex, so znode's dirty flag may now have
+ * been cleared. The child is always cleared before the
+ * parent, so we just need to check again.
+ */
+ smp_mb();
+ if (ubifs_zn_dirty(znode)) {
+ err = 5;
+ goto out;
+ }
+ }
+ }
+
+ if (zp) {
+ const union ubifs_key *min, *max;
+
+ if (znode->level != zp->level - 1) {
+ err = 6;
+ goto out;
+ }
+
+ /* Make sure the 'parent' pointer in our znode is correct */
+ err = ubifs_search_zbranch(c, zp, &zbr->key, &n);
+ if (!err) {
+ /* This zbranch does not exist in the parent */
+ err = 7;
+ goto out;
+ }
+
+ if (znode->iip >= zp->child_cnt) {
+ err = 8;
+ goto out;
+ }
+
+ if (znode->iip != n) {
+ /* This may happen only in case of collisions */
+ if (keys_cmp(c, &zp->zbranch[n].key,
+ &zp->zbranch[znode->iip].key)) {
+ err = 9;
+ goto out;
+ }
+ n = znode->iip;
+ }
+
+ /*
+ * Make sure that the first key in our znode is greater than or
+ * equal to the key in the pointing zbranch.
+ */
+ min = &zbr->key;
+ cmp = keys_cmp(c, min, &znode->zbranch[0].key);
+ if (cmp == 1) {
+ err = 10;
+ goto out;
+ }
+
+ if (n + 1 < zp->child_cnt) {
+ max = &zp->zbranch[n + 1].key;
+
+ /*
+ * Make sure the last key in our znode is less or
+ * equivalent than the key in the zbranch which goes
+ * after our pointing zbranch.
+ */
+ cmp = keys_cmp(c, max,
+ &znode->zbranch[znode->child_cnt - 1].key);
+ if (cmp == -1) {
+ err = 11;
+ goto out;
+ }
+ }
+ } else {
+ /* This may only be root znode */
+ if (zbr != &c->zroot) {
+ err = 12;
+ goto out;
+ }
+ }
+
+ /*
+ * Make sure that next key is greater or equivalent then the previous
+ * one.
+ */
+ for (n = 1; n < znode->child_cnt; n++) {
+ cmp = keys_cmp(c, &znode->zbranch[n - 1].key,
+ &znode->zbranch[n].key);
+ if (cmp > 0) {
+ err = 13;
+ goto out;
+ }
+ if (cmp == 0) {
+ /* This can only be keys with colliding hash */
+ if (!is_hash_key(c, &znode->zbranch[n].key)) {
+ err = 14;
+ goto out;
+ }
+
+ if (znode->level != 0 || c->replaying)
+ continue;
+
+ /*
+ * Colliding keys should follow binary order of
+ * corresponding xentry/dentry names.
+ */
+ err = dbg_check_key_order(c, &znode->zbranch[n - 1],
+ &znode->zbranch[n]);
+ if (err < 0)
+ return err;
+ if (err) {
+ err = 15;
+ goto out;
+ }
+ }
+ }
+
+ for (n = 0; n < znode->child_cnt; n++) {
+ if (!znode->zbranch[n].znode &&
+ (znode->zbranch[n].lnum == 0 ||
+ znode->zbranch[n].len == 0)) {
+ err = 16;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum != 0 &&
+ znode->zbranch[n].len == 0) {
+ err = 17;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum == 0 &&
+ znode->zbranch[n].len != 0) {
+ err = 18;
+ goto out;
+ }
+
+ if (znode->zbranch[n].lnum == 0 &&
+ znode->zbranch[n].offs != 0) {
+ err = 19;
+ goto out;
+ }
+
+ if (znode->level != 0 && znode->zbranch[n].znode)
+ if (znode->zbranch[n].znode->parent != znode) {
+ err = 20;
+ goto out;
+ }
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "failed, error %d", err);
+ ubifs_msg(c, "dump of the znode");
+ ubifs_dump_znode(c, znode);
+ if (zp) {
+ ubifs_msg(c, "dump of the parent znode");
+ ubifs_dump_znode(c, zp);
+ }
+ dump_stack();
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_tnc - check TNC tree.
+ * @c: UBIFS file-system description object
+ * @extra: do extra checks that are possible at start commit
+ *
+ * This function traverses whole TNC tree and checks every znode. Returns zero
+ * if everything is all right and %-EINVAL if something is wrong with TNC.
+ */
+int dbg_check_tnc(struct ubifs_info *c, int extra)
+{
+ struct ubifs_znode *znode;
+ long clean_cnt = 0, dirty_cnt = 0;
+ int err, last;
+
+ if (!dbg_is_chk_index(c))
+ return 0;
+
+ ubifs_assert(c, mutex_is_locked(&c->tnc_mutex));
+ if (!c->zroot.znode)
+ return 0;
+
+ znode = ubifs_tnc_postorder_first(c->zroot.znode);
+ while (1) {
+ struct ubifs_znode *prev;
+ struct ubifs_zbranch *zbr;
+
+ if (!znode->parent)
+ zbr = &c->zroot;
+ else
+ zbr = &znode->parent->zbranch[znode->iip];
+
+ err = dbg_check_znode(c, zbr);
+ if (err)
+ return err;
+
+ if (extra) {
+ if (ubifs_zn_dirty(znode))
+ dirty_cnt += 1;
+ else
+ clean_cnt += 1;
+ }
+
+ prev = znode;
+ znode = ubifs_tnc_postorder_next(c, znode);
+ if (!znode)
+ break;
+
+ /*
+ * If the last key of this znode is equivalent to the first key
+ * of the next znode (collision), then check order of the keys.
+ */
+ last = prev->child_cnt - 1;
+ if (prev->level == 0 && znode->level == 0 && !c->replaying &&
+ !keys_cmp(c, &prev->zbranch[last].key,
+ &znode->zbranch[0].key)) {
+ err = dbg_check_key_order(c, &prev->zbranch[last],
+ &znode->zbranch[0]);
+ if (err < 0)
+ return err;
+ if (err) {
+ ubifs_msg(c, "first znode");
+ ubifs_dump_znode(c, prev);
+ ubifs_msg(c, "second znode");
+ ubifs_dump_znode(c, znode);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (extra) {
+ if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) {
+ ubifs_err(c, "incorrect clean_zn_cnt %ld, calculated %ld",
+ atomic_long_read(&c->clean_zn_cnt),
+ clean_cnt);
+ return -EINVAL;
+ }
+ if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) {
+ ubifs_err(c, "incorrect dirty_zn_cnt %ld, calculated %ld",
+ atomic_long_read(&c->dirty_zn_cnt),
+ dirty_cnt);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dbg_walk_index - walk the on-flash index.
+ * @c: UBIFS file-system description object
+ * @leaf_cb: called for each leaf node
+ * @znode_cb: called for each indexing node
+ * @priv: private data which is passed to callbacks
+ *
+ * This function walks the UBIFS index and calls the @leaf_cb for each leaf
+ * node and @znode_cb for each indexing node. Returns zero in case of success
+ * and a negative error code in case of failure.
+ *
+ * It would be better if this function removed every znode it pulled to into
+ * the TNC, so that the behavior more closely matched the non-debugging
+ * behavior.
+ */
+int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
+ dbg_znode_callback znode_cb, void *priv)
+{
+ int err;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *znode, *child;
+
+ mutex_lock(&c->tnc_mutex);
+ /* If the root indexing node is not in TNC - pull it */
+ if (!c->zroot.znode) {
+ c->zroot.znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(c->zroot.znode)) {
+ err = PTR_ERR(c->zroot.znode);
+ c->zroot.znode = NULL;
+ goto out_unlock;
+ }
+ }
+
+ /*
+ * We are going to traverse the indexing tree in the postorder manner.
+ * Go down and find the leftmost indexing node where we are going to
+ * start from.
+ */
+ znode = c->zroot.znode;
+ while (znode->level > 0) {
+ zbr = &znode->zbranch[0];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, 0);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ }
+
+ znode = child;
+ }
+
+ /* Iterate over all indexing nodes */
+ while (1) {
+ int idx;
+
+ cond_resched();
+
+ if (znode_cb) {
+ err = znode_cb(c, znode, priv);
+ if (err) {
+ ubifs_err(c, "znode checking function returned error %d",
+ err);
+ ubifs_dump_znode(c, znode);
+ goto out_dump;
+ }
+ }
+ if (leaf_cb && znode->level == 0) {
+ for (idx = 0; idx < znode->child_cnt; idx++) {
+ zbr = &znode->zbranch[idx];
+ err = leaf_cb(c, zbr, priv);
+ if (err) {
+ ubifs_err(c, "leaf checking function returned error %d, for leaf at LEB %d:%d",
+ err, zbr->lnum, zbr->offs);
+ goto out_dump;
+ }
+ }
+ }
+
+ if (!znode->parent)
+ break;
+
+ idx = znode->iip + 1;
+ znode = znode->parent;
+ if (idx < znode->child_cnt) {
+ /* Switch to the next index in the parent */
+ zbr = &znode->zbranch[idx];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, idx);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ zbr->znode = child;
+ }
+ znode = child;
+ } else
+ /*
+ * This is the last child, switch to the parent and
+ * continue.
+ */
+ continue;
+
+ /* Go to the lowest leftmost znode in the new sub-tree */
+ while (znode->level > 0) {
+ zbr = &znode->zbranch[0];
+ child = zbr->znode;
+ if (!child) {
+ child = ubifs_load_znode(c, zbr, znode, 0);
+ if (IS_ERR(child)) {
+ err = PTR_ERR(child);
+ goto out_unlock;
+ }
+ zbr->znode = child;
+ }
+ znode = child;
+ }
+ }
+
+ mutex_unlock(&c->tnc_mutex);
+ return 0;
+
+out_dump:
+ if (znode->parent)
+ zbr = &znode->parent->zbranch[znode->iip];
+ else
+ zbr = &c->zroot;
+ ubifs_msg(c, "dump of znode at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_znode(c, znode);
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * add_size - add znode size to partially calculated index size.
+ * @c: UBIFS file-system description object
+ * @znode: znode to add size for
+ * @priv: partially calculated index size
+ *
+ * This is a helper function for 'dbg_check_idx_size()' which is called for
+ * every indexing node and adds its size to the 'long long' variable pointed to
+ * by @priv.
+ */
+static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
+{
+ long long *idx_size = priv;
+ int add;
+
+ add = ubifs_idx_node_sz(c, znode->child_cnt);
+ add = ALIGN(add, 8);
+ *idx_size += add;
+ return 0;
+}
+
+/**
+ * dbg_check_idx_size - check index size.
+ * @c: UBIFS file-system description object
+ * @idx_size: size to check
+ *
+ * This function walks the UBIFS index, calculates its size and checks that the
+ * size is equivalent to @idx_size. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
+{
+ int err;
+ long long calc = 0;
+
+ if (!dbg_is_chk_index(c))
+ return 0;
+
+ err = dbg_walk_index(c, NULL, add_size, &calc);
+ if (err) {
+ ubifs_err(c, "error %d while walking the index", err);
+ goto out_err;
+ }
+
+ if (calc != idx_size) {
+ ubifs_err(c, "index size check failed: calculated size is %lld, should be %lld",
+ calc, idx_size);
+ dump_stack();
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ ubifs_destroy_tnc_tree(c);
+ return err;
+}
+
+/**
+ * struct fsck_inode - information about an inode used when checking the file-system.
+ * @rb: link in the RB-tree of inodes
+ * @inum: inode number
+ * @mode: inode type, permissions, etc
+ * @nlink: inode link count
+ * @xattr_cnt: count of extended attributes
+ * @references: how many directory/xattr entries refer this inode (calculated
+ * while walking the index)
+ * @calc_cnt: for directory inode count of child directories
+ * @size: inode size (read from on-flash inode)
+ * @xattr_sz: summary size of all extended attributes (read from on-flash
+ * inode)
+ * @calc_sz: for directories calculated directory size
+ * @calc_xcnt: count of extended attributes
+ * @calc_xsz: calculated summary size of all extended attributes
+ * @xattr_nms: sum of lengths of all extended attribute names belonging to this
+ * inode (read from on-flash inode)
+ * @calc_xnms: calculated sum of lengths of all extended attribute names
+ */
+struct fsck_inode {
+ struct rb_node rb;
+ ino_t inum;
+ umode_t mode;
+ unsigned int nlink;
+ unsigned int xattr_cnt;
+ int references;
+ int calc_cnt;
+ long long size;
+ unsigned int xattr_sz;
+ long long calc_sz;
+ long long calc_xcnt;
+ long long calc_xsz;
+ unsigned int xattr_nms;
+ long long calc_xnms;
+};
+
+/**
+ * struct fsck_data - private FS checking information.
+ * @inodes: RB-tree of all inodes (contains @struct fsck_inode objects)
+ */
+struct fsck_data {
+ struct rb_root inodes;
+};
+
+/**
+ * add_inode - add inode information to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @ino: raw UBIFS inode to add
+ *
+ * This is a helper function for 'check_leaf()' which adds information about
+ * inode @ino to the RB-tree of inodes. Returns inode information pointer in
+ * case of success and a negative error code in case of failure.
+ */
+static struct fsck_inode *add_inode(struct ubifs_info *c,
+ struct fsck_data *fsckd,
+ struct ubifs_ino_node *ino)
+{
+ struct rb_node **p, *parent = NULL;
+ struct fsck_inode *fscki;
+ ino_t inum = key_inum_flash(c, &ino->key);
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
+ p = &fsckd->inodes.rb_node;
+ while (*p) {
+ parent = *p;
+ fscki = rb_entry(parent, struct fsck_inode, rb);
+ if (inum < fscki->inum)
+ p = &(*p)->rb_left;
+ else if (inum > fscki->inum)
+ p = &(*p)->rb_right;
+ else
+ return fscki;
+ }
+
+ if (inum > c->highest_inum) {
+ ubifs_err(c, "too high inode number, max. is %lu",
+ (unsigned long)c->highest_inum);
+ return ERR_PTR(-EINVAL);
+ }
+
+ fscki = kzalloc(sizeof(struct fsck_inode), GFP_NOFS);
+ if (!fscki)
+ return ERR_PTR(-ENOMEM);
+
+ inode = ilookup(c->vfs_sb, inum);
+
+ fscki->inum = inum;
+ /*
+ * If the inode is present in the VFS inode cache, use it instead of
+ * the on-flash inode which might be out-of-date. E.g., the size might
+ * be out-of-date. If we do not do this, the following may happen, for
+ * example:
+ * 1. A power cut happens
+ * 2. We mount the file-system R/O, the replay process fixes up the
+ * inode size in the VFS cache, but on on-flash.
+ * 3. 'check_leaf()' fails because it hits a data node beyond inode
+ * size.
+ */
+ if (!inode) {
+ fscki->nlink = le32_to_cpu(ino->nlink);
+ fscki->size = le64_to_cpu(ino->size);
+ fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
+ fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
+ fscki->mode = le32_to_cpu(ino->mode);
+ } else {
+ ui = ubifs_inode(inode);
+ fscki->nlink = inode->i_nlink;
+ fscki->size = inode->i_size;
+ fscki->xattr_cnt = ui->xattr_cnt;
+ fscki->xattr_sz = ui->xattr_size;
+ fscki->xattr_nms = ui->xattr_names;
+ fscki->mode = inode->i_mode;
+ iput(inode);
+ }
+
+ if (S_ISDIR(fscki->mode)) {
+ fscki->calc_sz = UBIFS_INO_NODE_SZ;
+ fscki->calc_cnt = 2;
+ }
+
+ rb_link_node(&fscki->rb, parent, p);
+ rb_insert_color(&fscki->rb, &fsckd->inodes);
+
+ return fscki;
+}
+
+/**
+ * search_inode - search inode in the RB-tree of inodes.
+ * @fsckd: FS checking information
+ * @inum: inode number to search
+ *
+ * This is a helper function for 'check_leaf()' which searches inode @inum in
+ * the RB-tree of inodes and returns an inode information pointer or %NULL if
+ * the inode was not found.
+ */
+static struct fsck_inode *search_inode(struct fsck_data *fsckd, ino_t inum)
+{
+ struct rb_node *p;
+ struct fsck_inode *fscki;
+
+ p = fsckd->inodes.rb_node;
+ while (p) {
+ fscki = rb_entry(p, struct fsck_inode, rb);
+ if (inum < fscki->inum)
+ p = p->rb_left;
+ else if (inum > fscki->inum)
+ p = p->rb_right;
+ else
+ return fscki;
+ }
+ return NULL;
+}
+
+/**
+ * read_add_inode - read inode node and add it to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @inum: inode number to read
+ *
+ * This is a helper function for 'check_leaf()' which finds inode node @inum in
+ * the index, reads it, and adds it to the RB-tree of inodes. Returns inode
+ * information pointer in case of success and a negative error code in case of
+ * failure.
+ */
+static struct fsck_inode *read_add_inode(struct ubifs_info *c,
+ struct fsck_data *fsckd, ino_t inum)
+{
+ int n, err;
+ union ubifs_key key;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_ino_node *ino;
+ struct fsck_inode *fscki;
+
+ fscki = search_inode(fsckd, inum);
+ if (fscki)
+ return fscki;
+
+ ino_key_init(c, &key, inum);
+ err = ubifs_lookup_level0(c, &key, &znode, &n);
+ if (!err) {
+ ubifs_err(c, "inode %lu not found in index", (unsigned long)inum);
+ return ERR_PTR(-ENOENT);
+ } else if (err < 0) {
+ ubifs_err(c, "error %d while looking up inode %lu",
+ err, (unsigned long)inum);
+ return ERR_PTR(err);
+ }
+
+ zbr = &znode->zbranch[n];
+ if (zbr->len < UBIFS_INO_NODE_SZ) {
+ ubifs_err(c, "bad node %lu node length %d",
+ (unsigned long)inum, zbr->len);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ino = kmalloc(zbr->len, GFP_NOFS);
+ if (!ino)
+ return ERR_PTR(-ENOMEM);
+
+ err = ubifs_tnc_read_node(c, zbr, ino);
+ if (err) {
+ ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ kfree(ino);
+ return ERR_PTR(err);
+ }
+
+ fscki = add_inode(c, fsckd, ino);
+ kfree(ino);
+ if (IS_ERR(fscki)) {
+ ubifs_err(c, "error %ld while adding inode %lu node",
+ PTR_ERR(fscki), (unsigned long)inum);
+ return fscki;
+ }
+
+ return fscki;
+}
+
+/**
+ * check_leaf - check leaf node.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of the leaf node to check
+ * @priv: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which is called for
+ * every single leaf node while walking the indexing tree. It checks that the
+ * leaf node referred from the indexing tree exists, has correct CRC, and does
+ * some other basic validation. This function is also responsible for building
+ * an RB-tree of inodes - it adds all inodes into the RB-tree. It also
+ * calculates reference count, size, etc for each inode in order to later
+ * compare them to the information stored inside the inodes and detect possible
+ * inconsistencies. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *priv)
+{
+ ino_t inum;
+ void *node;
+ struct ubifs_ch *ch;
+ int err, type = key_type(c, &zbr->key);
+ struct fsck_inode *fscki;
+
+ if (zbr->len < UBIFS_CH_SZ) {
+ ubifs_err(c, "bad leaf length %d (LEB %d:%d)",
+ zbr->len, zbr->lnum, zbr->offs);
+ return -EINVAL;
+ }
+
+ node = kmalloc(zbr->len, GFP_NOFS);
+ if (!node)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, node);
+ if (err) {
+ ubifs_err(c, "cannot read leaf node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ goto out_free;
+ }
+
+ /* If this is an inode node, add it to RB-tree of inodes */
+ if (type == UBIFS_INO_KEY) {
+ fscki = add_inode(c, priv, node);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while adding inode node", err);
+ goto out_dump;
+ }
+ goto out;
+ }
+
+ if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY &&
+ type != UBIFS_DATA_KEY) {
+ ubifs_err(c, "unexpected node type %d at LEB %d:%d",
+ type, zbr->lnum, zbr->offs);
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ ch = node;
+ if (le64_to_cpu(ch->sqnum) > c->max_sqnum) {
+ ubifs_err(c, "too high sequence number, max. is %llu",
+ c->max_sqnum);
+ err = -EINVAL;
+ goto out_dump;
+ }
+
+ if (type == UBIFS_DATA_KEY) {
+ long long blk_offs;
+ struct ubifs_data_node *dn = node;
+
+ ubifs_assert(c, zbr->len >= UBIFS_DATA_NODE_SZ);
+
+ /*
+ * Search the inode node this data node belongs to and insert
+ * it to the RB-tree of inodes.
+ */
+ inum = key_inum_flash(c, &dn->key);
+ fscki = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while processing data node and trying to find inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ /* Make sure the data node is within inode size */
+ blk_offs = key_block_flash(c, &dn->key);
+ blk_offs <<= UBIFS_BLOCK_SHIFT;
+ blk_offs += le32_to_cpu(dn->size);
+ if (blk_offs > fscki->size) {
+ ubifs_err(c, "data node at LEB %d:%d is not within inode size %lld",
+ zbr->lnum, zbr->offs, fscki->size);
+ err = -EINVAL;
+ goto out_dump;
+ }
+ } else {
+ int nlen;
+ struct ubifs_dent_node *dent = node;
+ struct fsck_inode *fscki1;
+
+ ubifs_assert(c, zbr->len >= UBIFS_DENT_NODE_SZ);
+
+ err = ubifs_validate_entry(c, dent);
+ if (err)
+ goto out_dump;
+
+ /*
+ * Search the inode node this entry refers to and the parent
+ * inode node and insert them to the RB-tree of inodes.
+ */
+ inum = le64_to_cpu(dent->inum);
+ fscki = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki)) {
+ err = PTR_ERR(fscki);
+ ubifs_err(c, "error %d while processing entry node and trying to find inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ /* Count how many direntries or xentries refers this inode */
+ fscki->references += 1;
+
+ inum = key_inum_flash(c, &dent->key);
+ fscki1 = read_add_inode(c, priv, inum);
+ if (IS_ERR(fscki1)) {
+ err = PTR_ERR(fscki1);
+ ubifs_err(c, "error %d while processing entry node and trying to find parent inode node %lu",
+ err, (unsigned long)inum);
+ goto out_dump;
+ }
+
+ nlen = le16_to_cpu(dent->nlen);
+ if (type == UBIFS_XENT_KEY) {
+ fscki1->calc_xcnt += 1;
+ fscki1->calc_xsz += CALC_DENT_SIZE(nlen);
+ fscki1->calc_xsz += CALC_XATTR_BYTES(fscki->size);
+ fscki1->calc_xnms += nlen;
+ } else {
+ fscki1->calc_sz += CALC_DENT_SIZE(nlen);
+ if (dent->type == UBIFS_ITYPE_DIR)
+ fscki1->calc_cnt += 1;
+ }
+ }
+
+out:
+ kfree(node);
+ return 0;
+
+out_dump:
+ ubifs_msg(c, "dump of node at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, node, zbr->len);
+out_free:
+ kfree(node);
+ return err;
+}
+
+/**
+ * free_inodes - free RB-tree of inodes.
+ * @fsckd: FS checking information
+ */
+static void free_inodes(struct fsck_data *fsckd)
+{
+ struct fsck_inode *fscki, *n;
+
+ rbtree_postorder_for_each_entry_safe(fscki, n, &fsckd->inodes, rb)
+ kfree(fscki);
+}
+
+/**
+ * check_inodes - checks all inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which walks the
+ * RB-tree of inodes after the index scan has been finished, and checks that
+ * inode nlink, size, etc are correct. Returns zero if inodes are fine,
+ * %-EINVAL if not, and a negative error code in case of failure.
+ */
+static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd)
+{
+ int n, err;
+ union ubifs_key key;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zbr;
+ struct ubifs_ino_node *ino;
+ struct fsck_inode *fscki;
+ struct rb_node *this = rb_first(&fsckd->inodes);
+
+ while (this) {
+ fscki = rb_entry(this, struct fsck_inode, rb);
+ this = rb_next(this);
+
+ if (S_ISDIR(fscki->mode)) {
+ /*
+ * Directories have to have exactly one reference (they
+ * cannot have hardlinks), although root inode is an
+ * exception.
+ */
+ if (fscki->inum != UBIFS_ROOT_INO &&
+ fscki->references != 1) {
+ ubifs_err(c, "directory inode %lu has %d direntries which refer it, but should be 1",
+ (unsigned long)fscki->inum,
+ fscki->references);
+ goto out_dump;
+ }
+ if (fscki->inum == UBIFS_ROOT_INO &&
+ fscki->references != 0) {
+ ubifs_err(c, "root inode %lu has non-zero (%d) direntries which refer it",
+ (unsigned long)fscki->inum,
+ fscki->references);
+ goto out_dump;
+ }
+ if (fscki->calc_sz != fscki->size) {
+ ubifs_err(c, "directory inode %lu size is %lld, but calculated size is %lld",
+ (unsigned long)fscki->inum,
+ fscki->size, fscki->calc_sz);
+ goto out_dump;
+ }
+ if (fscki->calc_cnt != fscki->nlink) {
+ ubifs_err(c, "directory inode %lu nlink is %d, but calculated nlink is %d",
+ (unsigned long)fscki->inum,
+ fscki->nlink, fscki->calc_cnt);
+ goto out_dump;
+ }
+ } else {
+ if (fscki->references != fscki->nlink) {
+ ubifs_err(c, "inode %lu nlink is %d, but calculated nlink is %d",
+ (unsigned long)fscki->inum,
+ fscki->nlink, fscki->references);
+ goto out_dump;
+ }
+ }
+ if (fscki->xattr_sz != fscki->calc_xsz) {
+ ubifs_err(c, "inode %lu has xattr size %u, but calculated size is %lld",
+ (unsigned long)fscki->inum, fscki->xattr_sz,
+ fscki->calc_xsz);
+ goto out_dump;
+ }
+ if (fscki->xattr_cnt != fscki->calc_xcnt) {
+ ubifs_err(c, "inode %lu has %u xattrs, but calculated count is %lld",
+ (unsigned long)fscki->inum,
+ fscki->xattr_cnt, fscki->calc_xcnt);
+ goto out_dump;
+ }
+ if (fscki->xattr_nms != fscki->calc_xnms) {
+ ubifs_err(c, "inode %lu has xattr names' size %u, but calculated names' size is %lld",
+ (unsigned long)fscki->inum, fscki->xattr_nms,
+ fscki->calc_xnms);
+ goto out_dump;
+ }
+ }
+
+ return 0;
+
+out_dump:
+ /* Read the bad inode and dump it */
+ ino_key_init(c, &key, fscki->inum);
+ err = ubifs_lookup_level0(c, &key, &znode, &n);
+ if (!err) {
+ ubifs_err(c, "inode %lu not found in index",
+ (unsigned long)fscki->inum);
+ return -ENOENT;
+ } else if (err < 0) {
+ ubifs_err(c, "error %d while looking up inode %lu",
+ err, (unsigned long)fscki->inum);
+ return err;
+ }
+
+ zbr = &znode->zbranch[n];
+ ino = kmalloc(zbr->len, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, ino);
+ if (err) {
+ ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d",
+ zbr->lnum, zbr->offs, err);
+ kfree(ino);
+ return err;
+ }
+
+ ubifs_msg(c, "dump of the inode %lu sitting in LEB %d:%d",
+ (unsigned long)fscki->inum, zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, ino, zbr->len);
+ kfree(ino);
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_filesystem - check the file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks the file system, namely:
+ * o makes sure that all leaf nodes exist and their CRCs are correct;
+ * o makes sure inode nlink, size, xattr size/count are correct (for all
+ * inodes).
+ *
+ * The function reads whole indexing tree and all nodes, so it is pretty
+ * heavy-weight. Returns zero if the file-system is consistent, %-EINVAL if
+ * not, and a negative error code in case of failure.
+ */
+int dbg_check_filesystem(struct ubifs_info *c)
+{
+ int err;
+ struct fsck_data fsckd;
+
+ if (!dbg_is_chk_fs(c))
+ return 0;
+
+ fsckd.inodes = RB_ROOT;
+ err = dbg_walk_index(c, check_leaf, NULL, &fsckd);
+ if (err)
+ goto out_free;
+
+ err = check_inodes(c, &fsckd);
+ if (err)
+ goto out_free;
+
+ free_inodes(&fsckd);
+ return 0;
+
+out_free:
+ ubifs_err(c, "file-system check failed with error %d", err);
+ dump_stack();
+ free_inodes(&fsckd);
+ return err;
+}
+
+/**
+ * dbg_check_data_nodes_order - check that list of data nodes is sorted.
+ * @c: UBIFS file-system description object
+ * @head: the list of nodes ('struct ubifs_scan_node' objects)
+ *
+ * This function returns zero if the list of data nodes is sorted correctly,
+ * and %-EINVAL if not.
+ */
+int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
+{
+ struct list_head *cur;
+ struct ubifs_scan_node *sa, *sb;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ for (cur = head->next; cur->next != head; cur = cur->next) {
+ ino_t inuma, inumb;
+ uint32_t blka, blkb;
+
+ cond_resched();
+ sa = container_of(cur, struct ubifs_scan_node, list);
+ sb = container_of(cur->next, struct ubifs_scan_node, list);
+
+ if (sa->type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type %d", sa->type);
+ ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
+ return -EINVAL;
+ }
+ if (sb->type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type %d", sb->type);
+ ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
+ return -EINVAL;
+ }
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma < inumb)
+ continue;
+ if (inuma > inumb) {
+ ubifs_err(c, "larger inum %lu goes before inum %lu",
+ (unsigned long)inuma, (unsigned long)inumb);
+ goto error_dump;
+ }
+
+ blka = key_block(c, &sa->key);
+ blkb = key_block(c, &sb->key);
+
+ if (blka > blkb) {
+ ubifs_err(c, "larger block %u goes before %u", blka, blkb);
+ goto error_dump;
+ }
+ if (blka == blkb) {
+ ubifs_err(c, "two data nodes for the same block");
+ goto error_dump;
+ }
+ }
+
+ return 0;
+
+error_dump:
+ ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
+ ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
+ return -EINVAL;
+}
+
+/**
+ * dbg_check_nondata_nodes_order - check that list of data nodes is sorted.
+ * @c: UBIFS file-system description object
+ * @head: the list of nodes ('struct ubifs_scan_node' objects)
+ *
+ * This function returns zero if the list of non-data nodes is sorted correctly,
+ * and %-EINVAL if not.
+ */
+int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
+{
+ struct list_head *cur;
+ struct ubifs_scan_node *sa, *sb;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ for (cur = head->next; cur->next != head; cur = cur->next) {
+ ino_t inuma, inumb;
+ uint32_t hasha, hashb;
+
+ cond_resched();
+ sa = container_of(cur, struct ubifs_scan_node, list);
+ sb = container_of(cur->next, struct ubifs_scan_node, list);
+
+ if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE &&
+ sa->type != UBIFS_XENT_NODE) {
+ ubifs_err(c, "bad node type %d", sa->type);
+ ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
+ return -EINVAL;
+ }
+ if (sb->type != UBIFS_INO_NODE && sb->type != UBIFS_DENT_NODE &&
+ sb->type != UBIFS_XENT_NODE) {
+ ubifs_err(c, "bad node type %d", sb->type);
+ ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
+ return -EINVAL;
+ }
+
+ if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
+ ubifs_err(c, "non-inode node goes before inode node");
+ goto error_dump;
+ }
+
+ if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE)
+ continue;
+
+ if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) {
+ /* Inode nodes are sorted in descending size order */
+ if (sa->len < sb->len) {
+ ubifs_err(c, "smaller inode node goes first");
+ goto error_dump;
+ }
+ continue;
+ }
+
+ /*
+ * This is either a dentry or xentry, which should be sorted in
+ * ascending (parent ino, hash) order.
+ */
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma < inumb)
+ continue;
+ if (inuma > inumb) {
+ ubifs_err(c, "larger inum %lu goes before inum %lu",
+ (unsigned long)inuma, (unsigned long)inumb);
+ goto error_dump;
+ }
+
+ hasha = key_block(c, &sa->key);
+ hashb = key_block(c, &sb->key);
+
+ if (hasha > hashb) {
+ ubifs_err(c, "larger hash %u goes before %u",
+ hasha, hashb);
+ goto error_dump;
+ }
+ }
+
+ return 0;
+
+error_dump:
+ ubifs_msg(c, "dumping first node");
+ ubifs_dump_node(c, sa->node, c->leb_size - sa->offs);
+ ubifs_msg(c, "dumping second node");
+ ubifs_dump_node(c, sb->node, c->leb_size - sb->offs);
+ return -EINVAL;
+}
+
+static inline int chance(unsigned int n, unsigned int out_of)
+{
+ return !!(get_random_u32_below(out_of) + 1 <= n);
+
+}
+
+static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
+{
+ struct ubifs_debug_info *d = c->dbg;
+
+ ubifs_assert(c, dbg_is_tst_rcvry(c));
+
+ if (!d->pc_cnt) {
+ /* First call - decide delay to the power cut */
+ if (chance(1, 2)) {
+ unsigned long delay;
+
+ if (chance(1, 2)) {
+ d->pc_delay = 1;
+ /* Fail within 1 minute */
+ delay = get_random_u32_below(60000);
+ d->pc_timeout = jiffies;
+ d->pc_timeout += msecs_to_jiffies(delay);
+ ubifs_warn(c, "failing after %lums", delay);
+ } else {
+ d->pc_delay = 2;
+ delay = get_random_u32_below(10000);
+ /* Fail within 10000 operations */
+ d->pc_cnt_max = delay;
+ ubifs_warn(c, "failing after %lu calls", delay);
+ }
+ }
+
+ d->pc_cnt += 1;
+ }
+
+ /* Determine if failure delay has expired */
+ if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
+ return 0;
+ if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
+ return 0;
+
+ if (lnum == UBIFS_SB_LNUM) {
+ if (write && chance(1, 2))
+ return 0;
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in super block LEB %d", lnum);
+ } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in master LEB %d", lnum);
+ } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
+ if (write && chance(99, 100))
+ return 0;
+ if (chance(399, 400))
+ return 0;
+ ubifs_warn(c, "failing in log LEB %d", lnum);
+ } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
+ if (write && chance(7, 8))
+ return 0;
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in LPT LEB %d", lnum);
+ } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
+ if (write && chance(1, 2))
+ return 0;
+ if (chance(9, 10))
+ return 0;
+ ubifs_warn(c, "failing in orphan LEB %d", lnum);
+ } else if (lnum == c->ihead_lnum) {
+ if (chance(99, 100))
+ return 0;
+ ubifs_warn(c, "failing in index head LEB %d", lnum);
+ } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
+ if (chance(9, 10))
+ return 0;
+ ubifs_warn(c, "failing in GC head LEB %d", lnum);
+ } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
+ !ubifs_search_bud(c, lnum)) {
+ if (chance(19, 20))
+ return 0;
+ ubifs_warn(c, "failing in non-bud LEB %d", lnum);
+ } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
+ c->cmt_state == COMMIT_RUNNING_REQUIRED) {
+ if (chance(999, 1000))
+ return 0;
+ ubifs_warn(c, "failing in bud LEB %d commit running", lnum);
+ } else {
+ if (chance(9999, 10000))
+ return 0;
+ ubifs_warn(c, "failing in bud LEB %d commit not running", lnum);
+ }
+
+ d->pc_happened = 1;
+ ubifs_warn(c, "========== Power cut emulated ==========");
+ dump_stack();
+ return 1;
+}
+
+static int corrupt_data(const struct ubifs_info *c, const void *buf,
+ unsigned int len)
+{
+ unsigned int from, to, ffs = chance(1, 2);
+ unsigned char *p = (void *)buf;
+
+ from = get_random_u32_below(len);
+ /* Corruption span max to end of write unit */
+ to = min(len, ALIGN(from + 1, c->max_write_size));
+
+ ubifs_warn(c, "filled bytes %u-%u with %s", from, to - 1,
+ ffs ? "0xFFs" : "random data");
+
+ if (ffs)
+ memset(p + from, 0xFF, to - from);
+ else
+ get_random_bytes(p + from, to - from);
+
+ return to;
+}
+
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
+ int offs, int len)
+{
+ int err, failing;
+
+ if (dbg_is_power_cut(c))
+ return -EROFS;
+
+ failing = power_cut_emulated(c, lnum, 1);
+ if (failing) {
+ len = corrupt_data(c, buf, len);
+ ubifs_warn(c, "actually write %d bytes to LEB %d:%d (the buffer was corrupted)",
+ len, lnum, offs);
+ }
+ err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
+ if (err)
+ return err;
+ if (failing)
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
+ int len)
+{
+ int err;
+
+ if (dbg_is_power_cut(c))
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 1))
+ return -EROFS;
+ err = ubi_leb_change(c->ubi, lnum, buf, len);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 1))
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_unmap(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ if (dbg_is_power_cut(c))
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_unmap(c->ubi, lnum);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ return 0;
+}
+
+int dbg_leb_map(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ if (dbg_is_power_cut(c))
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_map(c->ubi, lnum);
+ if (err)
+ return err;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ return 0;
+}
+
+/*
+ * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular file-system mounts.
+ */
+static struct dentry *dfs_rootdir;
+
+static int dfs_file_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * provide_user_output - provide output to the user reading a debugfs file.
+ * @val: boolean value for the answer
+ * @u: the buffer to store the answer at
+ * @count: size of the buffer
+ * @ppos: position in the @u output buffer
+ *
+ * This is a simple helper function which stores @val boolean value in the user
+ * buffer when the user reads one of UBIFS debugfs files. Returns amount of
+ * bytes written to @u in case of success and a negative error code in case of
+ * failure.
+ */
+static int provide_user_output(int val, char __user *u, size_t count,
+ loff_t *ppos)
+{
+ char buf[3];
+
+ if (val)
+ buf[0] = '1';
+ else
+ buf[0] = '0';
+ buf[1] = '\n';
+ buf[2] = 0x00;
+
+ return simple_read_from_buffer(u, count, ppos, buf, 2);
+}
+
+static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
+ loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ struct ubifs_info *c = file->private_data;
+ struct ubifs_debug_info *d = c->dbg;
+ int val;
+
+ if (dent == d->dfs_chk_gen)
+ val = d->chk_gen;
+ else if (dent == d->dfs_chk_index)
+ val = d->chk_index;
+ else if (dent == d->dfs_chk_orph)
+ val = d->chk_orph;
+ else if (dent == d->dfs_chk_lprops)
+ val = d->chk_lprops;
+ else if (dent == d->dfs_chk_fs)
+ val = d->chk_fs;
+ else if (dent == d->dfs_tst_rcvry)
+ val = d->tst_rcvry;
+ else if (dent == d->dfs_ro_error)
+ val = c->ro_error;
+ else
+ return -EINVAL;
+
+ return provide_user_output(val, u, count, ppos);
+}
+
+/**
+ * interpret_user_input - interpret user debugfs file input.
+ * @u: user-provided buffer with the input
+ * @count: buffer size
+ *
+ * This is a helper function which interpret user input to a boolean UBIFS
+ * debugfs file. Returns %0 or %1 in case of success and a negative error code
+ * in case of failure.
+ */
+static int interpret_user_input(const char __user *u, size_t count)
+{
+ size_t buf_size;
+ char buf[8];
+
+ buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, u, buf_size))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ return 1;
+ else if (buf[0] == '0')
+ return 0;
+
+ return -EINVAL;
+}
+
+static ssize_t dfs_file_write(struct file *file, const char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct ubifs_info *c = file->private_data;
+ struct ubifs_debug_info *d = c->dbg;
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ if (file->f_path.dentry == d->dfs_dump_lprops) {
+ ubifs_dump_lprops(c);
+ return count;
+ }
+ if (file->f_path.dentry == d->dfs_dump_budg) {
+ ubifs_dump_budg(c, &c->bi);
+ return count;
+ }
+ if (file->f_path.dentry == d->dfs_dump_tnc) {
+ mutex_lock(&c->tnc_mutex);
+ ubifs_dump_tnc(c);
+ mutex_unlock(&c->tnc_mutex);
+ return count;
+ }
+
+ val = interpret_user_input(u, count);
+ if (val < 0)
+ return val;
+
+ if (dent == d->dfs_chk_gen)
+ d->chk_gen = val;
+ else if (dent == d->dfs_chk_index)
+ d->chk_index = val;
+ else if (dent == d->dfs_chk_orph)
+ d->chk_orph = val;
+ else if (dent == d->dfs_chk_lprops)
+ d->chk_lprops = val;
+ else if (dent == d->dfs_chk_fs)
+ d->chk_fs = val;
+ else if (dent == d->dfs_tst_rcvry)
+ d->tst_rcvry = val;
+ else if (dent == d->dfs_ro_error)
+ c->ro_error = !!val;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations dfs_fops = {
+ .open = dfs_file_open,
+ .read = dfs_file_read,
+ .write = dfs_file_write,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init_fs - initialize debugfs for UBIFS instance.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates all debugfs files for this instance of UBIFS.
+ *
+ * Note, the only reason we have not merged this function with the
+ * 'ubifs_debugging_init()' function is because it is better to initialize
+ * debugfs interfaces at the very end of the mount process, and remove them at
+ * the very beginning of the mount process.
+ */
+void dbg_debugfs_init_fs(struct ubifs_info *c)
+{
+ int n;
+ const char *fname;
+ struct ubifs_debug_info *d = c->dbg;
+
+ n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
+ c->vi.ubi_num, c->vi.vol_id);
+ if (n > UBIFS_DFS_DIR_LEN) {
+ /* The array size is too small */
+ return;
+ }
+
+ fname = d->dfs_dir_name;
+ d->dfs_dir = debugfs_create_dir(fname, dfs_rootdir);
+
+ fname = "dump_lprops";
+ d->dfs_dump_lprops = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+
+ fname = "dump_budg";
+ d->dfs_dump_budg = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+
+ fname = "dump_tnc";
+ d->dfs_dump_tnc = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c,
+ &dfs_fops);
+
+ fname = "chk_general";
+ d->dfs_chk_gen = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "chk_index";
+ d->dfs_chk_index = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "chk_orphans";
+ d->dfs_chk_orph = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "chk_lprops";
+ d->dfs_chk_lprops = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "chk_fs";
+ d->dfs_chk_fs = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "tst_recovery";
+ d->dfs_tst_rcvry = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+
+ fname = "ro_error";
+ d->dfs_ro_error = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ d->dfs_dir, c, &dfs_fops);
+}
+
+/**
+ * dbg_debugfs_exit_fs - remove all debugfs files.
+ * @c: UBIFS file-system description object
+ */
+void dbg_debugfs_exit_fs(struct ubifs_info *c)
+{
+ debugfs_remove_recursive(c->dbg->dfs_dir);
+}
+
+struct ubifs_global_debug_info ubifs_dbg;
+
+static struct dentry *dfs_chk_gen;
+static struct dentry *dfs_chk_index;
+static struct dentry *dfs_chk_orph;
+static struct dentry *dfs_chk_lprops;
+static struct dentry *dfs_chk_fs;
+static struct dentry *dfs_tst_rcvry;
+
+static ssize_t dfs_global_file_read(struct file *file, char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ if (dent == dfs_chk_gen)
+ val = ubifs_dbg.chk_gen;
+ else if (dent == dfs_chk_index)
+ val = ubifs_dbg.chk_index;
+ else if (dent == dfs_chk_orph)
+ val = ubifs_dbg.chk_orph;
+ else if (dent == dfs_chk_lprops)
+ val = ubifs_dbg.chk_lprops;
+ else if (dent == dfs_chk_fs)
+ val = ubifs_dbg.chk_fs;
+ else if (dent == dfs_tst_rcvry)
+ val = ubifs_dbg.tst_rcvry;
+ else
+ return -EINVAL;
+
+ return provide_user_output(val, u, count, ppos);
+}
+
+static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file->f_path.dentry;
+ int val;
+
+ val = interpret_user_input(u, count);
+ if (val < 0)
+ return val;
+
+ if (dent == dfs_chk_gen)
+ ubifs_dbg.chk_gen = val;
+ else if (dent == dfs_chk_index)
+ ubifs_dbg.chk_index = val;
+ else if (dent == dfs_chk_orph)
+ ubifs_dbg.chk_orph = val;
+ else if (dent == dfs_chk_lprops)
+ ubifs_dbg.chk_lprops = val;
+ else if (dent == dfs_chk_fs)
+ ubifs_dbg.chk_fs = val;
+ else if (dent == dfs_tst_rcvry)
+ ubifs_dbg.tst_rcvry = val;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations dfs_global_fops = {
+ .read = dfs_global_file_read,
+ .write = dfs_global_file_write,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init - initialize debugfs file-system.
+ *
+ * UBIFS uses debugfs file-system to expose various debugging knobs to
+ * user-space. This function creates "ubifs" directory in the debugfs
+ * file-system.
+ */
+void dbg_debugfs_init(void)
+{
+ const char *fname;
+
+ fname = "ubifs";
+ dfs_rootdir = debugfs_create_dir(fname, NULL);
+
+ fname = "chk_general";
+ dfs_chk_gen = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir,
+ NULL, &dfs_global_fops);
+
+ fname = "chk_index";
+ dfs_chk_index = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ dfs_rootdir, NULL, &dfs_global_fops);
+
+ fname = "chk_orphans";
+ dfs_chk_orph = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ dfs_rootdir, NULL, &dfs_global_fops);
+
+ fname = "chk_lprops";
+ dfs_chk_lprops = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ dfs_rootdir, NULL, &dfs_global_fops);
+
+ fname = "chk_fs";
+ dfs_chk_fs = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir,
+ NULL, &dfs_global_fops);
+
+ fname = "tst_recovery";
+ dfs_tst_rcvry = debugfs_create_file(fname, S_IRUSR | S_IWUSR,
+ dfs_rootdir, NULL, &dfs_global_fops);
+}
+
+/**
+ * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
+ */
+void dbg_debugfs_exit(void)
+{
+ debugfs_remove_recursive(dfs_rootdir);
+}
+
+void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
+ const char *file, int line)
+{
+ ubifs_err(c, "UBIFS assert failed: %s, in %s:%u", expr, file, line);
+
+ switch (c->assert_action) {
+ case ASSACT_PANIC:
+ BUG();
+ break;
+
+ case ASSACT_RO:
+ ubifs_ro_mode(c, -EINVAL);
+ break;
+
+ case ASSACT_REPORT:
+ default:
+ dump_stack();
+ break;
+
+ }
+}
+
+/**
+ * ubifs_debugging_init - initialize UBIFS debugging.
+ * @c: UBIFS file-system description object
+ *
+ * This function initializes debugging-related data for the file system.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_debugging_init(struct ubifs_info *c)
+{
+ c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
+ if (!c->dbg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * ubifs_debugging_exit - free debugging data.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_debugging_exit(struct ubifs_info *c)
+{
+ kfree(c->dbg);
+}
diff --git a/ubifs-utils/libubifs/debug.h b/ubifs-utils/libubifs/debug.h
new file mode 100644
index 00000000..ed966108
--- /dev/null
+++ b/ubifs-utils/libubifs/debug.h
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+#ifndef __UBIFS_DEBUG_H__
+#define __UBIFS_DEBUG_H__
+
+/* Checking helper functions */
+typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr, void *priv);
+typedef int (*dbg_znode_callback)(struct ubifs_info *c,
+ struct ubifs_znode *znode, void *priv);
+
+/*
+ * The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
+ */
+#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
+#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
+
+/**
+ * ubifs_debug_info - per-FS debugging information.
+ * @old_zroot: old index root - used by 'dbg_check_old_index()'
+ * @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
+ * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
+ *
+ * @pc_happened: non-zero if an emulated power cut happened
+ * @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
+ * @pc_timeout: time in jiffies when delay of failure mode expires
+ * @pc_cnt: current number of calls to failure mode I/O functions
+ * @pc_cnt_max: number of calls by which to delay failure mode
+ *
+ * @chk_lpt_sz: used by LPT tree size checker
+ * @chk_lpt_sz2: used by LPT tree size checker
+ * @chk_lpt_wastage: used by LPT tree size checker
+ * @chk_lpt_lebs: used by LPT tree size checker
+ * @new_nhead_offs: used by LPT tree size checker
+ * @new_ihead_lnum: used by debugging to check @c->ihead_lnum
+ * @new_ihead_offs: used by debugging to check @c->ihead_offs
+ *
+ * @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
+ * @saved_bi: saved budgeting information
+ * @saved_free: saved amount of free space
+ * @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
+ *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ *
+ * @dfs_dir_name: name of debugfs directory containing this file-system's files
+ * @dfs_dir: direntry object of the file-system debugfs directory
+ * @dfs_dump_lprops: "dump lprops" debugfs knob
+ * @dfs_dump_budg: "dump budgeting information" debugfs knob
+ * @dfs_dump_tnc: "dump TNC" debugfs knob
+ * @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks
+ * @dfs_chk_index: debugfs knob to enable UBIFS index extra checks
+ * @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks
+ * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
+ * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
+ * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
+ * @dfs_ro_error: debugfs knob to switch UBIFS to R/O mode (different to
+ * re-mounting to R/O mode because it does not flush any buffers
+ * and UBIFS just starts returning -EROFS on all write
+ * operations)
+ */
+struct ubifs_debug_info {
+ struct ubifs_zbranch old_zroot;
+ int old_zroot_level;
+ unsigned long long old_zroot_sqnum;
+
+ int pc_happened;
+ int pc_delay;
+ unsigned long pc_timeout;
+ unsigned int pc_cnt;
+ unsigned int pc_cnt_max;
+
+ long long chk_lpt_sz;
+ long long chk_lpt_sz2;
+ long long chk_lpt_wastage;
+ int chk_lpt_lebs;
+ int new_nhead_offs;
+ int new_ihead_lnum;
+ int new_ihead_offs;
+
+ struct ubifs_lp_stats saved_lst;
+ struct ubifs_budg_info saved_bi;
+ long long saved_free;
+ int saved_idx_gc_cnt;
+
+ unsigned int chk_gen:1;
+ unsigned int chk_index:1;
+ unsigned int chk_orph:1;
+ unsigned int chk_lprops:1;
+ unsigned int chk_fs:1;
+ unsigned int tst_rcvry:1;
+
+ char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1];
+ struct dentry *dfs_dir;
+ struct dentry *dfs_dump_lprops;
+ struct dentry *dfs_dump_budg;
+ struct dentry *dfs_dump_tnc;
+ struct dentry *dfs_chk_gen;
+ struct dentry *dfs_chk_index;
+ struct dentry *dfs_chk_orph;
+ struct dentry *dfs_chk_lprops;
+ struct dentry *dfs_chk_fs;
+ struct dentry *dfs_tst_rcvry;
+ struct dentry *dfs_ro_error;
+};
+
+/**
+ * ubifs_global_debug_info - global (not per-FS) UBIFS debugging information.
+ *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ */
+struct ubifs_global_debug_info {
+ unsigned int chk_gen:1;
+ unsigned int chk_index:1;
+ unsigned int chk_orph:1;
+ unsigned int chk_lprops:1;
+ unsigned int chk_fs:1;
+ unsigned int tst_rcvry:1;
+};
+
+void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
+ const char *file, int line);
+
+#define ubifs_assert(c, expr) do { \
+ if (unlikely(!(expr))) { \
+ ubifs_assert_failed((struct ubifs_info *)c, #expr, __FILE__, \
+ __LINE__); \
+ } \
+} while (0)
+
+#define ubifs_assert_cmt_locked(c) do { \
+ if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
+ up_write(&(c)->commit_sem); \
+ ubifs_err(c, "commit lock is not locked!\n"); \
+ ubifs_assert(c, 0); \
+ } \
+} while (0)
+
+#define ubifs_dbg_msg(type, fmt, ...) \
+ pr_debug("UBIFS DBG " type " (pid %d): " fmt "\n", current->pid, \
+ ##__VA_ARGS__)
+
+#define DBG_KEY_BUF_LEN 48
+#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
+ char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
+ pr_debug("UBIFS DBG " type " (pid %d): " fmt "%s\n", current->pid, \
+ ##__VA_ARGS__, \
+ dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \
+} while (0)
+
+/* General messages */
+#define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__)
+/* Additional journal messages */
+#define dbg_jnl(fmt, ...) ubifs_dbg_msg("jnl", fmt, ##__VA_ARGS__)
+#define dbg_jnlk(key, fmt, ...) \
+ ubifs_dbg_msg_key("jnl", key, fmt, ##__VA_ARGS__)
+/* Additional TNC messages */
+#define dbg_tnc(fmt, ...) ubifs_dbg_msg("tnc", fmt, ##__VA_ARGS__)
+#define dbg_tnck(key, fmt, ...) \
+ ubifs_dbg_msg_key("tnc", key, fmt, ##__VA_ARGS__)
+/* Additional lprops messages */
+#define dbg_lp(fmt, ...) ubifs_dbg_msg("lp", fmt, ##__VA_ARGS__)
+/* Additional LEB find messages */
+#define dbg_find(fmt, ...) ubifs_dbg_msg("find", fmt, ##__VA_ARGS__)
+/* Additional mount messages */
+#define dbg_mnt(fmt, ...) ubifs_dbg_msg("mnt", fmt, ##__VA_ARGS__)
+#define dbg_mntk(key, fmt, ...) \
+ ubifs_dbg_msg_key("mnt", key, fmt, ##__VA_ARGS__)
+/* Additional I/O messages */
+#define dbg_io(fmt, ...) ubifs_dbg_msg("io", fmt, ##__VA_ARGS__)
+/* Additional commit messages */
+#define dbg_cmt(fmt, ...) ubifs_dbg_msg("cmt", fmt, ##__VA_ARGS__)
+/* Additional budgeting messages */
+#define dbg_budg(fmt, ...) ubifs_dbg_msg("budg", fmt, ##__VA_ARGS__)
+/* Additional log messages */
+#define dbg_log(fmt, ...) ubifs_dbg_msg("log", fmt, ##__VA_ARGS__)
+/* Additional gc messages */
+#define dbg_gc(fmt, ...) ubifs_dbg_msg("gc", fmt, ##__VA_ARGS__)
+/* Additional scan messages */
+#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__)
+/* Additional recovery messages */
+#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
+
+extern struct ubifs_global_debug_info ubifs_dbg;
+
+static inline int dbg_is_chk_gen(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen);
+}
+static inline int dbg_is_chk_index(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_index || c->dbg->chk_index);
+}
+static inline int dbg_is_chk_orph(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph);
+}
+static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops);
+}
+static inline int dbg_is_chk_fs(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs);
+}
+static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
+{
+ return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry);
+}
+static inline int dbg_is_power_cut(const struct ubifs_info *c)
+{
+ return !!c->dbg->pc_happened;
+}
+
+int ubifs_debugging_init(struct ubifs_info *c);
+void ubifs_debugging_exit(struct ubifs_info *c);
+
+/* Dump functions */
+const char *dbg_ntype(int type);
+const char *dbg_cstate(int cmt_state);
+const char *dbg_jhead(int jhead);
+const char *dbg_get_key_dump(const struct ubifs_info *c,
+ const union ubifs_key *key);
+const char *dbg_snprintf_key(const struct ubifs_info *c,
+ const union ubifs_key *key, char *buffer, int len);
+void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode);
+void ubifs_dump_node(const struct ubifs_info *c, const void *node,
+ int node_len);
+void ubifs_dump_budget_req(const struct ubifs_budget_req *req);
+void ubifs_dump_lstats(const struct ubifs_lp_stats *lst);
+void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi);
+void ubifs_dump_lprop(const struct ubifs_info *c,
+ const struct ubifs_lprops *lp);
+void ubifs_dump_lprops(struct ubifs_info *c);
+void ubifs_dump_lpt_info(struct ubifs_info *c);
+void ubifs_dump_leb(const struct ubifs_info *c, int lnum);
+void ubifs_dump_znode(const struct ubifs_info *c,
+ const struct ubifs_znode *znode);
+void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ int cat);
+void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip);
+void ubifs_dump_tnc(struct ubifs_info *c);
+void ubifs_dump_index(struct ubifs_info *c);
+void ubifs_dump_lpt_lebs(const struct ubifs_info *c);
+
+int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
+ dbg_znode_callback znode_cb, void *priv);
+
+/* Checking functions */
+void dbg_save_space_info(struct ubifs_info *c);
+int dbg_check_space_info(struct ubifs_info *c);
+int dbg_check_lprops(struct ubifs_info *c);
+int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int dbg_check_cats(struct ubifs_info *c);
+int dbg_check_ltab(struct ubifs_info *c);
+int dbg_chk_lpt_free_spc(struct ubifs_info *c);
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode);
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir);
+int dbg_check_tnc(struct ubifs_info *c, int extra);
+int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
+int dbg_check_filesystem(struct ubifs_info *c);
+void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
+ int add_pos);
+int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
+ int row, int col);
+int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
+ loff_t size);
+int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
+int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
+
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len);
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
+int dbg_leb_unmap(struct ubifs_info *c, int lnum);
+int dbg_leb_map(struct ubifs_info *c, int lnum);
+
+/* Debugfs-related stuff */
+void dbg_debugfs_init(void);
+void dbg_debugfs_exit(void);
+void dbg_debugfs_init_fs(struct ubifs_info *c);
+void dbg_debugfs_exit_fs(struct ubifs_info *c);
+
+#endif /* !__UBIFS_DEBUG_H__ */
diff --git a/ubifs-utils/libubifs/dir.c b/ubifs-utils/libubifs/dir.c
new file mode 100644
index 00000000..c77ea57f
--- /dev/null
+++ b/ubifs-utils/libubifs/dir.c
@@ -0,0 +1,1744 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ * Copyright (C) 2006, 2007 University of Szeged, Hungary
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ * Zoltan Sogor
+ */
+
+/*
+ * This file implements directory operations.
+ *
+ * All FS operations in this file allocate budget before writing anything to the
+ * media. If they fail to allocate it, the error is returned. The only
+ * exceptions are 'ubifs_unlink()' and 'ubifs_rmdir()' which keep working even
+ * if they unable to allocate the budget, because deletion %-ENOSPC failure is
+ * not what users are usually ready to get. UBIFS budgeting subsystem has some
+ * space reserved for these purposes.
+ *
+ * All operations in this file write all inodes which they change straight
+ * away, instead of marking them dirty. For example, 'ubifs_link()' changes
+ * @i_size of the parent inode and writes the parent inode together with the
+ * target inode. This was done to simplify file-system recovery which would
+ * otherwise be very difficult to do. The only exception is rename which marks
+ * the re-named inode dirty (because its @i_ctime is updated) but does not
+ * write it, but just marks it as dirty.
+ */
+
+#include "ubifs.h"
+
+/**
+ * inherit_flags - inherit flags of the parent inode.
+ * @dir: parent inode
+ * @mode: new inode mode flags
+ *
+ * This is a helper function for 'ubifs_new_inode()' which inherits flag of the
+ * parent directory inode @dir. UBIFS inodes inherit the following flags:
+ * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on
+ * sub-directory basis;
+ * o %UBIFS_SYNC_FL - useful for the same reasons;
+ * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories.
+ *
+ * This function returns the inherited flags.
+ */
+static int inherit_flags(const struct inode *dir, umode_t mode)
+{
+ int flags;
+ const struct ubifs_inode *ui = ubifs_inode(dir);
+
+ if (!S_ISDIR(dir->i_mode))
+ /*
+ * The parent is not a directory, which means that an extended
+ * attribute inode is being created. No flags.
+ */
+ return 0;
+
+ flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL);
+ if (!S_ISDIR(mode))
+ /* The "DIRSYNC" flag only applies to directories */
+ flags &= ~UBIFS_DIRSYNC_FL;
+ return flags;
+}
+
+/**
+ * ubifs_new_inode - allocate new UBIFS inode object.
+ * @c: UBIFS file-system description object
+ * @dir: parent directory inode
+ * @mode: inode mode flags
+ * @is_xattr: whether the inode is xattr inode
+ *
+ * This function finds an unused inode number, allocates new inode and
+ * initializes it. Non-xattr new inode may be written with xattrs(selinux/
+ * encryption) before writing dentry, which could cause inconsistent problem
+ * when powercut happens between two operations. To deal with it, non-xattr
+ * new inode is initialized with zero-nlink and added into orphan list, caller
+ * should make sure that inode is relinked later, and make sure that orphan
+ * removing and journal writing into an committing atomic operation. Returns
+ * new inode in case of success and an error code in case of failure.
+ */
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
+ umode_t mode, bool is_xattr)
+{
+ int err;
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ bool encrypted = false;
+
+ inode = new_inode(c->vfs_sb);
+ ui = ubifs_inode(inode);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and
+ * marking them dirty in file write path (see 'file_update_time()').
+ * UBIFS has to fully control "clean <-> dirty" transitions of inodes
+ * to make budgeting work.
+ */
+ inode->i_flags |= S_NOCMTIME;
+
+ inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
+ simple_inode_init_ts(inode);
+ inode->i_mapping->nrpages = 0;
+
+ if (!is_xattr) {
+ err = fscrypt_prepare_new_inode(dir, inode, &encrypted);
+ if (err) {
+ ubifs_err(c, "fscrypt_prepare_new_inode failed: %i", err);
+ goto out_iput;
+ }
+ }
+
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_mapping->a_ops = &ubifs_file_address_operations;
+ inode->i_op = &ubifs_file_inode_operations;
+ inode->i_fop = &ubifs_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &ubifs_dir_inode_operations;
+ inode->i_fop = &ubifs_dir_operations;
+ inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;
+ break;
+ case S_IFLNK:
+ inode->i_op = &ubifs_symlink_inode_operations;
+ break;
+ case S_IFSOCK:
+ case S_IFIFO:
+ case S_IFBLK:
+ case S_IFCHR:
+ inode->i_op = &ubifs_file_inode_operations;
+ break;
+ default:
+ BUG();
+ }
+
+ ui->flags = inherit_flags(dir, mode);
+ ubifs_set_inode_flags(inode);
+ if (S_ISREG(mode))
+ ui->compr_type = c->default_compr;
+ else
+ ui->compr_type = UBIFS_COMPR_NONE;
+ ui->synced_i_size = 0;
+
+ spin_lock(&c->cnt_lock);
+ /* Inode number overflow is currently not supported */
+ if (c->highest_inum >= INUM_WARN_WATERMARK) {
+ if (c->highest_inum >= INUM_WATERMARK) {
+ spin_unlock(&c->cnt_lock);
+ ubifs_err(c, "out of inode numbers");
+ err = -EINVAL;
+ goto out_iput;
+ }
+ ubifs_warn(c, "running out of inode numbers (current %lu, max %u)",
+ (unsigned long)c->highest_inum, INUM_WATERMARK);
+ }
+
+ inode->i_ino = ++c->highest_inum;
+ /*
+ * The creation sequence number remains with this inode for its
+ * lifetime. All nodes for this inode have a greater sequence number,
+ * and so it is possible to distinguish obsolete nodes belonging to a
+ * previous incarnation of the same inode number - for example, for the
+ * purpose of rebuilding the index.
+ */
+ ui->creat_sqnum = ++c->max_sqnum;
+ spin_unlock(&c->cnt_lock);
+
+ if (!is_xattr) {
+ set_nlink(inode, 0);
+ err = ubifs_add_orphan(c, inode->i_ino);
+ if (err) {
+ ubifs_err(c, "ubifs_add_orphan failed: %i", err);
+ goto out_iput;
+ }
+ down_read(&c->commit_sem);
+ ui->del_cmtno = c->cmt_no;
+ up_read(&c->commit_sem);
+ }
+
+ if (encrypted) {
+ err = fscrypt_set_context(inode, NULL);
+ if (err) {
+ if (!is_xattr) {
+ set_nlink(inode, 1);
+ ubifs_delete_orphan(c, inode->i_ino);
+ }
+ ubifs_err(c, "fscrypt_set_context failed: %i", err);
+ goto out_iput;
+ }
+ }
+
+ return inode;
+
+out_iput:
+ make_bad_inode(inode);
+ iput(inode);
+ return ERR_PTR(err);
+}
+
+static int dbg_check_name(const struct ubifs_info *c,
+ const struct ubifs_dent_node *dent,
+ const struct fscrypt_name *nm)
+{
+ if (!dbg_is_chk_gen(c))
+ return 0;
+ if (le16_to_cpu(dent->nlen) != fname_len(nm))
+ return -EINVAL;
+ if (memcmp(dent->name, fname_name(nm), fname_len(nm)))
+ return -EINVAL;
+ return 0;
+}
+
+static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ int err;
+ union ubifs_key key;
+ struct inode *inode = NULL;
+ struct ubifs_dent_node *dent = NULL;
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct fscrypt_name nm;
+
+ dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
+
+ err = fscrypt_prepare_lookup(dir, dentry, &nm);
+ if (err == -ENOENT)
+ return d_splice_alias(NULL, dentry);
+ if (err)
+ return ERR_PTR(err);
+
+ if (fname_len(&nm) > UBIFS_MAX_NLEN) {
+ inode = ERR_PTR(-ENAMETOOLONG);
+ goto done;
+ }
+
+ dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent) {
+ inode = ERR_PTR(-ENOMEM);
+ goto done;
+ }
+
+ if (fname_name(&nm) == NULL) {
+ if (nm.hash & ~UBIFS_S_KEY_HASH_MASK)
+ goto done; /* ENOENT */
+ dent_key_init_hash(c, &key, dir->i_ino, nm.hash);
+ err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash);
+ } else {
+ dent_key_init(c, &key, dir->i_ino, &nm);
+ err = ubifs_tnc_lookup_nm(c, &key, dent, &nm);
+ }
+
+ if (err) {
+ if (err == -ENOENT)
+ dbg_gen("not found");
+ else
+ inode = ERR_PTR(err);
+ goto done;
+ }
+
+ if (dbg_check_name(c, dent, &nm)) {
+ inode = ERR_PTR(-EINVAL);
+ goto done;
+ }
+
+ inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum));
+ if (IS_ERR(inode)) {
+ /*
+ * This should not happen. Probably the file-system needs
+ * checking.
+ */
+ err = PTR_ERR(inode);
+ ubifs_err(c, "dead directory entry '%pd', error %d",
+ dentry, err);
+ ubifs_ro_mode(c, err);
+ goto done;
+ }
+
+ if (IS_ENCRYPTED(dir) &&
+ (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
+ !fscrypt_has_permitted_context(dir, inode)) {
+ ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu",
+ dir->i_ino, inode->i_ino);
+ iput(inode);
+ inode = ERR_PTR(-EPERM);
+ }
+
+done:
+ kfree(dent);
+ fscrypt_free_filename(&nm);
+ return d_splice_alias(inode, dentry);
+}
+
+static int ubifs_prepare_create(struct inode *dir, struct dentry *dentry,
+ struct fscrypt_name *nm)
+{
+ if (fscrypt_is_nokey_name(dentry))
+ return -ENOKEY;
+
+ return fscrypt_setup_filename(dir, &dentry->d_name, 0, nm);
+}
+
+static int ubifs_create(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, bool excl)
+{
+ struct inode *inode;
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1 };
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct fscrypt_name nm;
+ int err, sz_change;
+
+ /*
+ * Budget request settings: new inode, new direntry, changing the
+ * parent directory inode.
+ */
+
+ dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
+ dentry, mode, dir->i_ino);
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ err = ubifs_prepare_create(dir, dentry, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ inode = ubifs_new_inode(c, dir, mode, false);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_fname;
+ }
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err)
+ goto out_inode;
+
+ set_nlink(inode, 1);
+ mutex_lock(&dir_ui->ui_mutex);
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
+ if (err)
+ goto out_cancel;
+ mutex_unlock(&dir_ui->ui_mutex);
+
+ ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&nm);
+ insert_inode_hash(inode);
+ d_instantiate(dentry, inode);
+ return 0;
+
+out_cancel:
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ mutex_unlock(&dir_ui->ui_mutex);
+ set_nlink(inode, 0);
+out_inode:
+ iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
+out_budg:
+ ubifs_release_budget(c, &req);
+ ubifs_err(c, "cannot create regular file, error %d", err);
+ return err;
+}
+
+static struct inode *create_whiteout(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+ umode_t mode = S_IFCHR | WHITEOUT_MODE;
+ struct inode *inode;
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+
+ /*
+ * Create an inode('nlink = 1') for whiteout without updating journal,
+ * let ubifs_jnl_rename() store it on flash to complete rename whiteout
+ * atomically.
+ */
+
+ dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
+ dentry, mode, dir->i_ino);
+
+ inode = ubifs_new_inode(c, dir, mode, false);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_free;
+ }
+
+ init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
+ ubifs_assert(c, inode->i_op == &ubifs_file_inode_operations);
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err)
+ goto out_inode;
+
+ /* The dir size is updated by do_rename. */
+ insert_inode_hash(inode);
+
+ return inode;
+
+out_inode:
+ iput(inode);
+out_free:
+ ubifs_err(c, "cannot create whiteout file, error %d", err);
+ return ERR_PTR(err);
+}
+
+/**
+ * lock_2_inodes - a wrapper for locking two UBIFS inodes.
+ * @inode1: first inode
+ * @inode2: second inode
+ *
+ * We do not implement any tricks to guarantee strict lock ordering, because
+ * VFS has already done it for us on the @i_mutex. So this is just a simple
+ * wrapper function.
+ */
+static void lock_2_inodes(struct inode *inode1, struct inode *inode2)
+{
+ mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
+ mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
+}
+
+/**
+ * unlock_2_inodes - a wrapper for unlocking two UBIFS inodes.
+ * @inode1: first inode
+ * @inode2: second inode
+ */
+static void unlock_2_inodes(struct inode *inode1, struct inode *inode2)
+{
+ mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
+ mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
+}
+
+static int ubifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+ struct file *file, umode_t mode)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode *inode;
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1};
+ struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
+ struct ubifs_inode *ui;
+ int err, instantiated = 0;
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: new inode, new direntry, changing the
+ * parent directory inode.
+ * Allocate budget separately for new dirtied inode, the budget will
+ * be released via writeback.
+ */
+
+ dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
+ dentry, mode, dir->i_ino);
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ return err;
+
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ fscrypt_free_filename(&nm);
+ return err;
+ }
+
+ err = ubifs_budget_space(c, &ino_req);
+ if (err) {
+ ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&nm);
+ return err;
+ }
+
+ inode = ubifs_new_inode(c, dir, mode, false);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_budg;
+ }
+ ui = ubifs_inode(inode);
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err)
+ goto out_inode;
+
+ set_nlink(inode, 1);
+ mutex_lock(&ui->ui_mutex);
+ insert_inode_hash(inode);
+ d_tmpfile(file, inode);
+ ubifs_assert(c, ui->dirty);
+
+ instantiated = 1;
+ mutex_unlock(&ui->ui_mutex);
+
+ lock_2_inodes(dir, inode);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 1);
+ if (err)
+ goto out_cancel;
+ unlock_2_inodes(dir, inode);
+
+ ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&nm);
+
+ return finish_open_simple(file, 0);
+
+out_cancel:
+ unlock_2_inodes(dir, inode);
+out_inode:
+ if (!instantiated)
+ iput(inode);
+out_budg:
+ ubifs_release_budget(c, &req);
+ if (!instantiated)
+ ubifs_release_budget(c, &ino_req);
+ fscrypt_free_filename(&nm);
+ ubifs_err(c, "cannot create temporary file, error %d", err);
+ return err;
+}
+
+/**
+ * vfs_dent_type - get VFS directory entry type.
+ * @type: UBIFS directory entry type
+ *
+ * This function converts UBIFS directory entry type into VFS directory entry
+ * type.
+ */
+static unsigned int vfs_dent_type(uint8_t type)
+{
+ switch (type) {
+ case UBIFS_ITYPE_REG:
+ return DT_REG;
+ case UBIFS_ITYPE_DIR:
+ return DT_DIR;
+ case UBIFS_ITYPE_LNK:
+ return DT_LNK;
+ case UBIFS_ITYPE_BLK:
+ return DT_BLK;
+ case UBIFS_ITYPE_CHR:
+ return DT_CHR;
+ case UBIFS_ITYPE_FIFO:
+ return DT_FIFO;
+ case UBIFS_ITYPE_SOCK:
+ return DT_SOCK;
+ default:
+ BUG();
+ }
+ return 0;
+}
+
+/*
+ * The classical Unix view for directory is that it is a linear array of
+ * (name, inode number) entries. Linux/VFS assumes this model as well.
+ * Particularly, 'readdir()' call wants us to return a directory entry offset
+ * which later may be used to continue 'readdir()'ing the directory or to
+ * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this
+ * model because directory entries are identified by keys, which may collide.
+ *
+ * UBIFS uses directory entry hash value for directory offsets, so
+ * 'seekdir()'/'telldir()' may not always work because of possible key
+ * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work
+ * properly by means of saving full directory entry name in the private field
+ * of the file description object.
+ *
+ * This means that UBIFS cannot support NFS which requires full
+ * 'seekdir()'/'telldir()' support.
+ */
+static int ubifs_readdir(struct file *file, struct dir_context *ctx)
+{
+ int fstr_real_len = 0, err = 0;
+ struct fscrypt_name nm;
+ struct fscrypt_str fstr = {0};
+ union ubifs_key key;
+ struct ubifs_dent_node *dent;
+ struct inode *dir = file_inode(file);
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ bool encrypted = IS_ENCRYPTED(dir);
+
+ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos);
+
+ if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2)
+ /*
+ * The directory was seek'ed to a senseless position or there
+ * are no more entries.
+ */
+ return 0;
+
+ if (encrypted) {
+ err = fscrypt_prepare_readdir(dir);
+ if (err)
+ return err;
+
+ err = fscrypt_fname_alloc_buffer(UBIFS_MAX_NLEN, &fstr);
+ if (err)
+ return err;
+
+ fstr_real_len = fstr.len;
+ }
+
+ if (file->f_version == 0) {
+ /*
+ * The file was seek'ed, which means that @file->private_data
+ * is now invalid. This may also be just the first
+ * 'ubifs_readdir()' invocation, in which case
+ * @file->private_data is NULL, and the below code is
+ * basically a no-op.
+ */
+ kfree(file->private_data);
+ file->private_data = NULL;
+ }
+
+ /*
+ * 'generic_file_llseek()' unconditionally sets @file->f_version to
+ * zero, and we use this for detecting whether the file was seek'ed.
+ */
+ file->f_version = 1;
+
+ /* File positions 0 and 1 correspond to "." and ".." */
+ if (ctx->pos < 2) {
+ ubifs_assert(c, !file->private_data);
+ if (!dir_emit_dots(file, ctx)) {
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
+ return 0;
+ }
+
+ /* Find the first entry in TNC and save it */
+ lowest_dent_key(c, &key, dir->i_ino);
+ fname_len(&nm) = 0;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ ctx->pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ }
+
+ dent = file->private_data;
+ if (!dent) {
+ /*
+ * The directory was seek'ed to and is now readdir'ed.
+ * Find the entry corresponding to @ctx->pos or the closest one.
+ */
+ dent_key_init_hash(c, &key, dir->i_ino, ctx->pos);
+ fname_len(&nm) = 0;
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+ ctx->pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ }
+
+ while (1) {
+ dbg_gen("ino %llu, new f_pos %#x",
+ (unsigned long long)le64_to_cpu(dent->inum),
+ key_hash_flash(c, &dent->key));
+ ubifs_assert(c, le64_to_cpu(dent->ch.sqnum) >
+ ubifs_inode(dir)->creat_sqnum);
+
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
+ fname_name(&nm) = dent->name;
+
+ if (encrypted) {
+ fstr.len = fstr_real_len;
+
+ err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c,
+ &dent->key),
+ le32_to_cpu(dent->cookie),
+ &nm.disk_name, &fstr);
+ if (err)
+ goto out;
+ } else {
+ fstr.len = fname_len(&nm);
+ fstr.name = fname_name(&nm);
+ }
+
+ if (!dir_emit(ctx, fstr.name, fstr.len,
+ le64_to_cpu(dent->inum),
+ vfs_dent_type(dent->type))) {
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
+ return 0;
+ }
+
+ /* Switch to the next entry */
+ key_read(c, &dent->key, &key);
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ goto out;
+ }
+
+ kfree(file->private_data);
+ ctx->pos = key_hash_flash(c, &dent->key);
+ file->private_data = dent;
+ cond_resched();
+ }
+
+out:
+ kfree(file->private_data);
+ file->private_data = NULL;
+
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
+
+ if (err != -ENOENT)
+ ubifs_err(c, "cannot find next direntry, error %d", err);
+ else
+ /*
+ * -ENOENT is a non-fatal error in this context, the TNC uses
+ * it to indicate that the cursor moved past the current directory
+ * and readdir() has to stop.
+ */
+ err = 0;
+
+
+ /* 2 is a special value indicating that there are no more direntries */
+ ctx->pos = 2;
+ return err;
+}
+
+/* Free saved readdir() state when the directory is closed */
+static int ubifs_dir_release(struct inode *dir, struct file *file)
+{
+ kfree(file->private_data);
+ file->private_data = NULL;
+ return 0;
+}
+
+static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct inode *inode = d_inode(old_dentry);
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ int err, sz_change;
+ struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2,
+ .dirtied_ino_d = ALIGN(ui->data_len, 8) };
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: new direntry, changing the target inode,
+ * changing the parent inode.
+ */
+
+ dbg_gen("dent '%pd' to ino %lu (nlink %d) in dir ino %lu",
+ dentry, inode->i_ino,
+ inode->i_nlink, dir->i_ino);
+ ubifs_assert(c, inode_is_locked(dir));
+ ubifs_assert(c, inode_is_locked(inode));
+
+ err = fscrypt_prepare_link(old_dentry, dir, dentry);
+ if (err)
+ return err;
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ err = dbg_check_synced_i_size(c, inode);
+ if (err)
+ goto out_fname;
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ goto out_fname;
+
+ lock_2_inodes(dir, inode);
+
+ inc_nlink(inode);
+ ihold(inode);
+ inode_set_ctime_current(inode);
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, inode->i_nlink == 1);
+ if (err)
+ goto out_cancel;
+ unlock_2_inodes(dir, inode);
+
+ ubifs_release_budget(c, &req);
+ d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
+ return 0;
+
+out_cancel:
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ drop_nlink(inode);
+ unlock_2_inodes(dir, inode);
+ ubifs_release_budget(c, &req);
+ iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
+ return err;
+}
+
+static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct inode *inode = d_inode(dentry);
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ int err, sz_change, budgeted = 1;
+ struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+ unsigned int saved_nlink = inode->i_nlink;
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: deletion direntry, deletion inode (+1 for
+ * @dirtied_ino), changing the parent directory inode. If budgeting
+ * fails, go ahead anyway because we have extra space reserved for
+ * deletions.
+ */
+
+ dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu",
+ dentry, inode->i_ino,
+ inode->i_nlink, dir->i_ino);
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
+ if (err)
+ return err;
+
+ err = ubifs_purge_xattrs(inode);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ ubifs_assert(c, inode_is_locked(dir));
+ ubifs_assert(c, inode_is_locked(inode));
+ err = dbg_check_synced_i_size(c, inode);
+ if (err)
+ goto out_fname;
+
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ if (err != -ENOSPC)
+ goto out_fname;
+ budgeted = 0;
+ }
+
+ lock_2_inodes(dir, inode);
+ inode_set_ctime_current(inode);
+ drop_nlink(inode);
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
+ if (err)
+ goto out_cancel;
+ unlock_2_inodes(dir, inode);
+
+ if (budgeted)
+ ubifs_release_budget(c, &req);
+ else {
+ /* We've deleted something - clean the "no space" flags */
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+ }
+ fscrypt_free_filename(&nm);
+ return 0;
+
+out_cancel:
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ set_nlink(inode, saved_nlink);
+ unlock_2_inodes(dir, inode);
+ if (budgeted)
+ ubifs_release_budget(c, &req);
+out_fname:
+ fscrypt_free_filename(&nm);
+ return err;
+}
+
+/**
+ * ubifs_check_dir_empty - check if a directory is empty or not.
+ * @dir: VFS inode object of the directory to check
+ *
+ * This function checks if directory @dir is empty. Returns zero if the
+ * directory is empty, %-ENOTEMPTY if it is not, and other negative error codes
+ * in case of errors.
+ */
+int ubifs_check_dir_empty(struct inode *dir)
+{
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct fscrypt_name nm = { 0 };
+ struct ubifs_dent_node *dent;
+ union ubifs_key key;
+ int err;
+
+ lowest_dent_key(c, &key, dir->i_ino);
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ if (err == -ENOENT)
+ err = 0;
+ } else {
+ kfree(dent);
+ err = -ENOTEMPTY;
+ }
+ return err;
+}
+
+static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct inode *inode = d_inode(dentry);
+ int err, sz_change, budgeted = 1;
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: deletion direntry, deletion inode and
+ * changing the parent inode. If budgeting fails, go ahead anyway
+ * because we have extra space reserved for deletions.
+ */
+
+ dbg_gen("directory '%pd', ino %lu in dir ino %lu", dentry,
+ inode->i_ino, dir->i_ino);
+ ubifs_assert(c, inode_is_locked(dir));
+ ubifs_assert(c, inode_is_locked(inode));
+ err = ubifs_check_dir_empty(d_inode(dentry));
+ if (err)
+ return err;
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
+ if (err)
+ return err;
+
+ err = ubifs_purge_xattrs(inode);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ if (err != -ENOSPC)
+ goto out_fname;
+ budgeted = 0;
+ }
+
+ lock_2_inodes(dir, inode);
+ inode_set_ctime_current(inode);
+ clear_nlink(inode);
+ drop_nlink(dir);
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
+ if (err)
+ goto out_cancel;
+ unlock_2_inodes(dir, inode);
+
+ if (budgeted)
+ ubifs_release_budget(c, &req);
+ else {
+ /* We've deleted something - clean the "no space" flags */
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+ }
+ fscrypt_free_filename(&nm);
+ return 0;
+
+out_cancel:
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inc_nlink(dir);
+ set_nlink(inode, 2);
+ unlock_2_inodes(dir, inode);
+ if (budgeted)
+ ubifs_release_budget(c, &req);
+out_fname:
+ fscrypt_free_filename(&nm);
+ return err;
+}
+
+static int ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode)
+{
+ struct inode *inode;
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ int err, sz_change;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1};
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: new inode, new direntry and changing parent
+ * directory inode.
+ */
+
+ dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
+ dentry, mode, dir->i_ino);
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ err = ubifs_prepare_create(dir, dentry, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ inode = ubifs_new_inode(c, dir, S_IFDIR | mode, false);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_fname;
+ }
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err)
+ goto out_inode;
+
+ set_nlink(inode, 1);
+ mutex_lock(&dir_ui->ui_mutex);
+ insert_inode_hash(inode);
+ inc_nlink(inode);
+ inc_nlink(dir);
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
+ if (err) {
+ ubifs_err(c, "cannot create directory, error %d", err);
+ goto out_cancel;
+ }
+ mutex_unlock(&dir_ui->ui_mutex);
+
+ ubifs_release_budget(c, &req);
+ d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
+ return 0;
+
+out_cancel:
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ drop_nlink(dir);
+ mutex_unlock(&dir_ui->ui_mutex);
+ set_nlink(inode, 0);
+out_inode:
+ iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
+out_budg:
+ ubifs_release_budget(c, &req);
+ return err;
+}
+
+static int ubifs_mknod(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, dev_t rdev)
+{
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ union ubifs_dev_desc *dev = NULL;
+ int sz_change;
+ int err, devlen = 0;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1 };
+ struct fscrypt_name nm;
+
+ /*
+ * Budget request settings: new inode, new direntry and changing parent
+ * directory inode.
+ */
+
+ dbg_gen("dent '%pd' in dir ino %lu", dentry, dir->i_ino);
+
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+ dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+ if (!dev)
+ return -ENOMEM;
+ devlen = ubifs_encode_dev(dev, rdev);
+ }
+
+ req.new_ino_d = ALIGN(devlen, 8);
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ kfree(dev);
+ return err;
+ }
+
+ err = ubifs_prepare_create(dir, dentry, &nm);
+ if (err) {
+ kfree(dev);
+ goto out_budg;
+ }
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ inode = ubifs_new_inode(c, dir, mode, false);
+ if (IS_ERR(inode)) {
+ kfree(dev);
+ err = PTR_ERR(inode);
+ goto out_fname;
+ }
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err) {
+ kfree(dev);
+ goto out_inode;
+ }
+
+ init_special_inode(inode, inode->i_mode, rdev);
+ inode->i_size = ubifs_inode(inode)->ui_size = devlen;
+ ui = ubifs_inode(inode);
+ ui->data = dev;
+ ui->data_len = devlen;
+ set_nlink(inode, 1);
+
+ mutex_lock(&dir_ui->ui_mutex);
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
+ if (err)
+ goto out_cancel;
+ mutex_unlock(&dir_ui->ui_mutex);
+
+ ubifs_release_budget(c, &req);
+ insert_inode_hash(inode);
+ d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
+ return 0;
+
+out_cancel:
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ mutex_unlock(&dir_ui->ui_mutex);
+ set_nlink(inode, 0);
+out_inode:
+ iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
+out_budg:
+ ubifs_release_budget(c, &req);
+ return err;
+}
+
+static int ubifs_symlink(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, const char *symname)
+{
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
+ int err, sz_change, len = strlen(symname);
+ struct fscrypt_str disk_link;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1 };
+ struct fscrypt_name nm;
+
+ dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry,
+ symname, dir->i_ino);
+
+ err = fscrypt_prepare_symlink(dir, symname, len, UBIFS_MAX_INO_DATA,
+ &disk_link);
+ if (err)
+ return err;
+
+ /*
+ * Budget request settings: new inode, new direntry and changing parent
+ * directory inode.
+ */
+ req.new_ino_d = ALIGN(disk_link.len - 1, 8);
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ err = ubifs_prepare_create(dir, dentry, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
+ inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO, false);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_fname;
+ }
+
+ err = ubifs_init_security(dir, inode, &dentry->d_name);
+ if (err)
+ goto out_inode;
+
+ ui = ubifs_inode(inode);
+ ui->data = kmalloc(disk_link.len, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_inode;
+ }
+
+ if (IS_ENCRYPTED(inode)) {
+ disk_link.name = ui->data; /* encrypt directly into ui->data */
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
+ if (err)
+ goto out_inode;
+ } else {
+ memcpy(ui->data, disk_link.name, disk_link.len);
+ inode->i_link = ui->data;
+ }
+
+ /*
+ * The terminating zero byte is not written to the flash media and it
+ * is put just to make later in-memory string processing simpler. Thus,
+ * data length is @disk_link.len - 1, not @disk_link.len.
+ */
+ ui->data_len = disk_link.len - 1;
+ inode->i_size = ubifs_inode(inode)->ui_size = disk_link.len - 1;
+ set_nlink(inode, 1);
+
+ mutex_lock(&dir_ui->ui_mutex);
+ dir->i_size += sz_change;
+ dir_ui->ui_size = dir->i_size;
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1);
+ if (err)
+ goto out_cancel;
+ mutex_unlock(&dir_ui->ui_mutex);
+
+ insert_inode_hash(inode);
+ d_instantiate(dentry, inode);
+ err = 0;
+ goto out_fname;
+
+out_cancel:
+ dir->i_size -= sz_change;
+ dir_ui->ui_size = dir->i_size;
+ mutex_unlock(&dir_ui->ui_mutex);
+ set_nlink(inode, 0);
+out_inode:
+ /* Free inode->i_link before inode is marked as bad. */
+ fscrypt_free_inode(inode);
+ iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
+out_budg:
+ ubifs_release_budget(c, &req);
+ return err;
+}
+
+/**
+ * lock_4_inodes - a wrapper for locking three UBIFS inodes.
+ * @inode1: first inode
+ * @inode2: second inode
+ * @inode3: third inode
+ * @inode4: fourth inode
+ *
+ * This function is used for 'ubifs_rename()' and @inode1 may be the same as
+ * @inode2 whereas @inode3 and @inode4 may be %NULL.
+ *
+ * We do not implement any tricks to guarantee strict lock ordering, because
+ * VFS has already done it for us on the @i_mutex. So this is just a simple
+ * wrapper function.
+ */
+static void lock_4_inodes(struct inode *inode1, struct inode *inode2,
+ struct inode *inode3, struct inode *inode4)
+{
+ mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
+ if (inode2 != inode1)
+ mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
+ if (inode3)
+ mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3);
+ if (inode4)
+ mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4);
+}
+
+/**
+ * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename.
+ * @inode1: first inode
+ * @inode2: second inode
+ * @inode3: third inode
+ * @inode4: fourth inode
+ */
+static void unlock_4_inodes(struct inode *inode1, struct inode *inode2,
+ struct inode *inode3, struct inode *inode4)
+{
+ if (inode4)
+ mutex_unlock(&ubifs_inode(inode4)->ui_mutex);
+ if (inode3)
+ mutex_unlock(&ubifs_inode(inode3)->ui_mutex);
+ if (inode1 != inode2)
+ mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
+ mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
+}
+
+static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
+{
+ struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+ struct inode *old_inode = d_inode(old_dentry);
+ struct inode *new_inode = d_inode(new_dentry);
+ struct inode *whiteout = NULL;
+ struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
+ struct ubifs_inode *whiteout_ui = NULL;
+ int err, release, sync = 0, move = (new_dir != old_dir);
+ int is_dir = S_ISDIR(old_inode->i_mode);
+ int unlink = !!new_inode, new_sz, old_sz;
+ struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
+ .dirtied_ino = 3 };
+ struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
+ .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
+ struct ubifs_budget_req wht_req;
+ unsigned int saved_nlink;
+ struct fscrypt_name old_nm, new_nm;
+
+ /*
+ * Budget request settings:
+ * req: deletion direntry, new direntry, removing the old inode,
+ * and changing old and new parent directory inodes.
+ *
+ * wht_req: new whiteout inode for RENAME_WHITEOUT.
+ *
+ * ino_req: marks the target inode as dirty and does not write it.
+ */
+
+ dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x",
+ old_dentry, old_inode->i_ino, old_dir->i_ino,
+ new_dentry, new_dir->i_ino, flags);
+
+ if (unlink) {
+ ubifs_assert(c, inode_is_locked(new_inode));
+
+ /* Budget for old inode's data when its nlink > 1. */
+ req.dirtied_ino_d = ALIGN(ubifs_inode(new_inode)->data_len, 8);
+ err = ubifs_purge_xattrs(new_inode);
+ if (err)
+ return err;
+ }
+
+ if (unlink && is_dir) {
+ err = ubifs_check_dir_empty(new_inode);
+ if (err)
+ return err;
+ }
+
+ err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_nm);
+ if (err)
+ return err;
+
+ err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_nm);
+ if (err) {
+ fscrypt_free_filename(&old_nm);
+ return err;
+ }
+
+ new_sz = CALC_DENT_SIZE(fname_len(&new_nm));
+ old_sz = CALC_DENT_SIZE(fname_len(&old_nm));
+
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
+ return err;
+ }
+ err = ubifs_budget_space(c, &ino_req);
+ if (err) {
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
+ ubifs_release_budget(c, &req);
+ return err;
+ }
+
+ if (flags & RENAME_WHITEOUT) {
+ union ubifs_dev_desc *dev = NULL;
+
+ dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+ if (!dev) {
+ err = -ENOMEM;
+ goto out_release;
+ }
+
+ /*
+ * The whiteout inode without dentry is pinned in memory,
+ * umount won't happen during rename process because we
+ * got parent dentry.
+ */
+ whiteout = create_whiteout(old_dir, old_dentry);
+ if (IS_ERR(whiteout)) {
+ err = PTR_ERR(whiteout);
+ kfree(dev);
+ goto out_release;
+ }
+
+ whiteout_ui = ubifs_inode(whiteout);
+ whiteout_ui->data = dev;
+ whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0));
+ ubifs_assert(c, !whiteout_ui->dirty);
+
+ memset(&wht_req, 0, sizeof(struct ubifs_budget_req));
+ wht_req.new_ino = 1;
+ wht_req.new_ino_d = ALIGN(whiteout_ui->data_len, 8);
+ /*
+ * To avoid deadlock between space budget (holds ui_mutex and
+ * waits wb work) and writeback work(waits ui_mutex), do space
+ * budget before ubifs inodes locked.
+ */
+ err = ubifs_budget_space(c, &wht_req);
+ if (err) {
+ iput(whiteout);
+ goto out_release;
+ }
+ set_nlink(whiteout, 1);
+
+ /* Add the old_dentry size to the old_dir size. */
+ old_sz -= CALC_DENT_SIZE(fname_len(&old_nm));
+ }
+
+ lock_4_inodes(old_dir, new_dir, new_inode, whiteout);
+
+ /*
+ * Like most other Unix systems, set the @i_ctime for inodes on a
+ * rename.
+ */
+ simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
+
+ /* We must adjust parent link count when renaming directories */
+ if (is_dir) {
+ if (move) {
+ /*
+ * @old_dir loses a link because we are moving
+ * @old_inode to a different directory.
+ */
+ drop_nlink(old_dir);
+ /*
+ * @new_dir only gains a link if we are not also
+ * overwriting an existing directory.
+ */
+ if (!unlink)
+ inc_nlink(new_dir);
+ } else {
+ /*
+ * @old_inode is not moving to a different directory,
+ * but @old_dir still loses a link if we are
+ * overwriting an existing directory.
+ */
+ if (unlink)
+ drop_nlink(old_dir);
+ }
+ }
+
+ old_dir->i_size -= old_sz;
+ ubifs_inode(old_dir)->ui_size = old_dir->i_size;
+
+ /*
+ * And finally, if we unlinked a direntry which happened to have the
+ * same name as the moved direntry, we have to decrement @i_nlink of
+ * the unlinked inode.
+ */
+ if (unlink) {
+ /*
+ * Directories cannot have hard-links, so if this is a
+ * directory, just clear @i_nlink.
+ */
+ saved_nlink = new_inode->i_nlink;
+ if (is_dir)
+ clear_nlink(new_inode);
+ else
+ drop_nlink(new_inode);
+ } else {
+ new_dir->i_size += new_sz;
+ ubifs_inode(new_dir)->ui_size = new_dir->i_size;
+ }
+
+ /*
+ * Do not ask 'ubifs_jnl_rename()' to flush write-buffer if @old_inode
+ * is dirty, because this will be done later on at the end of
+ * 'ubifs_rename()'.
+ */
+ if (IS_SYNC(old_inode)) {
+ sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
+ if (unlink && IS_SYNC(new_inode))
+ sync = 1;
+ /*
+ * S_SYNC flag of whiteout inherits from the old_dir, and we
+ * have already checked the old dir inode. So there is no need
+ * to check whiteout.
+ */
+ }
+
+ err = ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir,
+ new_inode, &new_nm, whiteout, sync, !!whiteout);
+ if (err)
+ goto out_cancel;
+
+ unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
+ ubifs_release_budget(c, &req);
+
+ if (whiteout) {
+ ubifs_release_budget(c, &wht_req);
+ iput(whiteout);
+ }
+
+ mutex_lock(&old_inode_ui->ui_mutex);
+ release = old_inode_ui->dirty;
+ mark_inode_dirty_sync(old_inode);
+ mutex_unlock(&old_inode_ui->ui_mutex);
+
+ if (release)
+ ubifs_release_budget(c, &ino_req);
+ if (IS_SYNC(old_inode))
+ /*
+ * Rename finished here. Although old inode cannot be updated
+ * on flash, old ctime is not a big problem, don't return err
+ * code to userspace.
+ */
+ old_inode->i_sb->s_op->write_inode(old_inode, NULL);
+
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
+ return 0;
+
+out_cancel:
+ if (unlink) {
+ set_nlink(new_inode, saved_nlink);
+ } else {
+ new_dir->i_size -= new_sz;
+ ubifs_inode(new_dir)->ui_size = new_dir->i_size;
+ }
+ old_dir->i_size += old_sz;
+ ubifs_inode(old_dir)->ui_size = old_dir->i_size;
+ if (is_dir) {
+ if (move) {
+ inc_nlink(old_dir);
+ if (!unlink)
+ drop_nlink(new_dir);
+ } else {
+ if (unlink)
+ inc_nlink(old_dir);
+ }
+ }
+ unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
+ if (whiteout) {
+ ubifs_release_budget(c, &wht_req);
+ set_nlink(whiteout, 0);
+ iput(whiteout);
+ }
+out_release:
+ ubifs_release_budget(c, &ino_req);
+ ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
+ return err;
+}
+
+static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+ struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
+ .dirtied_ino = 2 };
+ int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
+ struct inode *fst_inode = d_inode(old_dentry);
+ struct inode *snd_inode = d_inode(new_dentry);
+ int err;
+ struct fscrypt_name fst_nm, snd_nm;
+
+ ubifs_assert(c, fst_inode && snd_inode);
+
+ /*
+ * Budget request settings: changing two direntries, changing the two
+ * parent directory inodes.
+ */
+
+ dbg_gen("dent '%pd' ino %lu in dir ino %lu exchange dent '%pd' ino %lu in dir ino %lu",
+ old_dentry, fst_inode->i_ino, old_dir->i_ino,
+ new_dentry, snd_inode->i_ino, new_dir->i_ino);
+
+ err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &fst_nm);
+ if (err)
+ return err;
+
+ err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &snd_nm);
+ if (err) {
+ fscrypt_free_filename(&fst_nm);
+ return err;
+ }
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ goto out;
+
+ lock_4_inodes(old_dir, new_dir, NULL, NULL);
+
+ simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
+
+ if (old_dir != new_dir) {
+ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
+ inc_nlink(new_dir);
+ drop_nlink(old_dir);
+ }
+ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
+ drop_nlink(new_dir);
+ inc_nlink(old_dir);
+ }
+ }
+
+ err = ubifs_jnl_xrename(c, old_dir, fst_inode, &fst_nm, new_dir,
+ snd_inode, &snd_nm, sync);
+
+ unlock_4_inodes(old_dir, new_dir, NULL, NULL);
+ ubifs_release_budget(c, &req);
+
+out:
+ fscrypt_free_filename(&fst_nm);
+ fscrypt_free_filename(&snd_nm);
+ return err;
+}
+
+static int ubifs_rename(struct mnt_idmap *idmap,
+ struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
+{
+ int err;
+ struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+
+ if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE))
+ return -EINVAL;
+
+ ubifs_assert(c, inode_is_locked(old_dir));
+ ubifs_assert(c, inode_is_locked(new_dir));
+
+ err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
+ flags);
+ if (err)
+ return err;
+
+ if (flags & RENAME_EXCHANGE)
+ return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry);
+
+ return do_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
+}
+
+int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,
+ struct kstat *stat, u32 request_mask, unsigned int flags)
+{
+ loff_t size;
+ struct inode *inode = d_inode(path->dentry);
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ mutex_lock(&ui->ui_mutex);
+
+ if (ui->flags & UBIFS_APPEND_FL)
+ stat->attributes |= STATX_ATTR_APPEND;
+ if (ui->flags & UBIFS_COMPR_FL)
+ stat->attributes |= STATX_ATTR_COMPRESSED;
+ if (ui->flags & UBIFS_CRYPT_FL)
+ stat->attributes |= STATX_ATTR_ENCRYPTED;
+ if (ui->flags & UBIFS_IMMUTABLE_FL)
+ stat->attributes |= STATX_ATTR_IMMUTABLE;
+
+ stat->attributes_mask |= (STATX_ATTR_APPEND |
+ STATX_ATTR_COMPRESSED |
+ STATX_ATTR_ENCRYPTED |
+ STATX_ATTR_IMMUTABLE);
+
+ generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
+ stat->blksize = UBIFS_BLOCK_SIZE;
+ stat->size = ui->ui_size;
+
+ /*
+ * Unfortunately, the 'stat()' system call was designed for block
+ * device based file systems, and it is not appropriate for UBIFS,
+ * because UBIFS does not have notion of "block". For example, it is
+ * difficult to tell how many block a directory takes - it actually
+ * takes less than 300 bytes, but we have to round it to block size,
+ * which introduces large mistake. This makes utilities like 'du' to
+ * report completely senseless numbers. This is the reason why UBIFS
+ * goes the same way as JFFS2 - it reports zero blocks for everything
+ * but regular files, which makes more sense than reporting completely
+ * wrong sizes.
+ */
+ if (S_ISREG(inode->i_mode)) {
+ size = ui->xattr_size;
+ size += stat->size;
+ size = ALIGN(size, UBIFS_BLOCK_SIZE);
+ /*
+ * Note, user-space expects 512-byte blocks count irrespectively
+ * of what was reported in @stat->size.
+ */
+ stat->blocks = size >> 9;
+ } else
+ stat->blocks = 0;
+ mutex_unlock(&ui->ui_mutex);
+ return 0;
+}
+
+const struct inode_operations ubifs_dir_inode_operations = {
+ .lookup = ubifs_lookup,
+ .create = ubifs_create,
+ .link = ubifs_link,
+ .symlink = ubifs_symlink,
+ .unlink = ubifs_unlink,
+ .mkdir = ubifs_mkdir,
+ .rmdir = ubifs_rmdir,
+ .mknod = ubifs_mknod,
+ .rename = ubifs_rename,
+ .setattr = ubifs_setattr,
+ .getattr = ubifs_getattr,
+ .listxattr = ubifs_listxattr,
+ .update_time = ubifs_update_time,
+ .tmpfile = ubifs_tmpfile,
+ .fileattr_get = ubifs_fileattr_get,
+ .fileattr_set = ubifs_fileattr_set,
+};
+
+const struct file_operations ubifs_dir_operations = {
+ .llseek = generic_file_llseek,
+ .release = ubifs_dir_release,
+ .read = generic_read_dir,
+ .iterate_shared = ubifs_readdir,
+ .fsync = ubifs_fsync,
+ .unlocked_ioctl = ubifs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubifs_compat_ioctl,
+#endif
+};
diff --git a/ubifs-utils/libubifs/find.c b/ubifs-utils/libubifs/find.c
new file mode 100644
index 00000000..873e6e1c
--- /dev/null
+++ b/ubifs-utils/libubifs/find.c
@@ -0,0 +1,963 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file contains functions for finding LEBs for various purposes e.g.
+ * garbage collection. In general, lprops category heaps and lists are used
+ * for fast access, falling back on scanning the LPT as a last resort.
+ */
+
+#include <linux/sort.h>
+#include "ubifs.h"
+
+/**
+ * struct scan_data - data provided to scan callback functions
+ * @min_space: minimum number of bytes for which to scan
+ * @pick_free: whether it is OK to scan for empty LEBs
+ * @lnum: LEB number found is returned here
+ * @exclude_index: whether to exclude index LEBs
+ */
+struct scan_data {
+ int min_space;
+ int pick_free;
+ int lnum;
+ int exclude_index;
+};
+
+/**
+ * valuable - determine whether LEB properties are valuable.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties
+ *
+ * This function return %1 if the LEB properties should be added to the LEB
+ * properties tree in memory. Otherwise %0 is returned.
+ */
+static int valuable(struct ubifs_info *c, const struct ubifs_lprops *lprops)
+{
+ int n, cat = lprops->flags & LPROPS_CAT_MASK;
+ struct ubifs_lpt_heap *heap;
+
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ heap = &c->lpt_heap[cat - 1];
+ if (heap->cnt < heap->max_cnt)
+ return 1;
+ if (lprops->free + lprops->dirty >= c->dark_wm)
+ return 1;
+ return 0;
+ case LPROPS_EMPTY:
+ n = c->lst.empty_lebs + c->freeable_cnt -
+ c->lst.taken_empty_lebs;
+ if (n < c->lsave_cnt)
+ return 1;
+ return 0;
+ case LPROPS_FREEABLE:
+ return 1;
+ case LPROPS_FRDI_IDX:
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * scan_for_dirty_cb - dirty space scan callback.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_dirty_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops, int in_tree,
+ struct scan_data *data)
+{
+ int ret = LPT_SCAN_CONTINUE;
+
+ /* Exclude LEBs that are currently in use */
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPT_SCAN_CONTINUE;
+ /* Determine whether to add these LEB properties to the tree */
+ if (!in_tree && valuable(c, lprops))
+ ret |= LPT_SCAN_ADD;
+ /* Exclude LEBs with too little space */
+ if (lprops->free + lprops->dirty < data->min_space)
+ return ret;
+ /* If specified, exclude index LEBs */
+ if (data->exclude_index && lprops->flags & LPROPS_INDEX)
+ return ret;
+ /* If specified, exclude empty or freeable LEBs */
+ if (lprops->free + lprops->dirty == c->leb_size) {
+ if (!data->pick_free)
+ return ret;
+ /* Exclude LEBs with too little dirty space (unless it is empty) */
+ } else if (lprops->dirty < c->dead_wm)
+ return ret;
+ /* Finally we found space */
+ data->lnum = lprops->lnum;
+ return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * scan_for_dirty - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount free plus dirty space the returned LEB has to
+ * have
+ * @pick_free: if it is OK to return a free or freeable LEB
+ * @exclude_index: whether to exclude index LEBs
+ *
+ * This function returns a pointer to the LEB properties found or a negative
+ * error code.
+ */
+static const struct ubifs_lprops *scan_for_dirty(struct ubifs_info *c,
+ int min_space, int pick_free,
+ int exclude_index)
+{
+ const struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ struct scan_data data;
+ int err, i;
+
+ /* There may be an LEB with enough dirty space on the free heap */
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ lprops = heap->arr[i];
+ if (lprops->free + lprops->dirty < min_space)
+ continue;
+ if (lprops->dirty < c->dead_wm)
+ continue;
+ return lprops;
+ }
+ /*
+ * A LEB may have fallen off of the bottom of the dirty heap, and ended
+ * up as uncategorized even though it has enough dirty space for us now,
+ * so check the uncategorized list. N.B. neither empty nor freeable LEBs
+ * can end up as uncategorized because they are kept on lists not
+ * finite-sized heaps.
+ */
+ list_for_each_entry(lprops, &c->uncat_list, list) {
+ if (lprops->flags & LPROPS_TAKEN)
+ continue;
+ if (lprops->free + lprops->dirty < min_space)
+ continue;
+ if (exclude_index && (lprops->flags & LPROPS_INDEX))
+ continue;
+ if (lprops->dirty < c->dead_wm)
+ continue;
+ return lprops;
+ }
+ /* We have looked everywhere in main memory, now scan the flash */
+ if (c->pnodes_have >= c->pnode_cnt)
+ /* All pnodes are in memory, so skip scan */
+ return ERR_PTR(-ENOSPC);
+ data.min_space = min_space;
+ data.pick_free = pick_free;
+ data.lnum = -1;
+ data.exclude_index = exclude_index;
+ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+ (ubifs_lpt_scan_callback)scan_for_dirty_cb,
+ &data);
+ if (err)
+ return ERR_PTR(err);
+ ubifs_assert(c, data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+ c->lscan_lnum = data.lnum;
+ lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+ if (IS_ERR(lprops))
+ return lprops;
+ ubifs_assert(c, lprops->lnum == data.lnum);
+ ubifs_assert(c, lprops->free + lprops->dirty >= min_space);
+ ubifs_assert(c, lprops->dirty >= c->dead_wm ||
+ (pick_free &&
+ lprops->free + lprops->dirty == c->leb_size));
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !exclude_index || !(lprops->flags & LPROPS_INDEX));
+ return lprops;
+}
+
+/**
+ * ubifs_find_dirty_leb - find a dirty LEB for the Garbage Collector.
+ * @c: the UBIFS file-system description object
+ * @ret_lp: LEB properties are returned here on exit
+ * @min_space: minimum amount free plus dirty space the returned LEB has to
+ * have
+ * @pick_free: controls whether it is OK to pick empty or index LEBs
+ *
+ * This function tries to find a dirty logical eraseblock which has at least
+ * @min_space free and dirty space. It prefers to take an LEB from the dirty or
+ * dirty index heap, and it falls-back to LPT scanning if the heaps are empty
+ * or do not have an LEB which satisfies the @min_space criteria.
+ *
+ * Note, LEBs which have less than dead watermark of free + dirty space are
+ * never picked by this function.
+ *
+ * The additional @pick_free argument controls if this function has to return a
+ * free or freeable LEB if one is present. For example, GC must to set it to %1,
+ * when called from the journal space reservation function, because the
+ * appearance of free space may coincide with the loss of enough dirty space
+ * for GC to succeed anyway.
+ *
+ * In contrast, if the Garbage Collector is called from budgeting, it should
+ * just make free space, not return LEBs which are already free or freeable.
+ *
+ * In addition @pick_free is set to %2 by the recovery process in order to
+ * recover gc_lnum in which case an index LEB must not be returned.
+ *
+ * This function returns zero and the LEB properties of found dirty LEB in case
+ * of success, %-ENOSPC if no dirty LEB was found and a negative error code in
+ * case of other failures. The returned LEB is marked as "taken".
+ */
+int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
+ int min_space, int pick_free)
+{
+ int err = 0, sum, exclude_index = pick_free == 2 ? 1 : 0;
+ const struct ubifs_lprops *lp = NULL, *idx_lp = NULL;
+ struct ubifs_lpt_heap *heap, *idx_heap;
+
+ ubifs_get_lprops(c);
+
+ if (pick_free) {
+ int lebs, rsvd_idx_lebs = 0;
+
+ spin_lock(&c->space_lock);
+ lebs = c->lst.empty_lebs + c->idx_gc_cnt;
+ lebs += c->freeable_cnt - c->lst.taken_empty_lebs;
+
+ /*
+ * Note, the index may consume more LEBs than have been reserved
+ * for it. It is OK because it might be consolidated by GC.
+ * But if the index takes fewer LEBs than it is reserved for it,
+ * this function must avoid picking those reserved LEBs.
+ */
+ if (c->bi.min_idx_lebs >= c->lst.idx_lebs) {
+ rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
+ exclude_index = 1;
+ }
+ spin_unlock(&c->space_lock);
+
+ /* Check if there are enough free LEBs for the index */
+ if (rsvd_idx_lebs < lebs) {
+ /* OK, try to find an empty LEB */
+ lp = ubifs_fast_find_empty(c);
+ if (lp)
+ goto found;
+
+ /* Or a freeable LEB */
+ lp = ubifs_fast_find_freeable(c);
+ if (lp)
+ goto found;
+ } else
+ /*
+ * We cannot pick free/freeable LEBs in the below code.
+ */
+ pick_free = 0;
+ } else {
+ spin_lock(&c->space_lock);
+ exclude_index = (c->bi.min_idx_lebs >= c->lst.idx_lebs);
+ spin_unlock(&c->space_lock);
+ }
+
+ /* Look on the dirty and dirty index heaps */
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ idx_heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+
+ if (idx_heap->cnt && !exclude_index) {
+ idx_lp = idx_heap->arr[0];
+ sum = idx_lp->free + idx_lp->dirty;
+ /*
+ * Since we reserve thrice as much space for the index than it
+ * actually takes, it does not make sense to pick indexing LEBs
+ * with less than, say, half LEB of dirty space. May be half is
+ * not the optimal boundary - this should be tested and
+ * checked. This boundary should determine how much we use
+ * in-the-gaps to consolidate the index comparing to how much
+ * we use garbage collector to consolidate it. The "half"
+ * criteria just feels to be fine.
+ */
+ if (sum < min_space || sum < c->half_leb_size)
+ idx_lp = NULL;
+ }
+
+ if (heap->cnt) {
+ lp = heap->arr[0];
+ if (lp->dirty + lp->free < min_space)
+ lp = NULL;
+ }
+
+ /* Pick the LEB with most space */
+ if (idx_lp && lp) {
+ if (idx_lp->free + idx_lp->dirty >= lp->free + lp->dirty)
+ lp = idx_lp;
+ } else if (idx_lp && !lp)
+ lp = idx_lp;
+
+ if (lp) {
+ ubifs_assert(c, lp->free + lp->dirty >= c->dead_wm);
+ goto found;
+ }
+
+ /* Did not find a dirty LEB on the dirty heaps, have to scan */
+ dbg_find("scanning LPT for a dirty LEB");
+ lp = scan_for_dirty(c, min_space, pick_free, exclude_index);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ ubifs_assert(c, lp->dirty >= c->dead_wm ||
+ (pick_free && lp->free + lp->dirty == c->leb_size));
+
+found:
+ dbg_find("found LEB %d, free %d, dirty %d, flags %#x",
+ lp->lnum, lp->free, lp->dirty, lp->flags);
+
+ lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ memcpy(ret_lp, lp, sizeof(struct ubifs_lprops));
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * scan_for_free_cb - free space scan callback.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_free_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops, int in_tree,
+ struct scan_data *data)
+{
+ int ret = LPT_SCAN_CONTINUE;
+
+ /* Exclude LEBs that are currently in use */
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPT_SCAN_CONTINUE;
+ /* Determine whether to add these LEB properties to the tree */
+ if (!in_tree && valuable(c, lprops))
+ ret |= LPT_SCAN_ADD;
+ /* Exclude index LEBs */
+ if (lprops->flags & LPROPS_INDEX)
+ return ret;
+ /* Exclude LEBs with too little space */
+ if (lprops->free < data->min_space)
+ return ret;
+ /* If specified, exclude empty LEBs */
+ if (!data->pick_free && lprops->free == c->leb_size)
+ return ret;
+ /*
+ * LEBs that have only free and dirty space must not be allocated
+ * because they may have been unmapped already or they may have data
+ * that is obsolete only because of nodes that are still sitting in a
+ * wbuf.
+ */
+ if (lprops->free + lprops->dirty == c->leb_size && lprops->dirty > 0)
+ return ret;
+ /* Finally we found space */
+ data->lnum = lprops->lnum;
+ return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * do_find_free_space - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount of free space required
+ * @pick_free: whether it is OK to scan for empty LEBs
+ * @squeeze: whether to try to find space in a non-empty LEB first
+ *
+ * This function returns a pointer to the LEB properties found or a negative
+ * error code.
+ */
+static
+const struct ubifs_lprops *do_find_free_space(struct ubifs_info *c,
+ int min_space, int pick_free,
+ int squeeze)
+{
+ const struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ struct scan_data data;
+ int err, i;
+
+ if (squeeze) {
+ lprops = ubifs_fast_find_free(c);
+ if (lprops && lprops->free >= min_space)
+ return lprops;
+ }
+ if (pick_free) {
+ lprops = ubifs_fast_find_empty(c);
+ if (lprops)
+ return lprops;
+ }
+ if (!squeeze) {
+ lprops = ubifs_fast_find_free(c);
+ if (lprops && lprops->free >= min_space)
+ return lprops;
+ }
+ /* There may be an LEB with enough free space on the dirty heap */
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ lprops = heap->arr[i];
+ if (lprops->free >= min_space)
+ return lprops;
+ }
+ /*
+ * A LEB may have fallen off of the bottom of the free heap, and ended
+ * up as uncategorized even though it has enough free space for us now,
+ * so check the uncategorized list. N.B. neither empty nor freeable LEBs
+ * can end up as uncategorized because they are kept on lists not
+ * finite-sized heaps.
+ */
+ list_for_each_entry(lprops, &c->uncat_list, list) {
+ if (lprops->flags & LPROPS_TAKEN)
+ continue;
+ if (lprops->flags & LPROPS_INDEX)
+ continue;
+ if (lprops->free >= min_space)
+ return lprops;
+ }
+ /* We have looked everywhere in main memory, now scan the flash */
+ if (c->pnodes_have >= c->pnode_cnt)
+ /* All pnodes are in memory, so skip scan */
+ return ERR_PTR(-ENOSPC);
+ data.min_space = min_space;
+ data.pick_free = pick_free;
+ data.lnum = -1;
+ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+ (ubifs_lpt_scan_callback)scan_for_free_cb,
+ &data);
+ if (err)
+ return ERR_PTR(err);
+ ubifs_assert(c, data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+ c->lscan_lnum = data.lnum;
+ lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+ if (IS_ERR(lprops))
+ return lprops;
+ ubifs_assert(c, lprops->lnum == data.lnum);
+ ubifs_assert(c, lprops->free >= min_space);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ return lprops;
+}
+
+/**
+ * ubifs_find_free_space - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount of required free space
+ * @offs: contains offset of where free space starts on exit
+ * @squeeze: whether to try to find space in a non-empty LEB first
+ *
+ * This function looks for an LEB with at least @min_space bytes of free space.
+ * It tries to find an empty LEB if possible. If no empty LEBs are available,
+ * this function searches for a non-empty data LEB. The returned LEB is marked
+ * as "taken".
+ *
+ * This function returns found LEB number in case of success, %-ENOSPC if it
+ * failed to find a LEB with @min_space bytes of free space and other a negative
+ * error codes in case of failure.
+ */
+int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *offs,
+ int squeeze)
+{
+ const struct ubifs_lprops *lprops;
+ int lebs, rsvd_idx_lebs, pick_free = 0, err, lnum, flags;
+
+ dbg_find("min_space %d", min_space);
+ ubifs_get_lprops(c);
+
+ /* Check if there are enough empty LEBs for commit */
+ spin_lock(&c->space_lock);
+ if (c->bi.min_idx_lebs > c->lst.idx_lebs)
+ rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs;
+ else
+ rsvd_idx_lebs = 0;
+ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+ c->lst.taken_empty_lebs;
+ if (rsvd_idx_lebs < lebs)
+ /*
+ * OK to allocate an empty LEB, but we still don't want to go
+ * looking for one if there aren't any.
+ */
+ if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) {
+ pick_free = 1;
+ /*
+ * Because we release the space lock, we must account
+ * for this allocation here. After the LEB properties
+ * flags have been updated, we subtract one. Note, the
+ * result of this is that lprops also decreases
+ * @taken_empty_lebs in 'ubifs_change_lp()', so it is
+ * off by one for a short period of time which may
+ * introduce a small disturbance to budgeting
+ * calculations, but this is harmless because at the
+ * worst case this would make the budgeting subsystem
+ * be more pessimistic than needed.
+ *
+ * Fundamentally, this is about serialization of the
+ * budgeting and lprops subsystems. We could make the
+ * @space_lock a mutex and avoid dropping it before
+ * calling 'ubifs_change_lp()', but mutex is more
+ * heavy-weight, and we want budgeting to be as fast as
+ * possible.
+ */
+ c->lst.taken_empty_lebs += 1;
+ }
+ spin_unlock(&c->space_lock);
+
+ lprops = do_find_free_space(c, min_space, pick_free, squeeze);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+
+ lnum = lprops->lnum;
+ flags = lprops->flags | LPROPS_TAKEN;
+
+ lprops = ubifs_change_lp(c, lprops, LPROPS_NC, LPROPS_NC, flags, 0);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+
+ if (pick_free) {
+ spin_lock(&c->space_lock);
+ c->lst.taken_empty_lebs -= 1;
+ spin_unlock(&c->space_lock);
+ }
+
+ *offs = c->leb_size - lprops->free;
+ ubifs_release_lprops(c);
+
+ if (*offs == 0) {
+ /*
+ * Ensure that empty LEBs have been unmapped. They may not have
+ * been, for example, because of an unclean unmount. Also
+ * LEBs that were freeable LEBs (free + dirty == leb_size) will
+ * not have been unmapped.
+ */
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+
+ dbg_find("found LEB %d, free %d", lnum, c->leb_size - *offs);
+ ubifs_assert(c, *offs <= c->leb_size - min_space);
+ return lnum;
+
+out:
+ if (pick_free) {
+ spin_lock(&c->space_lock);
+ c->lst.taken_empty_lebs -= 1;
+ spin_unlock(&c->space_lock);
+ }
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * scan_for_idx_cb - callback used by the scan for a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_idx_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops, int in_tree,
+ struct scan_data *data)
+{
+ int ret = LPT_SCAN_CONTINUE;
+
+ /* Exclude LEBs that are currently in use */
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPT_SCAN_CONTINUE;
+ /* Determine whether to add these LEB properties to the tree */
+ if (!in_tree && valuable(c, lprops))
+ ret |= LPT_SCAN_ADD;
+ /* Exclude index LEBS */
+ if (lprops->flags & LPROPS_INDEX)
+ return ret;
+ /* Exclude LEBs that cannot be made empty */
+ if (lprops->free + lprops->dirty != c->leb_size)
+ return ret;
+ /*
+ * We are allocating for the index so it is safe to allocate LEBs with
+ * only free and dirty space, because write buffers are sync'd at commit
+ * start.
+ */
+ data->lnum = lprops->lnum;
+ return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * scan_for_leb_for_idx - scan for a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ */
+static const struct ubifs_lprops *scan_for_leb_for_idx(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lprops;
+ struct scan_data data;
+ int err;
+
+ data.lnum = -1;
+ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+ (ubifs_lpt_scan_callback)scan_for_idx_cb,
+ &data);
+ if (err)
+ return ERR_PTR(err);
+ ubifs_assert(c, data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+ c->lscan_lnum = data.lnum;
+ lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+ if (IS_ERR(lprops))
+ return lprops;
+ ubifs_assert(c, lprops->lnum == data.lnum);
+ ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ return lprops;
+}
+
+/**
+ * ubifs_find_free_leb_for_idx - find a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ *
+ * This function looks for a free LEB and returns that LEB number. The returned
+ * LEB is marked as "taken", "index".
+ *
+ * Only empty LEBs are allocated. This is for two reasons. First, the commit
+ * calculates the number of LEBs to allocate based on the assumption that they
+ * will be empty. Secondly, free space at the end of an index LEB is not
+ * guaranteed to be empty because it may have been used by the in-the-gaps
+ * method prior to an unclean unmount.
+ *
+ * If no LEB is found %-ENOSPC is returned. For other failures another negative
+ * error code is returned.
+ */
+int ubifs_find_free_leb_for_idx(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lprops;
+ int lnum = -1, err, flags;
+
+ ubifs_get_lprops(c);
+
+ lprops = ubifs_fast_find_empty(c);
+ if (!lprops) {
+ lprops = ubifs_fast_find_freeable(c);
+ if (!lprops) {
+ /*
+ * The first condition means the following: go scan the
+ * LPT if there are uncategorized lprops, which means
+ * there may be freeable LEBs there (UBIFS does not
+ * store the information about freeable LEBs in the
+ * master node).
+ */
+ if (c->in_a_category_cnt != c->main_lebs ||
+ c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) {
+ ubifs_assert(c, c->freeable_cnt == 0);
+ lprops = scan_for_leb_for_idx(c);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+ }
+ }
+ }
+
+ if (!lprops) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ lnum = lprops->lnum;
+
+ dbg_find("found LEB %d, free %d, dirty %d, flags %#x",
+ lnum, lprops->free, lprops->dirty, lprops->flags);
+
+ flags = lprops->flags | LPROPS_TAKEN | LPROPS_INDEX;
+ lprops = ubifs_change_lp(c, lprops, c->leb_size, 0, flags, 0);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+
+ ubifs_release_lprops(c);
+
+ /*
+ * Ensure that empty LEBs have been unmapped. They may not have been,
+ * for example, because of an unclean unmount. Also LEBs that were
+ * freeable LEBs (free + dirty == leb_size) will not have been unmapped.
+ */
+ err = ubifs_leb_unmap(c, lnum);
+ if (err) {
+ ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_TAKEN | LPROPS_INDEX, 0);
+ return err;
+ }
+
+ return lnum;
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+static int cmp_dirty_idx(const struct ubifs_lprops **a,
+ const struct ubifs_lprops **b)
+{
+ const struct ubifs_lprops *lpa = *a;
+ const struct ubifs_lprops *lpb = *b;
+
+ return lpa->dirty + lpa->free - lpb->dirty - lpb->free;
+}
+
+/**
+ * ubifs_save_dirty_idx_lnums - save an array of the most dirty index LEB nos.
+ * @c: the UBIFS file-system description object
+ *
+ * This function is called each commit to create an array of LEB numbers of
+ * dirty index LEBs sorted in order of dirty and free space. This is used by
+ * the in-the-gaps method of TNC commit.
+ */
+int ubifs_save_dirty_idx_lnums(struct ubifs_info *c)
+{
+ int i;
+
+ ubifs_get_lprops(c);
+ /* Copy the LPROPS_DIRTY_IDX heap */
+ c->dirty_idx.cnt = c->lpt_heap[LPROPS_DIRTY_IDX - 1].cnt;
+ memcpy(c->dirty_idx.arr, c->lpt_heap[LPROPS_DIRTY_IDX - 1].arr,
+ sizeof(void *) * c->dirty_idx.cnt);
+ /* Sort it so that the dirtiest is now at the end */
+ sort(c->dirty_idx.arr, c->dirty_idx.cnt, sizeof(void *),
+ (int (*)(const void *, const void *))cmp_dirty_idx, NULL);
+ dbg_find("found %d dirty index LEBs", c->dirty_idx.cnt);
+ if (c->dirty_idx.cnt)
+ dbg_find("dirtiest index LEB is %d with dirty %d and free %d",
+ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->lnum,
+ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->dirty,
+ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->free);
+ /* Replace the lprops pointers with LEB numbers */
+ for (i = 0; i < c->dirty_idx.cnt; i++)
+ c->dirty_idx.arr[i] = (void *)(size_t)c->dirty_idx.arr[i]->lnum;
+ ubifs_release_lprops(c);
+ return 0;
+}
+
+/**
+ * scan_dirty_idx_cb - callback used by the scan for a dirty index LEB.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_dirty_idx_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops, int in_tree,
+ struct scan_data *data)
+{
+ int ret = LPT_SCAN_CONTINUE;
+
+ /* Exclude LEBs that are currently in use */
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPT_SCAN_CONTINUE;
+ /* Determine whether to add these LEB properties to the tree */
+ if (!in_tree && valuable(c, lprops))
+ ret |= LPT_SCAN_ADD;
+ /* Exclude non-index LEBs */
+ if (!(lprops->flags & LPROPS_INDEX))
+ return ret;
+ /* Exclude LEBs with too little space */
+ if (lprops->free + lprops->dirty < c->min_idx_node_sz)
+ return ret;
+ /* Finally we found space */
+ data->lnum = lprops->lnum;
+ return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * find_dirty_idx_leb - find a dirty index LEB.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB number upon success and a negative error code upon
+ * failure. In particular, -ENOSPC is returned if a dirty index LEB is not
+ * found.
+ *
+ * Note that this function scans the entire LPT but it is called very rarely.
+ */
+static int find_dirty_idx_leb(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ struct scan_data data;
+ int err, i, ret;
+
+ /* Check all structures in memory first */
+ data.lnum = -1;
+ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ lprops = heap->arr[i];
+ ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+ if (ret & LPT_SCAN_STOP)
+ goto found;
+ }
+ list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+ ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+ if (ret & LPT_SCAN_STOP)
+ goto found;
+ }
+ list_for_each_entry(lprops, &c->uncat_list, list) {
+ ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+ if (ret & LPT_SCAN_STOP)
+ goto found;
+ }
+ if (c->pnodes_have >= c->pnode_cnt)
+ /* All pnodes are in memory, so skip scan */
+ return -ENOSPC;
+ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+ (ubifs_lpt_scan_callback)scan_dirty_idx_cb,
+ &data);
+ if (err)
+ return err;
+found:
+ ubifs_assert(c, data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+ c->lscan_lnum = data.lnum;
+ lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+ if (IS_ERR(lprops))
+ return PTR_ERR(lprops);
+ ubifs_assert(c, lprops->lnum == data.lnum);
+ ubifs_assert(c, lprops->free + lprops->dirty >= c->min_idx_node_sz);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, (lprops->flags & LPROPS_INDEX));
+
+ dbg_find("found dirty LEB %d, free %d, dirty %d, flags %#x",
+ lprops->lnum, lprops->free, lprops->dirty, lprops->flags);
+
+ lprops = ubifs_change_lp(c, lprops, LPROPS_NC, LPROPS_NC,
+ lprops->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lprops))
+ return PTR_ERR(lprops);
+
+ return lprops->lnum;
+}
+
+/**
+ * get_idx_gc_leb - try to get a LEB number from trivial GC.
+ * @c: the UBIFS file-system description object
+ */
+static int get_idx_gc_leb(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lp;
+ int err, lnum;
+
+ err = ubifs_get_idx_gc_leb(c);
+ if (err < 0)
+ return err;
+ lnum = err;
+ /*
+ * The LEB was due to be unmapped after the commit but
+ * it is needed now for this commit.
+ */
+ lp = ubifs_lpt_lookup_dirty(c, lnum);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+ lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+ lp->flags | LPROPS_INDEX, -1);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+ dbg_find("LEB %d, dirty %d and free %d flags %#x",
+ lp->lnum, lp->dirty, lp->free, lp->flags);
+ return lnum;
+}
+
+/**
+ * find_dirtiest_idx_leb - find dirtiest index LEB from dirtiest array.
+ * @c: the UBIFS file-system description object
+ */
+static int find_dirtiest_idx_leb(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lp;
+ int lnum;
+
+ while (1) {
+ if (!c->dirty_idx.cnt)
+ return -ENOSPC;
+ /* The lprops pointers were replaced by LEB numbers */
+ lnum = (size_t)c->dirty_idx.arr[--c->dirty_idx.cnt];
+ lp = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+ if ((lp->flags & LPROPS_TAKEN) || !(lp->flags & LPROPS_INDEX))
+ continue;
+ lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+ break;
+ }
+ dbg_find("LEB %d, dirty %d and free %d flags %#x", lp->lnum, lp->dirty,
+ lp->free, lp->flags);
+ ubifs_assert(c, lp->flags & LPROPS_TAKEN);
+ ubifs_assert(c, lp->flags & LPROPS_INDEX);
+ return lnum;
+}
+
+/**
+ * ubifs_find_dirty_idx_leb - try to find dirtiest index LEB as at last commit.
+ * @c: the UBIFS file-system description object
+ *
+ * This function attempts to find an untaken index LEB with the most free and
+ * dirty space that can be used without overwriting index nodes that were in the
+ * last index committed.
+ */
+int ubifs_find_dirty_idx_leb(struct ubifs_info *c)
+{
+ int err;
+
+ ubifs_get_lprops(c);
+
+ /*
+ * We made an array of the dirtiest index LEB numbers as at the start of
+ * last commit. Try that array first.
+ */
+ err = find_dirtiest_idx_leb(c);
+
+ /* Next try scanning the entire LPT */
+ if (err == -ENOSPC)
+ err = find_dirty_idx_leb(c);
+
+ /* Finally take any index LEBs awaiting trivial GC */
+ if (err == -ENOSPC)
+ err = get_idx_gc_leb(c);
+
+ ubifs_release_lprops(c);
+ return err;
+}
diff --git a/ubifs-utils/libubifs/gc.c b/ubifs-utils/libubifs/gc.c
new file mode 100644
index 00000000..3134d070
--- /dev/null
+++ b/ubifs-utils/libubifs/gc.c
@@ -0,0 +1,1017 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements garbage collection. The procedure for garbage collection
+ * is different depending on whether a LEB as an index LEB (contains index
+ * nodes) or not. For non-index LEBs, garbage collection finds a LEB which
+ * contains a lot of dirty space (obsolete nodes), and copies the non-obsolete
+ * nodes to the journal, at which point the garbage-collected LEB is free to be
+ * reused. For index LEBs, garbage collection marks the non-obsolete index nodes
+ * dirty in the TNC, and after the next commit, the garbage-collected LEB is
+ * to be reused. Garbage collection will cause the number of dirty index nodes
+ * to grow, however sufficient space is reserved for the index to ensure the
+ * commit will never run out of space.
+ *
+ * Notes about dead watermark. At current UBIFS implementation we assume that
+ * LEBs which have less than @c->dead_wm bytes of free + dirty space are full
+ * and not worth garbage-collecting. The dead watermark is one min. I/O unit
+ * size, or min. UBIFS node size, depending on what is greater. Indeed, UBIFS
+ * Garbage Collector has to synchronize the GC head's write buffer before
+ * returning, so this is about wasting one min. I/O unit. However, UBIFS GC can
+ * actually reclaim even very small pieces of dirty space by garbage collecting
+ * enough dirty LEBs, but we do not bother doing this at this implementation.
+ *
+ * Notes about dark watermark. The results of GC work depends on how big are
+ * the UBIFS nodes GC deals with. Large nodes make GC waste more space. Indeed,
+ * if GC move data from LEB A to LEB B and nodes in LEB A are large, GC would
+ * have to waste large pieces of free space at the end of LEB B, because nodes
+ * from LEB A would not fit. And the worst situation is when all nodes are of
+ * maximum size. So dark watermark is the amount of free + dirty space in LEB
+ * which are guaranteed to be reclaimable. If LEB has less space, the GC might
+ * be unable to reclaim it. So, LEBs with free + dirty greater than dark
+ * watermark are "good" LEBs from GC's point of view. The other LEBs are not so
+ * good, and GC takes extra care when moving them.
+ */
+
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/list_sort.h>
+#include "ubifs.h"
+
+/*
+ * GC may need to move more than one LEB to make progress. The below constants
+ * define "soft" and "hard" limits on the number of LEBs the garbage collector
+ * may move.
+ */
+#define SOFT_LEBS_LIMIT 4
+#define HARD_LEBS_LIMIT 32
+
+/**
+ * switch_gc_head - switch the garbage collection journal head.
+ * @c: UBIFS file-system description object
+ *
+ * This function switch the GC head to the next LEB which is reserved in
+ * @c->gc_lnum. Returns %0 in case of success, %-EAGAIN if commit is required,
+ * and other negative error code in case of failures.
+ */
+static int switch_gc_head(struct ubifs_info *c)
+{
+ int err, gc_lnum = c->gc_lnum;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ ubifs_assert(c, gc_lnum != -1);
+ dbg_gc("switch GC head from LEB %d:%d to LEB %d (waste %d bytes)",
+ wbuf->lnum, wbuf->offs + wbuf->used, gc_lnum,
+ c->leb_size - wbuf->offs - wbuf->used);
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ return err;
+
+ /*
+ * The GC write-buffer was synchronized, we may safely unmap
+ * 'c->gc_lnum'.
+ */
+ err = ubifs_leb_unmap(c, gc_lnum);
+ if (err)
+ return err;
+
+ err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0);
+ if (err)
+ return err;
+
+ c->gc_lnum = -1;
+ err = ubifs_wbuf_seek_nolock(wbuf, gc_lnum, 0);
+ return err;
+}
+
+/**
+ * data_nodes_cmp - compare 2 data nodes.
+ * @priv: UBIFS file-system description object
+ * @a: first data node
+ * @b: second data node
+ *
+ * This function compares data nodes @a and @b. Returns %1 if @a has greater
+ * inode or block number, and %-1 otherwise.
+ */
+static int data_nodes_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ ino_t inuma, inumb;
+ struct ubifs_info *c = priv;
+ struct ubifs_scan_node *sa, *sb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ sa = list_entry(a, struct ubifs_scan_node, list);
+ sb = list_entry(b, struct ubifs_scan_node, list);
+
+ ubifs_assert(c, key_type(c, &sa->key) == UBIFS_DATA_KEY);
+ ubifs_assert(c, key_type(c, &sb->key) == UBIFS_DATA_KEY);
+ ubifs_assert(c, sa->type == UBIFS_DATA_NODE);
+ ubifs_assert(c, sb->type == UBIFS_DATA_NODE);
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma == inumb) {
+ unsigned int blka = key_block(c, &sa->key);
+ unsigned int blkb = key_block(c, &sb->key);
+
+ if (blka <= blkb)
+ return -1;
+ } else if (inuma <= inumb)
+ return -1;
+
+ return 1;
+}
+
+/*
+ * nondata_nodes_cmp - compare 2 non-data nodes.
+ * @priv: UBIFS file-system description object
+ * @a: first node
+ * @a: second node
+ *
+ * This function compares nodes @a and @b. It makes sure that inode nodes go
+ * first and sorted by length in descending order. Directory entry nodes go
+ * after inode nodes and are sorted in ascending hash valuer order.
+ */
+static int nondata_nodes_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ ino_t inuma, inumb;
+ struct ubifs_info *c = priv;
+ struct ubifs_scan_node *sa, *sb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ sa = list_entry(a, struct ubifs_scan_node, list);
+ sb = list_entry(b, struct ubifs_scan_node, list);
+
+ ubifs_assert(c, key_type(c, &sa->key) != UBIFS_DATA_KEY &&
+ key_type(c, &sb->key) != UBIFS_DATA_KEY);
+ ubifs_assert(c, sa->type != UBIFS_DATA_NODE &&
+ sb->type != UBIFS_DATA_NODE);
+
+ /* Inodes go before directory entries */
+ if (sa->type == UBIFS_INO_NODE) {
+ if (sb->type == UBIFS_INO_NODE)
+ return sb->len - sa->len;
+ return -1;
+ }
+ if (sb->type == UBIFS_INO_NODE)
+ return 1;
+
+ ubifs_assert(c, key_type(c, &sa->key) == UBIFS_DENT_KEY ||
+ key_type(c, &sa->key) == UBIFS_XENT_KEY);
+ ubifs_assert(c, key_type(c, &sb->key) == UBIFS_DENT_KEY ||
+ key_type(c, &sb->key) == UBIFS_XENT_KEY);
+ ubifs_assert(c, sa->type == UBIFS_DENT_NODE ||
+ sa->type == UBIFS_XENT_NODE);
+ ubifs_assert(c, sb->type == UBIFS_DENT_NODE ||
+ sb->type == UBIFS_XENT_NODE);
+
+ inuma = key_inum(c, &sa->key);
+ inumb = key_inum(c, &sb->key);
+
+ if (inuma == inumb) {
+ uint32_t hasha = key_hash(c, &sa->key);
+ uint32_t hashb = key_hash(c, &sb->key);
+
+ if (hasha <= hashb)
+ return -1;
+ } else if (inuma <= inumb)
+ return -1;
+
+ return 1;
+}
+
+/**
+ * sort_nodes - sort nodes for GC.
+ * @c: UBIFS file-system description object
+ * @sleb: describes nodes to sort and contains the result on exit
+ * @nondata: contains non-data nodes on exit
+ * @min: minimum node size is returned here
+ *
+ * This function sorts the list of inodes to garbage collect. First of all, it
+ * kills obsolete nodes and separates data and non-data nodes to the
+ * @sleb->nodes and @nondata lists correspondingly.
+ *
+ * Data nodes are then sorted in block number order - this is important for
+ * bulk-read; data nodes with lower inode number go before data nodes with
+ * higher inode number, and data nodes with lower block number go before data
+ * nodes with higher block number;
+ *
+ * Non-data nodes are sorted as follows.
+ * o First go inode nodes - they are sorted in descending length order.
+ * o Then go directory entry nodes - they are sorted in hash order, which
+ * should supposedly optimize 'readdir()'. Direntry nodes with lower parent
+ * inode number go before direntry nodes with higher parent inode number,
+ * and direntry nodes with lower name hash values go before direntry nodes
+ * with higher name hash values.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ struct list_head *nondata, int *min)
+{
+ int err;
+ struct ubifs_scan_node *snod, *tmp;
+
+ *min = INT_MAX;
+
+ /* Separate data nodes and non-data nodes */
+ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
+ ubifs_assert(c, snod->type == UBIFS_INO_NODE ||
+ snod->type == UBIFS_DATA_NODE ||
+ snod->type == UBIFS_DENT_NODE ||
+ snod->type == UBIFS_XENT_NODE ||
+ snod->type == UBIFS_TRUN_NODE ||
+ snod->type == UBIFS_AUTH_NODE);
+
+ if (snod->type != UBIFS_INO_NODE &&
+ snod->type != UBIFS_DATA_NODE &&
+ snod->type != UBIFS_DENT_NODE &&
+ snod->type != UBIFS_XENT_NODE) {
+ /* Probably truncation node, zap it */
+ list_del(&snod->list);
+ kfree(snod);
+ continue;
+ }
+
+ ubifs_assert(c, key_type(c, &snod->key) == UBIFS_DATA_KEY ||
+ key_type(c, &snod->key) == UBIFS_INO_KEY ||
+ key_type(c, &snod->key) == UBIFS_DENT_KEY ||
+ key_type(c, &snod->key) == UBIFS_XENT_KEY);
+
+ err = ubifs_tnc_has_node(c, &snod->key, 0, sleb->lnum,
+ snod->offs, 0);
+ if (err < 0)
+ return err;
+
+ if (!err) {
+ /* The node is obsolete, remove it from the list */
+ list_del(&snod->list);
+ kfree(snod);
+ continue;
+ }
+
+ if (snod->len < *min)
+ *min = snod->len;
+
+ if (key_type(c, &snod->key) != UBIFS_DATA_KEY)
+ list_move_tail(&snod->list, nondata);
+ }
+
+ /* Sort data and non-data nodes */
+ list_sort(c, &sleb->nodes, &data_nodes_cmp);
+ list_sort(c, nondata, &nondata_nodes_cmp);
+
+ err = dbg_check_data_nodes_order(c, &sleb->nodes);
+ if (err)
+ return err;
+ err = dbg_check_nondata_nodes_order(c, nondata);
+ if (err)
+ return err;
+ return 0;
+}
+
+/**
+ * move_node - move a node.
+ * @c: UBIFS file-system description object
+ * @sleb: describes the LEB to move nodes from
+ * @snod: the mode to move
+ * @wbuf: write-buffer to move node to
+ *
+ * This function moves node @snod to @wbuf, changes TNC correspondingly, and
+ * destroys @snod. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int move_node(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ struct ubifs_scan_node *snod, struct ubifs_wbuf *wbuf)
+{
+ int err, new_lnum = wbuf->lnum, new_offs = wbuf->offs + wbuf->used;
+
+ cond_resched();
+ err = ubifs_wbuf_write_nolock(wbuf, snod->node, snod->len);
+ if (err)
+ return err;
+
+ err = ubifs_tnc_replace(c, &snod->key, sleb->lnum,
+ snod->offs, new_lnum, new_offs,
+ snod->len);
+ list_del(&snod->list);
+ kfree(snod);
+ return err;
+}
+
+/**
+ * move_nodes - move nodes.
+ * @c: UBIFS file-system description object
+ * @sleb: describes the LEB to move nodes from
+ *
+ * This function moves valid nodes from data LEB described by @sleb to the GC
+ * journal head. This function returns zero in case of success, %-EAGAIN if
+ * commit is required, and other negative error codes in case of other
+ * failures.
+ */
+static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
+{
+ int err, min;
+ LIST_HEAD(nondata);
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ if (wbuf->lnum == -1) {
+ /*
+ * The GC journal head is not set, because it is the first GC
+ * invocation since mount.
+ */
+ err = switch_gc_head(c);
+ if (err)
+ return err;
+ }
+
+ err = sort_nodes(c, sleb, &nondata, &min);
+ if (err)
+ goto out;
+
+ /* Write nodes to their new location. Use the first-fit strategy */
+ while (1) {
+ int avail, moved = 0;
+ struct ubifs_scan_node *snod, *tmp;
+
+ /* Move data nodes */
+ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
+ avail = c->leb_size - wbuf->offs - wbuf->used -
+ ubifs_auth_node_sz(c);
+ if (snod->len > avail)
+ /*
+ * Do not skip data nodes in order to optimize
+ * bulk-read.
+ */
+ break;
+
+ err = ubifs_shash_update(c, c->jheads[GCHD].log_hash,
+ snod->node, snod->len);
+ if (err)
+ goto out;
+
+ err = move_node(c, sleb, snod, wbuf);
+ if (err)
+ goto out;
+ moved = 1;
+ }
+
+ /* Move non-data nodes */
+ list_for_each_entry_safe(snod, tmp, &nondata, list) {
+ avail = c->leb_size - wbuf->offs - wbuf->used -
+ ubifs_auth_node_sz(c);
+ if (avail < min)
+ break;
+
+ if (snod->len > avail) {
+ /*
+ * Keep going only if this is an inode with
+ * some data. Otherwise stop and switch the GC
+ * head. IOW, we assume that data-less inode
+ * nodes and direntry nodes are roughly of the
+ * same size.
+ */
+ if (key_type(c, &snod->key) == UBIFS_DENT_KEY ||
+ snod->len == UBIFS_INO_NODE_SZ)
+ break;
+ continue;
+ }
+
+ err = ubifs_shash_update(c, c->jheads[GCHD].log_hash,
+ snod->node, snod->len);
+ if (err)
+ goto out;
+
+ err = move_node(c, sleb, snod, wbuf);
+ if (err)
+ goto out;
+ moved = 1;
+ }
+
+ if (ubifs_authenticated(c) && moved) {
+ struct ubifs_auth_node *auth;
+
+ auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
+ if (!auth) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = ubifs_prepare_auth_node(c, auth,
+ c->jheads[GCHD].log_hash);
+ if (err) {
+ kfree(auth);
+ goto out;
+ }
+
+ err = ubifs_wbuf_write_nolock(wbuf, auth,
+ ubifs_auth_node_sz(c));
+ if (err) {
+ kfree(auth);
+ goto out;
+ }
+
+ ubifs_add_dirt(c, wbuf->lnum, ubifs_auth_node_sz(c));
+ }
+
+ if (list_empty(&sleb->nodes) && list_empty(&nondata))
+ break;
+
+ /*
+ * Waste the rest of the space in the LEB and switch to the
+ * next LEB.
+ */
+ err = switch_gc_head(c);
+ if (err)
+ goto out;
+ }
+
+ return 0;
+
+out:
+ list_splice_tail(&nondata, &sleb->nodes);
+ return err;
+}
+
+/**
+ * gc_sync_wbufs - sync write-buffers for GC.
+ * @c: UBIFS file-system description object
+ *
+ * We must guarantee that obsoleting nodes are on flash. Unfortunately they may
+ * be in a write-buffer instead. That is, a node could be written to a
+ * write-buffer, obsoleting another node in a LEB that is GC'd. If that LEB is
+ * erased before the write-buffer is sync'd and then there is an unclean
+ * unmount, then an existing node is lost. To avoid this, we sync all
+ * write-buffers.
+ *
+ * This function returns %0 on success or a negative error code on failure.
+ */
+static int gc_sync_wbufs(struct ubifs_info *c)
+{
+ int err, i;
+
+ for (i = 0; i < c->jhead_cnt; i++) {
+ if (i == GCHD)
+ continue;
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * ubifs_garbage_collect_leb - garbage-collect a logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lp: describes the LEB to garbage collect
+ *
+ * This function garbage-collects an LEB and returns one of the @LEB_FREED,
+ * @LEB_RETAINED, etc positive codes in case of success, %-EAGAIN if commit is
+ * required, and other negative error codes in case of failures.
+ */
+int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+ int err = 0, lnum = lp->lnum;
+
+ ubifs_assert(c, c->gc_lnum != -1 || wbuf->offs + wbuf->used == 0 ||
+ c->need_recovery);
+ ubifs_assert(c, c->gc_lnum != lnum);
+ ubifs_assert(c, wbuf->lnum != lnum);
+
+ if (lp->free + lp->dirty == c->leb_size) {
+ /* Special case - a free LEB */
+ dbg_gc("LEB %d is free, return it", lp->lnum);
+ ubifs_assert(c, !(lp->flags & LPROPS_INDEX));
+
+ if (lp->free != c->leb_size) {
+ /*
+ * Write buffers must be sync'd before unmapping
+ * freeable LEBs, because one of them may contain data
+ * which obsoletes something in 'lp->lnum'.
+ */
+ err = gc_sync_wbufs(c);
+ if (err)
+ return err;
+ err = ubifs_change_one_lp(c, lp->lnum, c->leb_size,
+ 0, 0, 0, 0);
+ if (err)
+ return err;
+ }
+ err = ubifs_leb_unmap(c, lp->lnum);
+ if (err)
+ return err;
+
+ if (c->gc_lnum == -1) {
+ c->gc_lnum = lnum;
+ return LEB_RETAINED;
+ }
+
+ return LEB_FREED;
+ }
+
+ /*
+ * We scan the entire LEB even though we only really need to scan up to
+ * (c->leb_size - lp->free).
+ */
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+
+ ubifs_assert(c, !list_empty(&sleb->nodes));
+ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_gced_idx_leb *idx_gc;
+
+ dbg_gc("indexing LEB %d (free %d, dirty %d)",
+ lnum, lp->free, lp->dirty);
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ struct ubifs_idx_node *idx = snod->node;
+ int level = le16_to_cpu(idx->level);
+
+ ubifs_assert(c, snod->type == UBIFS_IDX_NODE);
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ err = ubifs_dirty_idx_node(c, &snod->key, level, lnum,
+ snod->offs);
+ if (err)
+ goto out;
+ }
+
+ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS);
+ if (!idx_gc) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ idx_gc->lnum = lnum;
+ idx_gc->unmap = 0;
+ list_add(&idx_gc->list, &c->idx_gc);
+
+ /*
+ * Don't release the LEB until after the next commit, because
+ * it may contain data which is needed for recovery. So
+ * although we freed this LEB, it will become usable only after
+ * the commit.
+ */
+ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0,
+ LPROPS_INDEX, 1);
+ if (err)
+ goto out;
+ err = LEB_FREED_IDX;
+ } else {
+ dbg_gc("data LEB %d (free %d, dirty %d)",
+ lnum, lp->free, lp->dirty);
+
+ err = move_nodes(c, sleb);
+ if (err)
+ goto out_inc_seq;
+
+ err = gc_sync_wbufs(c);
+ if (err)
+ goto out_inc_seq;
+
+ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0, 0, 0);
+ if (err)
+ goto out_inc_seq;
+
+ /* Allow for races with TNC */
+ c->gced_lnum = lnum;
+ smp_wmb();
+ c->gc_seq += 1;
+ smp_wmb();
+
+ if (c->gc_lnum == -1) {
+ c->gc_lnum = lnum;
+ err = LEB_RETAINED;
+ } else {
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ goto out;
+
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+
+ err = LEB_FREED;
+ }
+ }
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_inc_seq:
+ /* We may have moved at least some nodes so allow for races with TNC */
+ c->gced_lnum = lnum;
+ smp_wmb();
+ c->gc_seq += 1;
+ smp_wmb();
+ goto out;
+}
+
+/**
+ * ubifs_garbage_collect - UBIFS garbage collector.
+ * @c: UBIFS file-system description object
+ * @anyway: do GC even if there are free LEBs
+ *
+ * This function does out-of-place garbage collection. The return codes are:
+ * o positive LEB number if the LEB has been freed and may be used;
+ * o %-EAGAIN if the caller has to run commit;
+ * o %-ENOSPC if GC failed to make any progress;
+ * o other negative error codes in case of other errors.
+ *
+ * Garbage collector writes data to the journal when GC'ing data LEBs, and just
+ * marking indexing nodes dirty when GC'ing indexing LEBs. Thus, at some point
+ * commit may be required. But commit cannot be run from inside GC, because the
+ * caller might be holding the commit lock, so %-EAGAIN is returned instead;
+ * And this error code means that the caller has to run commit, and re-run GC
+ * if there is still no free space.
+ *
+ * There are many reasons why this function may return %-EAGAIN:
+ * o the log is full and there is no space to write an LEB reference for
+ * @c->gc_lnum;
+ * o the journal is too large and exceeds size limitations;
+ * o GC moved indexing LEBs, but they can be used only after the commit;
+ * o the shrinker fails to find clean znodes to free and requests the commit;
+ * o etc.
+ *
+ * Note, if the file-system is close to be full, this function may return
+ * %-EAGAIN infinitely, so the caller has to limit amount of re-invocations of
+ * the function. E.g., this happens if the limits on the journal size are too
+ * tough and GC writes too much to the journal before an LEB is freed. This
+ * might also mean that the journal is too large, and the TNC becomes to big,
+ * so that the shrinker is constantly called, finds not clean znodes to free,
+ * and requests commit. Well, this may also happen if the journal is all right,
+ * but another kernel process consumes too much memory. Anyway, infinite
+ * %-EAGAIN may happen, but in some extreme/misconfiguration cases.
+ */
+int ubifs_garbage_collect(struct ubifs_info *c, int anyway)
+{
+ int i, err, ret, min_space = c->dead_wm;
+ struct ubifs_lprops lp;
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+ ubifs_assert_cmt_locked(c);
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+
+ if (ubifs_gc_should_commit(c))
+ return -EAGAIN;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+
+ if (c->ro_error) {
+ ret = -EROFS;
+ goto out_unlock;
+ }
+
+ /* We expect the write-buffer to be empty on entry */
+ ubifs_assert(c, !wbuf->used);
+
+ for (i = 0; ; i++) {
+ int space_before, space_after;
+
+ /* Maybe continue after find and break before find */
+ lp.lnum = -1;
+
+ cond_resched();
+
+ /* Give the commit an opportunity to run */
+ if (ubifs_gc_should_commit(c)) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (i > SOFT_LEBS_LIMIT && !list_empty(&c->idx_gc)) {
+ /*
+ * We've done enough iterations. Indexing LEBs were
+ * moved and will be available after the commit.
+ */
+ dbg_gc("soft limit, some index LEBs GC'ed, -EAGAIN");
+ ubifs_commit_required(c);
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (i > HARD_LEBS_LIMIT) {
+ /*
+ * We've moved too many LEBs and have not made
+ * progress, give up.
+ */
+ dbg_gc("hard limit, -ENOSPC");
+ ret = -ENOSPC;
+ break;
+ }
+
+ /*
+ * Empty and freeable LEBs can turn up while we waited for
+ * the wbuf lock, or while we have been running GC. In that
+ * case, we should just return one of those instead of
+ * continuing to GC dirty LEBs. Hence we request
+ * 'ubifs_find_dirty_leb()' to return an empty LEB if it can.
+ */
+ ret = ubifs_find_dirty_leb(c, &lp, min_space, anyway ? 0 : 1);
+ if (ret) {
+ if (ret == -ENOSPC)
+ dbg_gc("no more dirty LEBs");
+ break;
+ }
+
+ dbg_gc("found LEB %d: free %d, dirty %d, sum %d (min. space %d)",
+ lp.lnum, lp.free, lp.dirty, lp.free + lp.dirty,
+ min_space);
+
+ space_before = c->leb_size - wbuf->offs - wbuf->used;
+ if (wbuf->lnum == -1)
+ space_before = 0;
+
+ ret = ubifs_garbage_collect_leb(c, &lp);
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ /*
+ * This is not error, so we have to return the
+ * LEB to lprops. But if 'ubifs_return_leb()'
+ * fails, its failure code is propagated to the
+ * caller instead of the original '-EAGAIN'.
+ */
+ err = ubifs_return_leb(c, lp.lnum);
+ if (err) {
+ ret = err;
+ /*
+ * An LEB may always be "taken",
+ * so setting ubifs to read-only,
+ * and then executing sync wbuf will
+ * return -EROFS and enter the "out"
+ * error branch.
+ */
+ ubifs_ro_mode(c, ret);
+ }
+ /* Maybe double return LEB if goto out */
+ lp.lnum = -1;
+ break;
+ }
+ goto out;
+ }
+
+ if (ret == LEB_FREED) {
+ /* An LEB has been freed and is ready for use */
+ dbg_gc("LEB %d freed, return", lp.lnum);
+ ret = lp.lnum;
+ break;
+ }
+
+ if (ret == LEB_FREED_IDX) {
+ /*
+ * This was an indexing LEB and it cannot be
+ * immediately used. And instead of requesting the
+ * commit straight away, we try to garbage collect some
+ * more.
+ */
+ dbg_gc("indexing LEB %d freed, continue", lp.lnum);
+ continue;
+ }
+
+ ubifs_assert(c, ret == LEB_RETAINED);
+ space_after = c->leb_size - wbuf->offs - wbuf->used;
+ dbg_gc("LEB %d retained, freed %d bytes", lp.lnum,
+ space_after - space_before);
+
+ if (space_after > space_before) {
+ /* GC makes progress, keep working */
+ min_space >>= 1;
+ if (min_space < c->dead_wm)
+ min_space = c->dead_wm;
+ continue;
+ }
+
+ dbg_gc("did not make progress");
+
+ /*
+ * GC moved an LEB bud have not done any progress. This means
+ * that the previous GC head LEB contained too few free space
+ * and the LEB which was GC'ed contained only large nodes which
+ * did not fit that space.
+ *
+ * We can do 2 things:
+ * 1. pick another LEB in a hope it'll contain a small node
+ * which will fit the space we have at the end of current GC
+ * head LEB, but there is no guarantee, so we try this out
+ * unless we have already been working for too long;
+ * 2. request an LEB with more dirty space, which will force
+ * 'ubifs_find_dirty_leb()' to start scanning the lprops
+ * table, instead of just picking one from the heap
+ * (previously it already picked the dirtiest LEB).
+ */
+ if (i < SOFT_LEBS_LIMIT) {
+ dbg_gc("try again");
+ continue;
+ }
+
+ min_space <<= 1;
+ if (min_space > c->dark_wm)
+ min_space = c->dark_wm;
+ dbg_gc("set min. space to %d", min_space);
+ }
+
+ if (ret == -ENOSPC && !list_empty(&c->idx_gc)) {
+ dbg_gc("no space, some index LEBs GC'ed, -EAGAIN");
+ ubifs_commit_required(c);
+ ret = -EAGAIN;
+ }
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (!err)
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err) {
+ ret = err;
+ goto out;
+ }
+out_unlock:
+ mutex_unlock(&wbuf->io_mutex);
+ return ret;
+
+out:
+ ubifs_assert(c, ret < 0);
+ ubifs_assert(c, ret != -ENOSPC && ret != -EAGAIN);
+ ubifs_wbuf_sync_nolock(wbuf);
+ ubifs_ro_mode(c, ret);
+ mutex_unlock(&wbuf->io_mutex);
+ if (lp.lnum != -1)
+ ubifs_return_leb(c, lp.lnum);
+ return ret;
+}
+
+/**
+ * ubifs_gc_start_commit - garbage collection at start of commit.
+ * @c: UBIFS file-system description object
+ *
+ * If a LEB has only dirty and free space, then we may safely unmap it and make
+ * it free. Note, we cannot do this with indexing LEBs because dirty space may
+ * correspond index nodes that are required for recovery. In that case, the
+ * LEB cannot be unmapped until after the next commit.
+ *
+ * This function returns %0 upon success and a negative error code upon failure.
+ */
+int ubifs_gc_start_commit(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc;
+ const struct ubifs_lprops *lp;
+ int err = 0, flags;
+
+ ubifs_get_lprops(c);
+
+ /*
+ * Unmap (non-index) freeable LEBs. Note that recovery requires that all
+ * wbufs are sync'd before this, which is done in 'do_commit()'.
+ */
+ while (1) {
+ lp = ubifs_fast_find_freeable(c);
+ if (!lp)
+ break;
+ ubifs_assert(c, !(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lp->flags & LPROPS_INDEX));
+ err = ubifs_leb_unmap(c, lp->lnum);
+ if (err)
+ goto out;
+ lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ ubifs_assert(c, !(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lp->flags & LPROPS_INDEX));
+ }
+
+ /* Mark GC'd index LEBs OK to unmap after this commit finishes */
+ list_for_each_entry(idx_gc, &c->idx_gc, list)
+ idx_gc->unmap = 1;
+
+ /* Record index freeable LEBs for unmapping after commit */
+ while (1) {
+ lp = ubifs_fast_find_frdi_idx(c);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+ if (!lp)
+ break;
+ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS);
+ if (!idx_gc) {
+ err = -ENOMEM;
+ goto out;
+ }
+ ubifs_assert(c, !(lp->flags & LPROPS_TAKEN));
+ ubifs_assert(c, lp->flags & LPROPS_INDEX);
+ /* Don't release the LEB until after the next commit */
+ flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX;
+ lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ kfree(idx_gc);
+ goto out;
+ }
+ ubifs_assert(c, lp->flags & LPROPS_TAKEN);
+ ubifs_assert(c, !(lp->flags & LPROPS_INDEX));
+ idx_gc->lnum = lp->lnum;
+ idx_gc->unmap = 1;
+ list_add(&idx_gc->list, &c->idx_gc);
+ }
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_gc_end_commit - garbage collection at end of commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function completes out-of-place garbage collection of index LEBs.
+ */
+int ubifs_gc_end_commit(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc, *tmp;
+ struct ubifs_wbuf *wbuf;
+ int err = 0;
+
+ wbuf = &c->jheads[GCHD].wbuf;
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ list_for_each_entry_safe(idx_gc, tmp, &c->idx_gc, list)
+ if (idx_gc->unmap) {
+ dbg_gc("LEB %d", idx_gc->lnum);
+ err = ubifs_leb_unmap(c, idx_gc->lnum);
+ if (err)
+ goto out;
+ err = ubifs_change_one_lp(c, idx_gc->lnum, LPROPS_NC,
+ LPROPS_NC, 0, LPROPS_TAKEN, -1);
+ if (err)
+ goto out;
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ }
+out:
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+}
+
+/**
+ * ubifs_destroy_idx_gc - destroy idx_gc list.
+ * @c: UBIFS file-system description object
+ *
+ * This function destroys the @c->idx_gc list. It is called when unmounting
+ * so locks are not needed. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+void ubifs_destroy_idx_gc(struct ubifs_info *c)
+{
+ while (!list_empty(&c->idx_gc)) {
+ struct ubifs_gced_idx_leb *idx_gc;
+
+ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb,
+ list);
+ c->idx_gc_cnt -= 1;
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ }
+}
+
+/**
+ * ubifs_get_idx_gc_leb - get a LEB from GC'd index LEB list.
+ * @c: UBIFS file-system description object
+ *
+ * Called during start commit so locks are not needed.
+ */
+int ubifs_get_idx_gc_leb(struct ubifs_info *c)
+{
+ struct ubifs_gced_idx_leb *idx_gc;
+ int lnum;
+
+ if (list_empty(&c->idx_gc))
+ return -ENOSPC;
+ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb, list);
+ lnum = idx_gc->lnum;
+ /* c->idx_gc_cnt is updated by the caller when lprops are updated */
+ list_del(&idx_gc->list);
+ kfree(idx_gc);
+ return lnum;
+}
diff --git a/ubifs-utils/libubifs/io.c b/ubifs-utils/libubifs/io.c
new file mode 100644
index 00000000..01d8eb17
--- /dev/null
+++ b/ubifs-utils/libubifs/io.c
@@ -0,0 +1,1268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ * Copyright (C) 2006, 2007 University of Szeged, Hungary
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ * Zoltan Sogor
+ */
+
+/*
+ * This file implements UBIFS I/O subsystem which provides various I/O-related
+ * helper functions (reading/writing/checking/validating nodes) and implements
+ * write-buffering support. Write buffers help to save space which otherwise
+ * would have been wasted for padding to the nearest minimal I/O unit boundary.
+ * Instead, data first goes to the write-buffer and is flushed when the
+ * buffer is full or when it is not used for some time (by timer). This is
+ * similar to the mechanism is used by JFFS2.
+ *
+ * UBIFS distinguishes between minimum write size (@c->min_io_size) and maximum
+ * write size (@c->max_write_size). The latter is the maximum amount of bytes
+ * the underlying flash is able to program at a time, and writing in
+ * @c->max_write_size units should presumably be faster. Obviously,
+ * @c->min_io_size <= @c->max_write_size. Write-buffers are of
+ * @c->max_write_size bytes in size for maximum performance. However, when a
+ * write-buffer is flushed, only the portion of it (aligned to @c->min_io_size
+ * boundary) which contains data is written, not the whole write-buffer,
+ * because this is more space-efficient.
+ *
+ * This optimization adds few complications to the code. Indeed, on the one
+ * hand, we want to write in optimal @c->max_write_size bytes chunks, which
+ * also means aligning writes at the @c->max_write_size bytes offsets. On the
+ * other hand, we do not want to waste space when synchronizing the write
+ * buffer, so during synchronization we writes in smaller chunks. And this makes
+ * the next write offset to be not aligned to @c->max_write_size bytes. So the
+ * have to make sure that the write-buffer offset (@wbuf->offs) becomes aligned
+ * to @c->max_write_size bytes again. We do this by temporarily shrinking
+ * write-buffer size (@wbuf->size).
+ *
+ * Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by
+ * mutexes defined inside these objects. Since sometimes upper-level code
+ * has to lock the write-buffer (e.g. journal space reservation code), many
+ * functions related to write-buffers have "nolock" suffix which means that the
+ * caller has to lock the write-buffer before calling this function.
+ *
+ * UBIFS stores nodes at 64 bit-aligned addresses. If the node length is not
+ * aligned, UBIFS starts the next node from the aligned address, and the padded
+ * bytes may contain any rubbish. In other words, UBIFS does not put padding
+ * bytes in those small gaps. Common headers of nodes store real node lengths,
+ * not aligned lengths. Indexing nodes also store real lengths in branches.
+ *
+ * UBIFS uses padding when it pads to the next min. I/O unit. In this case it
+ * uses padding nodes or padding bytes, if the padding node does not fit.
+ *
+ * All UBIFS nodes are protected by CRC checksums and UBIFS checks CRC when
+ * they are read from the flash media.
+ */
+
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include "ubifs.h"
+
+/**
+ * ubifs_ro_mode - switch UBIFS to read read-only mode.
+ * @c: UBIFS file-system description object
+ * @err: error code which is the reason of switching to R/O mode
+ */
+void ubifs_ro_mode(struct ubifs_info *c, int err)
+{
+ if (!c->ro_error) {
+ c->ro_error = 1;
+ c->no_chk_data_crc = 0;
+ c->vfs_sb->s_flags |= SB_RDONLY;
+ ubifs_warn(c, "switched to read-only mode, error %d", err);
+ dump_stack();
+ }
+}
+
+/*
+ * Below are simple wrappers over UBI I/O functions which include some
+ * additional checks and UBIFS debugging stuff. See corresponding UBI function
+ * for more information.
+ */
+
+int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
+ int len, int even_ebadmsg)
+{
+ int err;
+
+ err = ubi_read(c->ubi, lnum, buf, offs, len);
+ /*
+ * In case of %-EBADMSG print the error message only if the
+ * @even_ebadmsg is true.
+ */
+ if (err && (err != -EBADMSG || even_ebadmsg)) {
+ ubifs_err(c, "reading %d bytes from LEB %d:%d failed, error %d",
+ len, lnum, offs, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len)
+{
+ int err;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_write(c->ubi, lnum, buf, offs, len);
+ else
+ err = dbg_leb_write(c, lnum, buf, offs, len);
+ if (err) {
+ ubifs_err(c, "writing %d bytes to LEB %d:%d failed, error %d",
+ len, lnum, offs, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len)
+{
+ int err;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_change(c->ubi, lnum, buf, len);
+ else
+ err = dbg_leb_change(c, lnum, buf, len);
+ if (err) {
+ ubifs_err(c, "changing %d bytes in LEB %d failed, error %d",
+ len, lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_unmap(c->ubi, lnum);
+ else
+ err = dbg_leb_unmap(c, lnum);
+ if (err) {
+ ubifs_err(c, "unmap LEB %d failed, error %d", lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_leb_map(struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+ if (!dbg_is_tst_rcvry(c))
+ err = ubi_leb_map(c->ubi, lnum);
+ else
+ err = dbg_leb_map(c, lnum);
+ if (err) {
+ ubifs_err(c, "mapping LEB %d failed, error %d", lnum, err);
+ ubifs_ro_mode(c, err);
+ dump_stack();
+ }
+ return err;
+}
+
+int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
+{
+ int err;
+
+ err = ubi_is_mapped(c->ubi, lnum);
+ if (err < 0) {
+ ubifs_err(c, "ubi_is_mapped failed for LEB %d, error %d",
+ lnum, err);
+ dump_stack();
+ }
+ return err;
+}
+
+static void record_magic_error(struct ubifs_stats_info *stats)
+{
+ if (stats)
+ stats->magic_errors++;
+}
+
+static void record_node_error(struct ubifs_stats_info *stats)
+{
+ if (stats)
+ stats->node_errors++;
+}
+
+static void record_crc_error(struct ubifs_stats_info *stats)
+{
+ if (stats)
+ stats->crc_errors++;
+}
+
+/**
+ * ubifs_check_node - check node.
+ * @c: UBIFS file-system description object
+ * @buf: node to check
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ * @quiet: print no messages
+ * @must_chk_crc: indicates whether to always check the CRC
+ *
+ * This function checks node magic number and CRC checksum. This function also
+ * validates node length to prevent UBIFS from becoming crazy when an attacker
+ * feeds it a file-system image with incorrect nodes. For example, too large
+ * node length in the common header could cause UBIFS to read memory outside of
+ * allocated buffer when checking the CRC checksum.
+ *
+ * This function may skip data nodes CRC checking if @c->no_chk_data_crc is
+ * true, which is controlled by corresponding UBIFS mount option. However, if
+ * @must_chk_crc is true, then @c->no_chk_data_crc is ignored and CRC is
+ * checked. Similarly, if @c->mounting or @c->remounting_rw is true (we are
+ * mounting or re-mounting to R/W mode), @c->no_chk_data_crc is ignored and CRC
+ * is checked. This is because during mounting or re-mounting from R/O mode to
+ * R/W mode we may read journal nodes (when replying the journal or doing the
+ * recovery) and the journal nodes may potentially be corrupted, so checking is
+ * required.
+ *
+ * This function returns zero in case of success and %-EUCLEAN in case of bad
+ * CRC or magic.
+ */
+int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
+ int lnum, int offs, int quiet, int must_chk_crc)
+{
+ int err = -EINVAL, type, node_len;
+ uint32_t crc, node_crc, magic;
+ const struct ubifs_ch *ch = buf;
+
+ ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(c, !(offs & 7) && offs < c->leb_size);
+
+ magic = le32_to_cpu(ch->magic);
+ if (magic != UBIFS_NODE_MAGIC) {
+ if (!quiet)
+ ubifs_err(c, "bad magic %#08x, expected %#08x",
+ magic, UBIFS_NODE_MAGIC);
+ record_magic_error(c->stats);
+ err = -EUCLEAN;
+ goto out;
+ }
+
+ type = ch->node_type;
+ if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
+ if (!quiet)
+ ubifs_err(c, "bad node type %d", type);
+ record_node_error(c->stats);
+ goto out;
+ }
+
+ node_len = le32_to_cpu(ch->len);
+ if (node_len + offs > c->leb_size)
+ goto out_len;
+
+ if (c->ranges[type].max_len == 0) {
+ if (node_len != c->ranges[type].len)
+ goto out_len;
+ } else if (node_len < c->ranges[type].min_len ||
+ node_len > c->ranges[type].max_len)
+ goto out_len;
+
+ if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->mounting &&
+ !c->remounting_rw && c->no_chk_data_crc)
+ return 0;
+
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
+ node_crc = le32_to_cpu(ch->crc);
+ if (crc != node_crc) {
+ if (!quiet)
+ ubifs_err(c, "bad CRC: calculated %#08x, read %#08x",
+ crc, node_crc);
+ record_crc_error(c->stats);
+ err = -EUCLEAN;
+ goto out;
+ }
+
+ return 0;
+
+out_len:
+ if (!quiet)
+ ubifs_err(c, "bad node length %d", node_len);
+out:
+ if (!quiet) {
+ ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
+ ubifs_dump_node(c, buf, len);
+ dump_stack();
+ }
+ return err;
+}
+
+/**
+ * ubifs_pad - pad flash space.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to put padding to
+ * @pad: how many bytes to pad
+ *
+ * The flash media obliges us to write only in chunks of %c->min_io_size and
+ * when we have to write less data we add padding node to the write-buffer and
+ * pad it to the next minimal I/O unit's boundary. Padding nodes help when the
+ * media is being scanned. If the amount of wasted space is not enough to fit a
+ * padding node which takes %UBIFS_PAD_NODE_SZ bytes, we write padding bytes
+ * pattern (%UBIFS_PADDING_BYTE).
+ *
+ * Padding nodes are also used to fill gaps when the "commit-in-gaps" method is
+ * used.
+ */
+void ubifs_pad(const struct ubifs_info *c, void *buf, int pad)
+{
+ uint32_t crc;
+
+ ubifs_assert(c, pad >= 0);
+
+ if (pad >= UBIFS_PAD_NODE_SZ) {
+ struct ubifs_ch *ch = buf;
+ struct ubifs_pad_node *pad_node = buf;
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->node_type = UBIFS_PAD_NODE;
+ ch->group_type = UBIFS_NO_NODE_GROUP;
+ ch->padding[0] = ch->padding[1] = 0;
+ ch->sqnum = 0;
+ ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ);
+ pad -= UBIFS_PAD_NODE_SZ;
+ pad_node->pad_len = cpu_to_le32(pad);
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8);
+ ch->crc = cpu_to_le32(crc);
+ memset(buf + UBIFS_PAD_NODE_SZ, 0, pad);
+ } else if (pad > 0)
+ /* Too little space, padding node won't fit */
+ memset(buf, UBIFS_PADDING_BYTE, pad);
+}
+
+/**
+ * next_sqnum - get next sequence number.
+ * @c: UBIFS file-system description object
+ */
+static unsigned long long next_sqnum(struct ubifs_info *c)
+{
+ unsigned long long sqnum;
+
+ spin_lock(&c->cnt_lock);
+ sqnum = ++c->max_sqnum;
+ spin_unlock(&c->cnt_lock);
+
+ if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) {
+ if (sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "sequence number overflow %llu, end of life",
+ sqnum);
+ ubifs_ro_mode(c, -EINVAL);
+ }
+ ubifs_warn(c, "running out of sequence numbers, end of life soon");
+ }
+
+ return sqnum;
+}
+
+void ubifs_init_node(struct ubifs_info *c, void *node, int len, int pad)
+{
+ struct ubifs_ch *ch = node;
+ unsigned long long sqnum = next_sqnum(c);
+
+ ubifs_assert(c, len >= UBIFS_CH_SZ);
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->len = cpu_to_le32(len);
+ ch->group_type = UBIFS_NO_NODE_GROUP;
+ ch->sqnum = cpu_to_le64(sqnum);
+ ch->padding[0] = ch->padding[1] = 0;
+
+ if (pad) {
+ len = ALIGN(len, 8);
+ pad = ALIGN(len, c->min_io_size) - len;
+ ubifs_pad(c, node + len, pad);
+ }
+}
+
+void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
+{
+ struct ubifs_ch *ch = node;
+ uint32_t crc;
+
+ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
+ ch->crc = cpu_to_le32(crc);
+}
+
+/**
+ * ubifs_prepare_node_hmac - prepare node to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @hmac_offs: offset of the HMAC in the node
+ * @pad: if the buffer has to be padded
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC, fills the common header, and adds proper padding up to
+ * the next minimum I/O unit if @pad is not zero. if @hmac_offs is positive then
+ * a HMAC is inserted into the node at the given offset.
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len,
+ int hmac_offs, int pad)
+{
+ int err;
+
+ ubifs_init_node(c, node, len, pad);
+
+ if (hmac_offs > 0) {
+ err = ubifs_node_insert_hmac(c, node, len, hmac_offs);
+ if (err)
+ return err;
+ }
+
+ ubifs_crc_node(c, node, len);
+
+ return 0;
+}
+
+/**
+ * ubifs_prepare_node - prepare node to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @pad: if the buffer has to be padded
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC, fills the common header, and adds proper padding up to
+ * the next minimum I/O unit if @pad is not zero.
+ */
+void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
+{
+ /*
+ * Deliberately ignore return value since this function can only fail
+ * when a hmac offset is given.
+ */
+ ubifs_prepare_node_hmac(c, node, len, 0, pad);
+}
+
+/**
+ * ubifs_prep_grp_node - prepare node of a group to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @last: indicates the last node of the group
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC and fills the common header.
+ */
+void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last)
+{
+ uint32_t crc;
+ struct ubifs_ch *ch = node;
+ unsigned long long sqnum = next_sqnum(c);
+
+ ubifs_assert(c, len >= UBIFS_CH_SZ);
+
+ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
+ ch->len = cpu_to_le32(len);
+ if (last)
+ ch->group_type = UBIFS_LAST_OF_NODE_GROUP;
+ else
+ ch->group_type = UBIFS_IN_NODE_GROUP;
+ ch->sqnum = cpu_to_le64(sqnum);
+ ch->padding[0] = ch->padding[1] = 0;
+ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
+ ch->crc = cpu_to_le32(crc);
+}
+
+/**
+ * wbuf_timer_callback_nolock - write-buffer timer callback function.
+ * @timer: timer data (write-buffer descriptor)
+ *
+ * This function is called when the write-buffer timer expires.
+ */
+static enum hrtimer_restart wbuf_timer_callback_nolock(struct hrtimer *timer)
+{
+ struct ubifs_wbuf *wbuf = container_of(timer, struct ubifs_wbuf, timer);
+
+ dbg_io("jhead %s", dbg_jhead(wbuf->jhead));
+ wbuf->need_sync = 1;
+ wbuf->c->need_wbuf_sync = 1;
+ ubifs_wake_up_bgt(wbuf->c);
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * new_wbuf_timer_nolock - start new write-buffer timer.
+ * @c: UBIFS file-system description object
+ * @wbuf: write-buffer descriptor
+ */
+static void new_wbuf_timer_nolock(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
+{
+ ktime_t softlimit = ms_to_ktime(dirty_writeback_interval * 10);
+ unsigned long long delta = dirty_writeback_interval;
+
+ /* centi to milli, milli to nano, then 10% */
+ delta *= 10ULL * NSEC_PER_MSEC / 10ULL;
+
+ ubifs_assert(c, !hrtimer_active(&wbuf->timer));
+ ubifs_assert(c, delta <= ULONG_MAX);
+
+ if (wbuf->no_timer)
+ return;
+ dbg_io("set timer for jhead %s, %llu-%llu millisecs",
+ dbg_jhead(wbuf->jhead),
+ div_u64(ktime_to_ns(softlimit), USEC_PER_SEC),
+ div_u64(ktime_to_ns(softlimit) + delta, USEC_PER_SEC));
+ hrtimer_start_range_ns(&wbuf->timer, softlimit, delta,
+ HRTIMER_MODE_REL);
+}
+
+/**
+ * cancel_wbuf_timer_nolock - cancel write-buffer timer.
+ * @wbuf: write-buffer descriptor
+ */
+static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
+{
+ if (wbuf->no_timer)
+ return;
+ wbuf->need_sync = 0;
+ hrtimer_cancel(&wbuf->timer);
+}
+
+/**
+ * ubifs_wbuf_sync_nolock - synchronize write-buffer.
+ * @wbuf: write-buffer to synchronize
+ *
+ * This function synchronizes write-buffer @buf and returns zero in case of
+ * success or a negative error code in case of failure.
+ *
+ * Note, although write-buffers are of @c->max_write_size, this function does
+ * not necessarily writes all @c->max_write_size bytes to the flash. Instead,
+ * if the write-buffer is only partially filled with data, only the used part
+ * of the write-buffer (aligned on @c->min_io_size boundary) is synchronized.
+ * This way we waste less space.
+ */
+int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
+{
+ struct ubifs_info *c = wbuf->c;
+ int err, dirt, sync_len;
+
+ cancel_wbuf_timer_nolock(wbuf);
+ if (!wbuf->used || wbuf->lnum == -1)
+ /* Write-buffer is empty or not seeked */
+ return 0;
+
+ dbg_io("LEB %d:%d, %d bytes, jhead %s",
+ wbuf->lnum, wbuf->offs, wbuf->used, dbg_jhead(wbuf->jhead));
+ ubifs_assert(c, !(wbuf->avail & 7));
+ ubifs_assert(c, wbuf->offs + wbuf->size <= c->leb_size);
+ ubifs_assert(c, wbuf->size >= c->min_io_size);
+ ubifs_assert(c, wbuf->size <= c->max_write_size);
+ ubifs_assert(c, wbuf->size % c->min_io_size == 0);
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ ubifs_assert(c, !((wbuf->offs + wbuf->size) % c->max_write_size));
+
+ if (c->ro_error)
+ return -EROFS;
+
+ /*
+ * Do not write whole write buffer but write only the minimum necessary
+ * amount of min. I/O units.
+ */
+ sync_len = ALIGN(wbuf->used, c->min_io_size);
+ dirt = sync_len - wbuf->used;
+ if (dirt)
+ ubifs_pad(c, wbuf->buf + wbuf->used, dirt);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, sync_len);
+ if (err)
+ return err;
+
+ spin_lock(&wbuf->lock);
+ wbuf->offs += sync_len;
+ /*
+ * Now @wbuf->offs is not necessarily aligned to @c->max_write_size.
+ * But our goal is to optimize writes and make sure we write in
+ * @c->max_write_size chunks and to @c->max_write_size-aligned offset.
+ * Thus, if @wbuf->offs is not aligned to @c->max_write_size now, make
+ * sure that @wbuf->offs + @wbuf->size is aligned to
+ * @c->max_write_size. This way we make sure that after next
+ * write-buffer flush we are again at the optimal offset (aligned to
+ * @c->max_write_size).
+ */
+ if (c->leb_size - wbuf->offs < c->max_write_size)
+ wbuf->size = c->leb_size - wbuf->offs;
+ else if (wbuf->offs & (c->max_write_size - 1))
+ wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
+ else
+ wbuf->size = c->max_write_size;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+
+ if (wbuf->sync_callback)
+ err = wbuf->sync_callback(c, wbuf->lnum,
+ c->leb_size - wbuf->offs, dirt);
+ return err;
+}
+
+/**
+ * ubifs_wbuf_seek_nolock - seek write-buffer.
+ * @wbuf: write-buffer
+ * @lnum: logical eraseblock number to seek to
+ * @offs: logical eraseblock offset to seek to
+ *
+ * This function targets the write-buffer to logical eraseblock @lnum:@offs.
+ * The write-buffer has to be empty. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+
+ dbg_io("LEB %d:%d, jhead %s", lnum, offs, dbg_jhead(wbuf->jhead));
+ ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt);
+ ubifs_assert(c, offs >= 0 && offs <= c->leb_size);
+ ubifs_assert(c, offs % c->min_io_size == 0 && !(offs & 7));
+ ubifs_assert(c, lnum != wbuf->lnum);
+ ubifs_assert(c, wbuf->used == 0);
+
+ spin_lock(&wbuf->lock);
+ wbuf->lnum = lnum;
+ wbuf->offs = offs;
+ if (c->leb_size - wbuf->offs < c->max_write_size)
+ wbuf->size = c->leb_size - wbuf->offs;
+ else if (wbuf->offs & (c->max_write_size - 1))
+ wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs;
+ else
+ wbuf->size = c->max_write_size;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ spin_unlock(&wbuf->lock);
+
+ return 0;
+}
+
+/**
+ * ubifs_bg_wbufs_sync - synchronize write-buffers.
+ * @c: UBIFS file-system description object
+ *
+ * This function is called by background thread to synchronize write-buffers.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_bg_wbufs_sync(struct ubifs_info *c)
+{
+ int err, i;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (!c->need_wbuf_sync)
+ return 0;
+ c->need_wbuf_sync = 0;
+
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_timers;
+ }
+
+ dbg_io("synchronize");
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ cond_resched();
+
+ /*
+ * If the mutex is locked then wbuf is being changed, so
+ * synchronization is not necessary.
+ */
+ if (mutex_is_locked(&wbuf->io_mutex))
+ continue;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ if (!wbuf->need_sync) {
+ mutex_unlock(&wbuf->io_mutex);
+ continue;
+ }
+
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ if (err) {
+ ubifs_err(c, "cannot sync write-buffer, error %d", err);
+ ubifs_ro_mode(c, err);
+ goto out_timers;
+ }
+ }
+
+ return 0;
+
+out_timers:
+ /* Cancel all timers to prevent repeated errors */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ cancel_wbuf_timer_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ }
+ return err;
+}
+
+/**
+ * ubifs_wbuf_write_nolock - write data to flash via write-buffer.
+ * @wbuf: write-buffer
+ * @buf: node to write
+ * @len: node length
+ *
+ * This function writes data to flash via write-buffer @wbuf. This means that
+ * the last piece of the node won't reach the flash media immediately if it
+ * does not take whole max. write unit (@c->max_write_size). Instead, the node
+ * will sit in RAM until the write-buffer is synchronized (e.g., by timer, or
+ * because more data are appended to the write-buffer).
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If the node cannot be written because there is no more
+ * space in this logical eraseblock, %-ENOSPC is returned.
+ */
+int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
+{
+ struct ubifs_info *c = wbuf->c;
+ int err, n, written = 0, aligned_len = ALIGN(len, 8);
+
+ dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len,
+ dbg_ntype(((struct ubifs_ch *)buf)->node_type),
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs + wbuf->used);
+ ubifs_assert(c, len > 0 && wbuf->lnum >= 0 && wbuf->lnum < c->leb_cnt);
+ ubifs_assert(c, wbuf->offs >= 0 && wbuf->offs % c->min_io_size == 0);
+ ubifs_assert(c, !(wbuf->offs & 7) && wbuf->offs <= c->leb_size);
+ ubifs_assert(c, wbuf->avail > 0 && wbuf->avail <= wbuf->size);
+ ubifs_assert(c, wbuf->size >= c->min_io_size);
+ ubifs_assert(c, wbuf->size <= c->max_write_size);
+ ubifs_assert(c, wbuf->size % c->min_io_size == 0);
+ ubifs_assert(c, mutex_is_locked(&wbuf->io_mutex));
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ ubifs_assert(c, !c->space_fixup);
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ ubifs_assert(c, !((wbuf->offs + wbuf->size) % c->max_write_size));
+
+ if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ cancel_wbuf_timer_nolock(wbuf);
+
+ if (c->ro_error)
+ return -EROFS;
+
+ if (aligned_len <= wbuf->avail) {
+ /*
+ * The node is not very large and fits entirely within
+ * write-buffer.
+ */
+ memcpy(wbuf->buf + wbuf->used, buf, len);
+ if (aligned_len > len) {
+ ubifs_assert(c, aligned_len - len < 8);
+ ubifs_pad(c, wbuf->buf + wbuf->used + len, aligned_len - len);
+ }
+
+ if (aligned_len == wbuf->avail) {
+ dbg_io("flush jhead %s wbuf to LEB %d:%d",
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf,
+ wbuf->offs, wbuf->size);
+ if (err)
+ goto out;
+
+ spin_lock(&wbuf->lock);
+ wbuf->offs += wbuf->size;
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ wbuf->size = c->max_write_size;
+ else
+ wbuf->size = c->leb_size - wbuf->offs;
+ wbuf->avail = wbuf->size;
+ wbuf->used = 0;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+ } else {
+ spin_lock(&wbuf->lock);
+ wbuf->avail -= aligned_len;
+ wbuf->used += aligned_len;
+ spin_unlock(&wbuf->lock);
+ }
+
+ goto exit;
+ }
+
+ if (wbuf->used) {
+ /*
+ * The node is large enough and does not fit entirely within
+ * current available space. We have to fill and flush
+ * write-buffer and switch to the next max. write unit.
+ */
+ dbg_io("flush jhead %s wbuf to LEB %d:%d",
+ dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
+ memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail);
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs,
+ wbuf->size);
+ if (err)
+ goto out;
+
+ wbuf->offs += wbuf->size;
+ len -= wbuf->avail;
+ aligned_len -= wbuf->avail;
+ written += wbuf->avail;
+ } else if (wbuf->offs & (c->max_write_size - 1)) {
+ /*
+ * The write-buffer offset is not aligned to
+ * @c->max_write_size and @wbuf->size is less than
+ * @c->max_write_size. Write @wbuf->size bytes to make sure the
+ * following writes are done in optimal @c->max_write_size
+ * chunks.
+ */
+ dbg_io("write %d bytes to LEB %d:%d",
+ wbuf->size, wbuf->lnum, wbuf->offs);
+ err = ubifs_leb_write(c, wbuf->lnum, buf, wbuf->offs,
+ wbuf->size);
+ if (err)
+ goto out;
+
+ wbuf->offs += wbuf->size;
+ len -= wbuf->size;
+ aligned_len -= wbuf->size;
+ written += wbuf->size;
+ }
+
+ /*
+ * The remaining data may take more whole max. write units, so write the
+ * remains multiple to max. write unit size directly to the flash media.
+ * We align node length to 8-byte boundary because we anyway flash wbuf
+ * if the remaining space is less than 8 bytes.
+ */
+ n = aligned_len >> c->max_write_shift;
+ if (n) {
+ int m = n - 1;
+
+ dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum,
+ wbuf->offs);
+
+ if (m) {
+ /* '(n-1)<<c->max_write_shift < len' is always true. */
+ m <<= c->max_write_shift;
+ err = ubifs_leb_write(c, wbuf->lnum, buf + written,
+ wbuf->offs, m);
+ if (err)
+ goto out;
+ wbuf->offs += m;
+ aligned_len -= m;
+ len -= m;
+ written += m;
+ }
+
+ /*
+ * The non-written len of buf may be less than 'n' because
+ * parameter 'len' is not 8 bytes aligned, so here we read
+ * min(len, n) bytes from buf.
+ */
+ n = 1 << c->max_write_shift;
+ memcpy(wbuf->buf, buf + written, min(len, n));
+ if (n > len) {
+ ubifs_assert(c, n - len < 8);
+ ubifs_pad(c, wbuf->buf + len, n - len);
+ }
+
+ err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, n);
+ if (err)
+ goto out;
+ wbuf->offs += n;
+ aligned_len -= n;
+ len -= min(len, n);
+ written += n;
+ }
+
+ spin_lock(&wbuf->lock);
+ if (aligned_len) {
+ /*
+ * And now we have what's left and what does not take whole
+ * max. write unit, so write it to the write-buffer and we are
+ * done.
+ */
+ memcpy(wbuf->buf, buf + written, len);
+ if (aligned_len > len) {
+ ubifs_assert(c, aligned_len - len < 8);
+ ubifs_pad(c, wbuf->buf + len, aligned_len - len);
+ }
+ }
+
+ if (c->leb_size - wbuf->offs >= c->max_write_size)
+ wbuf->size = c->max_write_size;
+ else
+ wbuf->size = c->leb_size - wbuf->offs;
+ wbuf->avail = wbuf->size - aligned_len;
+ wbuf->used = aligned_len;
+ wbuf->next_ino = 0;
+ spin_unlock(&wbuf->lock);
+
+exit:
+ if (wbuf->sync_callback) {
+ int free = c->leb_size - wbuf->offs - wbuf->used;
+
+ err = wbuf->sync_callback(c, wbuf->lnum, free, 0);
+ if (err)
+ goto out;
+ }
+
+ if (wbuf->used)
+ new_wbuf_timer_nolock(c, wbuf);
+
+ return 0;
+
+out:
+ ubifs_err(c, "cannot write %d bytes to LEB %d:%d, error %d",
+ len, wbuf->lnum, wbuf->offs, err);
+ ubifs_dump_node(c, buf, written + len);
+ dump_stack();
+ ubifs_dump_leb(c, wbuf->lnum);
+ return err;
+}
+
+/**
+ * ubifs_write_node_hmac - write node to the media.
+ * @c: UBIFS file-system description object
+ * @buf: the node to write
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ * @hmac_offs: offset of the HMAC within the node
+ *
+ * This function automatically fills node magic number, assigns sequence
+ * number, and calculates node CRC checksum. The length of the @buf buffer has
+ * to be aligned to the minimal I/O unit size. This function automatically
+ * appends padding node and padding bytes if needed. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int hmac_offs)
+{
+ int err, buf_len = ALIGN(len, c->min_io_size);
+
+ dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
+ lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
+ buf_len);
+ ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(c, offs % c->min_io_size == 0 && offs < c->leb_size);
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ ubifs_assert(c, !c->space_fixup);
+
+ if (c->ro_error)
+ return -EROFS;
+
+ err = ubifs_prepare_node_hmac(c, buf, len, hmac_offs, 1);
+ if (err)
+ return err;
+
+ err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
+ if (err)
+ ubifs_dump_node(c, buf, len);
+
+ return err;
+}
+
+/**
+ * ubifs_write_node - write node to the media.
+ * @c: UBIFS file-system description object
+ * @buf: the node to write
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function automatically fills node magic number, assigns sequence
+ * number, and calculates node CRC checksum. The length of the @buf buffer has
+ * to be aligned to the minimal I/O unit size. This function automatically
+ * appends padding node and padding bytes if needed. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs)
+{
+ return ubifs_write_node_hmac(c, buf, len, lnum, offs, -1);
+}
+
+/**
+ * ubifs_read_node_wbuf - read node from the media or write-buffer.
+ * @wbuf: wbuf to check for un-written data
+ * @buf: buffer to read to
+ * @type: node type
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function reads a node of known type and length, checks it and stores
+ * in @buf. If the node partially or fully sits in the write-buffer, this
+ * function takes data from the buffer, otherwise it reads the flash media.
+ * Returns zero in case of success, %-EUCLEAN if CRC mismatched and a negative
+ * error code in case of failure.
+ */
+int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
+ int lnum, int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+ int err, rlen, overlap;
+ struct ubifs_ch *ch = buf;
+
+ dbg_io("LEB %d:%d, %s, length %d, jhead %s", lnum, offs,
+ dbg_ntype(type), len, dbg_jhead(wbuf->jhead));
+ ubifs_assert(c, wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(c, !(offs & 7) && offs < c->leb_size);
+ ubifs_assert(c, type >= 0 && type < UBIFS_NODE_TYPES_CNT);
+
+ spin_lock(&wbuf->lock);
+ overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
+ if (!overlap) {
+ /* We may safely unlock the write-buffer and read the data */
+ spin_unlock(&wbuf->lock);
+ return ubifs_read_node(c, buf, type, len, lnum, offs);
+ }
+
+ /* Don't read under wbuf */
+ rlen = wbuf->offs - offs;
+ if (rlen < 0)
+ rlen = 0;
+
+ /* Copy the rest from the write-buffer */
+ memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
+ spin_unlock(&wbuf->lock);
+
+ if (rlen > 0) {
+ /* Read everything that goes before write-buffer */
+ err = ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
+ if (err && err != -EBADMSG)
+ return err;
+ }
+
+ if (type != ch->node_type) {
+ ubifs_err(c, "bad node type (%d but expected %d)",
+ ch->node_type, type);
+ goto out;
+ }
+
+ err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
+ if (err) {
+ ubifs_err(c, "expected node type %d", type);
+ return err;
+ }
+
+ rlen = le32_to_cpu(ch->len);
+ if (rlen != len) {
+ ubifs_err(c, "bad node length %d, expected %d", rlen, len);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
+ ubifs_dump_node(c, buf, len);
+ dump_stack();
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_node - read node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to read to
+ * @type: node type
+ * @len: node length (not aligned)
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function reads a node of known type and length, checks it and
+ * stores in @buf. Returns zero in case of success, %-EUCLEAN if CRC mismatched
+ * and a negative error code in case of failure.
+ */
+int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
+ int lnum, int offs)
+{
+ int err, l;
+ struct ubifs_ch *ch = buf;
+
+ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
+ ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(c, len >= UBIFS_CH_SZ && offs + len <= c->leb_size);
+ ubifs_assert(c, !(offs & 7) && offs < c->leb_size);
+ ubifs_assert(c, type >= 0 && type < UBIFS_NODE_TYPES_CNT);
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ if (type != ch->node_type) {
+ ubifs_errc(c, "bad node type (%d but expected %d)",
+ ch->node_type, type);
+ goto out;
+ }
+
+ err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
+ if (err) {
+ ubifs_errc(c, "expected node type %d", type);
+ return err;
+ }
+
+ l = le32_to_cpu(ch->len);
+ if (l != len) {
+ ubifs_errc(c, "bad node length %d, expected %d", l, len);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_errc(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
+ offs, ubi_is_mapped(c->ubi, lnum));
+ if (!c->probing) {
+ ubifs_dump_node(c, buf, len);
+ dump_stack();
+ }
+ return -EINVAL;
+}
+
+/**
+ * ubifs_wbuf_init - initialize write-buffer.
+ * @c: UBIFS file-system description object
+ * @wbuf: write-buffer to initialize
+ *
+ * This function initializes write-buffer. Returns zero in case of success
+ * %-ENOMEM in case of failure.
+ */
+int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf)
+{
+ size_t size;
+
+ wbuf->buf = kmalloc(c->max_write_size, GFP_KERNEL);
+ if (!wbuf->buf)
+ return -ENOMEM;
+
+ size = (c->max_write_size / UBIFS_CH_SZ + 1) * sizeof(ino_t);
+ wbuf->inodes = kmalloc(size, GFP_KERNEL);
+ if (!wbuf->inodes) {
+ kfree(wbuf->buf);
+ wbuf->buf = NULL;
+ return -ENOMEM;
+ }
+
+ wbuf->used = 0;
+ wbuf->lnum = wbuf->offs = -1;
+ /*
+ * If the LEB starts at the max. write size aligned address, then
+ * write-buffer size has to be set to @c->max_write_size. Otherwise,
+ * set it to something smaller so that it ends at the closest max.
+ * write size boundary.
+ */
+ size = c->max_write_size - (c->leb_start % c->max_write_size);
+ wbuf->avail = wbuf->size = size;
+ wbuf->sync_callback = NULL;
+ mutex_init(&wbuf->io_mutex);
+ spin_lock_init(&wbuf->lock);
+ wbuf->c = c;
+ wbuf->next_ino = 0;
+
+ hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ wbuf->timer.function = wbuf_timer_callback_nolock;
+ return 0;
+}
+
+/**
+ * ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array.
+ * @wbuf: the write-buffer where to add
+ * @inum: the inode number
+ *
+ * This function adds an inode number to the inode array of the write-buffer.
+ */
+void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum)
+{
+ if (!wbuf->buf)
+ /* NOR flash or something similar */
+ return;
+
+ spin_lock(&wbuf->lock);
+ if (wbuf->used)
+ wbuf->inodes[wbuf->next_ino++] = inum;
+ spin_unlock(&wbuf->lock);
+}
+
+/**
+ * wbuf_has_ino - returns if the wbuf contains data from the inode.
+ * @wbuf: the write-buffer
+ * @inum: the inode number
+ *
+ * This function returns with %1 if the write-buffer contains some data from the
+ * given inode otherwise it returns with %0.
+ */
+static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum)
+{
+ int i, ret = 0;
+
+ spin_lock(&wbuf->lock);
+ for (i = 0; i < wbuf->next_ino; i++)
+ if (inum == wbuf->inodes[i]) {
+ ret = 1;
+ break;
+ }
+ spin_unlock(&wbuf->lock);
+
+ return ret;
+}
+
+/**
+ * ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode.
+ * @c: UBIFS file-system description object
+ * @inode: inode to synchronize
+ *
+ * This function synchronizes write-buffers which contain nodes belonging to
+ * @inode. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode)
+{
+ int i, err = 0;
+
+ for (i = 0; i < c->jhead_cnt; i++) {
+ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf;
+
+ if (i == GCHD)
+ /*
+ * GC head is special, do not look at it. Even if the
+ * head contains something related to this inode, it is
+ * a _copy_ of corresponding on-flash node which sits
+ * somewhere else.
+ */
+ continue;
+
+ if (!wbuf_has_ino(wbuf, inode->i_ino))
+ continue;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ if (wbuf_has_ino(wbuf, inode->i_ino))
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+
+ if (err) {
+ ubifs_ro_mode(c, err);
+ return err;
+ }
+ }
+ return 0;
+}
diff --git a/ubifs-utils/libubifs/journal.c b/ubifs-utils/libubifs/journal.c
new file mode 100644
index 00000000..4590d616
--- /dev/null
+++ b/ubifs-utils/libubifs/journal.c
@@ -0,0 +1,1928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements UBIFS journal.
+ *
+ * The journal consists of 2 parts - the log and bud LEBs. The log has fixed
+ * length and position, while a bud logical eraseblock is any LEB in the main
+ * area. Buds contain file system data - data nodes, inode nodes, etc. The log
+ * contains only references to buds and some other stuff like commit
+ * start node. The idea is that when we commit the journal, we do
+ * not copy the data, the buds just become indexed. Since after the commit the
+ * nodes in bud eraseblocks become leaf nodes of the file system index tree, we
+ * use term "bud". Analogy is obvious, bud eraseblocks contain nodes which will
+ * become leafs in the future.
+ *
+ * The journal is multi-headed because we want to write data to the journal as
+ * optimally as possible. It is nice to have nodes belonging to the same inode
+ * in one LEB, so we may write data owned by different inodes to different
+ * journal heads, although at present only one data head is used.
+ *
+ * For recovery reasons, the base head contains all inode nodes, all directory
+ * entry nodes and all truncate nodes. This means that the other heads contain
+ * only data nodes.
+ *
+ * Bud LEBs may be half-indexed. For example, if the bud was not full at the
+ * time of commit, the bud is retained to continue to be used in the journal,
+ * even though the "front" of the LEB is now indexed. In that case, the log
+ * reference contains the offset where the bud starts for the purposes of the
+ * journal.
+ *
+ * The journal size has to be limited, because the larger is the journal, the
+ * longer it takes to mount UBIFS (scanning the journal) and the more memory it
+ * takes (indexing in the TNC).
+ *
+ * All the journal write operations like 'ubifs_jnl_update()' here, which write
+ * multiple UBIFS nodes to the journal at one go, are atomic with respect to
+ * unclean reboots. Should the unclean reboot happen, the recovery code drops
+ * all the nodes.
+ */
+
+#include "ubifs.h"
+
+/**
+ * zero_ino_node_unused - zero out unused fields of an on-flash inode node.
+ * @ino: the inode to zero out
+ */
+static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
+{
+ memset(ino->padding1, 0, 4);
+ memset(ino->padding2, 0, 26);
+}
+
+/**
+ * zero_dent_node_unused - zero out unused fields of an on-flash directory
+ * entry node.
+ * @dent: the directory entry to zero out
+ */
+static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
+{
+ dent->padding1 = 0;
+}
+
+/**
+ * zero_trun_node_unused - zero out unused fields of an on-flash truncation
+ * node.
+ * @trun: the truncation node to zero out
+ */
+static inline void zero_trun_node_unused(struct ubifs_trun_node *trun)
+{
+ memset(trun->padding, 0, 12);
+}
+
+static void ubifs_add_auth_dirt(struct ubifs_info *c, int lnum)
+{
+ if (ubifs_authenticated(c))
+ ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+}
+
+/**
+ * reserve_space - reserve space in the journal.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head number
+ * @len: node length
+ *
+ * This function reserves space in journal head @head. If the reservation
+ * succeeded, the journal head stays locked and later has to be unlocked using
+ * 'release_head()'. Returns zero in case of success, %-EAGAIN if commit has to
+ * be done, and other negative error codes in case of other failures.
+ */
+static int reserve_space(struct ubifs_info *c, int jhead, int len)
+{
+ int err = 0, err1, retries = 0, avail, lnum, offs, squeeze;
+ struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
+
+ /*
+ * Typically, the base head has smaller nodes written to it, so it is
+ * better to try to allocate space at the ends of eraseblocks. This is
+ * what the squeeze parameter does.
+ */
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ squeeze = (jhead == BASEHD);
+again:
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_unlock;
+ }
+
+ avail = c->leb_size - wbuf->offs - wbuf->used;
+ if (wbuf->lnum != -1 && avail >= len)
+ return 0;
+
+ /*
+ * Write buffer wasn't seek'ed or there is no enough space - look for an
+ * LEB with some empty space.
+ */
+ lnum = ubifs_find_free_space(c, len, &offs, squeeze);
+ if (lnum >= 0)
+ goto out;
+
+ err = lnum;
+ if (err != -ENOSPC)
+ goto out_unlock;
+
+ /*
+ * No free space, we have to run garbage collector to make
+ * some. But the write-buffer mutex has to be unlocked because
+ * GC also takes it.
+ */
+ dbg_jnl("no free space in jhead %s, run GC", dbg_jhead(jhead));
+ mutex_unlock(&wbuf->io_mutex);
+
+ lnum = ubifs_garbage_collect(c, 0);
+ if (lnum < 0) {
+ err = lnum;
+ if (err != -ENOSPC)
+ return err;
+
+ /*
+ * GC could not make a free LEB. But someone else may
+ * have allocated new bud for this journal head,
+ * because we dropped @wbuf->io_mutex, so try once
+ * again.
+ */
+ dbg_jnl("GC couldn't make a free LEB for jhead %s",
+ dbg_jhead(jhead));
+ if (retries++ < 2) {
+ dbg_jnl("retry (%d)", retries);
+ goto again;
+ }
+
+ dbg_jnl("return -ENOSPC");
+ return err;
+ }
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ dbg_jnl("got LEB %d for jhead %s", lnum, dbg_jhead(jhead));
+ avail = c->leb_size - wbuf->offs - wbuf->used;
+
+ if (wbuf->lnum != -1 && avail >= len) {
+ /*
+ * Someone else has switched the journal head and we have
+ * enough space now. This happens when more than one process is
+ * trying to write to the same journal head at the same time.
+ */
+ dbg_jnl("return LEB %d back, already have LEB %d:%d",
+ lnum, wbuf->lnum, wbuf->offs + wbuf->used);
+ err = ubifs_return_leb(c, lnum);
+ if (err)
+ goto out_unlock;
+ return 0;
+ }
+
+ offs = 0;
+
+out:
+ /*
+ * Make sure we synchronize the write-buffer before we add the new bud
+ * to the log. Otherwise we may have a power cut after the log
+ * reference node for the last bud (@lnum) is written but before the
+ * write-buffer data are written to the next-to-last bud
+ * (@wbuf->lnum). And the effect would be that the recovery would see
+ * that there is corruption in the next-to-last bud.
+ */
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ if (err)
+ goto out_return;
+ err = ubifs_add_bud_to_log(c, jhead, lnum, offs);
+ if (err)
+ goto out_return;
+ err = ubifs_wbuf_seek_nolock(wbuf, lnum, offs);
+ if (err)
+ goto out_unlock;
+
+ return 0;
+
+out_unlock:
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+
+out_return:
+ /* An error occurred and the LEB has to be returned to lprops */
+ ubifs_assert(c, err < 0);
+ err1 = ubifs_return_leb(c, lnum);
+ if (err1 && err == -EAGAIN)
+ /*
+ * Return original error code only if it is not %-EAGAIN,
+ * which is not really an error. Otherwise, return the error
+ * code of 'ubifs_return_leb()'.
+ */
+ err = err1;
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+}
+
+static int ubifs_hash_nodes(struct ubifs_info *c, void *node,
+ int len, struct shash_desc *hash)
+{
+ int auth_node_size = ubifs_auth_node_sz(c);
+ int err;
+
+ while (1) {
+ const struct ubifs_ch *ch = node;
+ int nodelen = le32_to_cpu(ch->len);
+
+ ubifs_assert(c, len >= auth_node_size);
+
+ if (len == auth_node_size)
+ break;
+
+ ubifs_assert(c, len > nodelen);
+ ubifs_assert(c, ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));
+
+ err = ubifs_shash_update(c, hash, (void *)node, nodelen);
+ if (err)
+ return err;
+
+ node += ALIGN(nodelen, 8);
+ len -= ALIGN(nodelen, 8);
+ }
+
+ return ubifs_prepare_auth_node(c, node, hash);
+}
+
+/**
+ * write_head - write data to a journal head.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head
+ * @buf: buffer to write
+ * @len: length to write
+ * @lnum: LEB number written is returned here
+ * @offs: offset written is returned here
+ * @sync: non-zero if the write-buffer has to by synchronized
+ *
+ * This function writes data to the reserved space of journal head @jhead.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
+ int *lnum, int *offs, int sync)
+{
+ int err;
+ struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
+
+ ubifs_assert(c, jhead != GCHD);
+
+ *lnum = c->jheads[jhead].wbuf.lnum;
+ *offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used;
+ dbg_jnl("jhead %s, LEB %d:%d, len %d",
+ dbg_jhead(jhead), *lnum, *offs, len);
+
+ if (ubifs_authenticated(c)) {
+ err = ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
+ if (err)
+ return err;
+ }
+
+ err = ubifs_wbuf_write_nolock(wbuf, buf, len);
+ if (err)
+ return err;
+ if (sync)
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ return err;
+}
+
+/**
+ * make_reservation - reserve journal space.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head
+ * @len: how many bytes to reserve
+ *
+ * This function makes space reservation in journal head @jhead. The function
+ * takes the commit lock and locks the journal head, and the caller has to
+ * unlock the head and finish the reservation with 'finish_reservation()'.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ *
+ * Note, the journal head may be unlocked as soon as the data is written, while
+ * the commit lock has to be released after the data has been added to the
+ * TNC.
+ */
+static int make_reservation(struct ubifs_info *c, int jhead, int len)
+{
+ int err, cmt_retries = 0, nospc_retries = 0;
+
+again:
+ down_read(&c->commit_sem);
+ err = reserve_space(c, jhead, len);
+ if (!err)
+ /* c->commit_sem will get released via finish_reservation(). */
+ return 0;
+ up_read(&c->commit_sem);
+
+ if (err == -ENOSPC) {
+ /*
+ * GC could not make any progress. We should try to commit
+ * once because it could make some dirty space and GC would
+ * make progress, so make the error -EAGAIN so that the below
+ * will commit and re-try.
+ */
+ if (nospc_retries++ < 2) {
+ dbg_jnl("no space, retry");
+ err = -EAGAIN;
+ }
+
+ /*
+ * This means that the budgeting is incorrect. We always have
+ * to be able to write to the media, because all operations are
+ * budgeted. Deletions are not budgeted, though, but we reserve
+ * an extra LEB for them.
+ */
+ }
+
+ if (err != -EAGAIN)
+ goto out;
+
+ /*
+ * -EAGAIN means that the journal is full or too large, or the above
+ * code wants to do one commit. Do this and re-try.
+ */
+ if (cmt_retries > 128) {
+ /*
+ * This should not happen unless the journal size limitations
+ * are too tough.
+ */
+ ubifs_err(c, "stuck in space allocation");
+ err = -ENOSPC;
+ goto out;
+ } else if (cmt_retries > 32)
+ ubifs_warn(c, "too many space allocation re-tries (%d)",
+ cmt_retries);
+
+ dbg_jnl("-EAGAIN, commit and retry (retried %d times)",
+ cmt_retries);
+ cmt_retries += 1;
+
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+ goto again;
+
+out:
+ ubifs_err(c, "cannot reserve %d bytes in jhead %d, error %d",
+ len, jhead, err);
+ if (err == -ENOSPC) {
+ /* This are some budgeting problems, print useful information */
+ down_write(&c->commit_sem);
+ dump_stack();
+ ubifs_dump_budg(c, &c->bi);
+ ubifs_dump_lprops(c);
+ cmt_retries = dbg_check_lprops(c);
+ up_write(&c->commit_sem);
+ }
+ return err;
+}
+
+/**
+ * release_head - release a journal head.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head
+ *
+ * This function releases journal head @jhead which was locked by
+ * the 'make_reservation()' function. It has to be called after each successful
+ * 'make_reservation()' invocation.
+ */
+static inline void release_head(struct ubifs_info *c, int jhead)
+{
+ mutex_unlock(&c->jheads[jhead].wbuf.io_mutex);
+}
+
+/**
+ * finish_reservation - finish a reservation.
+ * @c: UBIFS file-system description object
+ *
+ * This function finishes journal space reservation. It must be called after
+ * 'make_reservation()'.
+ */
+static void finish_reservation(struct ubifs_info *c)
+{
+ up_read(&c->commit_sem);
+}
+
+/**
+ * get_dent_type - translate VFS inode mode to UBIFS directory entry type.
+ * @mode: inode mode
+ */
+static int get_dent_type(int mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ return UBIFS_ITYPE_REG;
+ case S_IFDIR:
+ return UBIFS_ITYPE_DIR;
+ case S_IFLNK:
+ return UBIFS_ITYPE_LNK;
+ case S_IFBLK:
+ return UBIFS_ITYPE_BLK;
+ case S_IFCHR:
+ return UBIFS_ITYPE_CHR;
+ case S_IFIFO:
+ return UBIFS_ITYPE_FIFO;
+ case S_IFSOCK:
+ return UBIFS_ITYPE_SOCK;
+ default:
+ BUG();
+ }
+ return 0;
+}
+
+/**
+ * pack_inode - pack an inode node.
+ * @c: UBIFS file-system description object
+ * @ino: buffer in which to pack inode node
+ * @inode: inode to pack
+ * @last: indicates the last node of the group
+ */
+static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino,
+ const struct inode *inode, int last)
+{
+ int data_len = 0, last_reference = !inode->i_nlink;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ino->ch.node_type = UBIFS_INO_NODE;
+ ino_key_init_flash(c, &ino->key, inode->i_ino);
+ ino->creat_sqnum = cpu_to_le64(ui->creat_sqnum);
+ ino->atime_sec = cpu_to_le64(inode_get_atime_sec(inode));
+ ino->atime_nsec = cpu_to_le32(inode_get_atime_nsec(inode));
+ ino->ctime_sec = cpu_to_le64(inode_get_ctime_sec(inode));
+ ino->ctime_nsec = cpu_to_le32(inode_get_ctime_nsec(inode));
+ ino->mtime_sec = cpu_to_le64(inode_get_mtime_sec(inode));
+ ino->mtime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode));
+ ino->uid = cpu_to_le32(i_uid_read(inode));
+ ino->gid = cpu_to_le32(i_gid_read(inode));
+ ino->mode = cpu_to_le32(inode->i_mode);
+ ino->flags = cpu_to_le32(ui->flags);
+ ino->size = cpu_to_le64(ui->ui_size);
+ ino->nlink = cpu_to_le32(inode->i_nlink);
+ ino->compr_type = cpu_to_le16(ui->compr_type);
+ ino->data_len = cpu_to_le32(ui->data_len);
+ ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt);
+ ino->xattr_size = cpu_to_le32(ui->xattr_size);
+ ino->xattr_names = cpu_to_le32(ui->xattr_names);
+ zero_ino_node_unused(ino);
+
+ /*
+ * Drop the attached data if this is a deletion inode, the data is not
+ * needed anymore.
+ */
+ if (!last_reference) {
+ memcpy(ino->data, ui->data, ui->data_len);
+ data_len = ui->data_len;
+ }
+
+ ubifs_prep_grp_node(c, ino, UBIFS_INO_NODE_SZ + data_len, last);
+}
+
+/**
+ * mark_inode_clean - mark UBIFS inode as clean.
+ * @c: UBIFS file-system description object
+ * @ui: UBIFS inode to mark as clean
+ *
+ * This helper function marks UBIFS inode @ui as clean by cleaning the
+ * @ui->dirty flag and releasing its budget. Note, VFS may still treat the
+ * inode as dirty and try to write it back, but 'ubifs_write_inode()' would
+ * just do nothing.
+ */
+static void mark_inode_clean(struct ubifs_info *c, struct ubifs_inode *ui)
+{
+ if (ui->dirty)
+ ubifs_release_dirty_inode_budget(c, ui);
+ ui->dirty = 0;
+}
+
+static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
+{
+ if (c->double_hash)
+ dent->cookie = (__force __le32) get_random_u32();
+ else
+ dent->cookie = 0;
+}
+
+/**
+ * ubifs_jnl_update - update inode.
+ * @c: UBIFS file-system description object
+ * @dir: parent inode or host inode in case of extended attributes
+ * @nm: directory entry name
+ * @inode: inode to update
+ * @deletion: indicates a directory entry deletion i.e unlink or rmdir
+ * @xent: non-zero if the directory entry is an extended attribute entry
+ * @in_orphan: indicates whether the @inode is in orphan list
+ *
+ * This function updates an inode by writing a directory entry (or extended
+ * attribute entry), the inode itself, and the parent directory inode (or the
+ * host inode) to the journal.
+ *
+ * The function writes the host inode @dir last, which is important in case of
+ * extended attributes. Indeed, then we guarantee that if the host inode gets
+ * synchronized (with 'fsync()'), and the write-buffer it sits in gets flushed,
+ * the extended attribute inode gets flushed too. And this is exactly what the
+ * user expects - synchronizing the host inode synchronizes its extended
+ * attributes. Similarly, this guarantees that if @dir is synchronized, its
+ * directory entry corresponding to @nm gets synchronized too.
+ *
+ * If the inode (@inode) or the parent directory (@dir) are synchronous, this
+ * function synchronizes the write-buffer.
+ *
+ * This function marks the @dir and @inode inodes as clean and returns zero on
+ * success. In case of failure, a negative error code is returned.
+ */
+int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
+ const struct fscrypt_name *nm, const struct inode *inode,
+ int deletion, int xent, int in_orphan)
+{
+ int err, dlen, ilen, len, lnum, ino_offs, dent_offs, orphan_added = 0;
+ int aligned_dlen, aligned_ilen, sync = IS_DIRSYNC(dir);
+ int last_reference = !!(deletion && inode->i_nlink == 0);
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ struct ubifs_inode *host_ui = ubifs_inode(dir);
+ struct ubifs_dent_node *dent;
+ struct ubifs_ino_node *ino;
+ union ubifs_key dent_key, ino_key;
+ u8 hash_dent[UBIFS_HASH_ARR_SZ];
+ u8 hash_ino[UBIFS_HASH_ARR_SZ];
+ u8 hash_ino_host[UBIFS_HASH_ARR_SZ];
+
+ ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
+
+ dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
+ ilen = UBIFS_INO_NODE_SZ;
+
+ /*
+ * If the last reference to the inode is being deleted, then there is
+ * no need to attach and write inode data, it is being deleted anyway.
+ * And if the inode is being deleted, no need to synchronize
+ * write-buffer even if the inode is synchronous.
+ */
+ if (!last_reference) {
+ ilen += ui->data_len;
+ sync |= IS_SYNC(inode);
+ }
+
+ aligned_dlen = ALIGN(dlen, 8);
+ aligned_ilen = ALIGN(ilen, 8);
+
+ len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
+ /* Make sure to also account for extended attributes */
+ if (ubifs_authenticated(c))
+ len += ALIGN(host_ui->data_len, 8) + ubifs_auth_node_sz(c);
+ else
+ len += host_ui->data_len;
+
+ dent = kzalloc(len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, len);
+ if (err)
+ goto out_free;
+
+ if (!xent) {
+ dent->ch.node_type = UBIFS_DENT_NODE;
+ if (fname_name(nm) == NULL)
+ dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash);
+ else
+ dent_key_init(c, &dent_key, dir->i_ino, nm);
+ } else {
+ dent->ch.node_type = UBIFS_XENT_NODE;
+ xent_key_init(c, &dent_key, dir->i_ino, nm);
+ }
+
+ key_write(c, &dent_key, dent->key);
+ dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino);
+ dent->type = get_dent_type(inode->i_mode);
+ dent->nlen = cpu_to_le16(fname_len(nm));
+ memcpy(dent->name, fname_name(nm), fname_len(nm));
+ dent->name[fname_len(nm)] = '\0';
+ set_dent_cookie(c, dent);
+
+ zero_dent_node_unused(dent);
+ ubifs_prep_grp_node(c, dent, dlen, 0);
+ err = ubifs_node_calc_hash(c, dent, hash_dent);
+ if (err)
+ goto out_release;
+
+ ino = (void *)dent + aligned_dlen;
+ pack_inode(c, ino, inode, 0);
+ err = ubifs_node_calc_hash(c, ino, hash_ino);
+ if (err)
+ goto out_release;
+
+ ino = (void *)ino + aligned_ilen;
+ pack_inode(c, ino, dir, 1);
+ err = ubifs_node_calc_hash(c, ino, hash_ino_host);
+ if (err)
+ goto out_release;
+
+ if (last_reference && !in_orphan) {
+ err = ubifs_add_orphan(c, inode->i_ino);
+ if (err) {
+ release_head(c, BASEHD);
+ goto out_finish;
+ }
+ ui->del_cmtno = c->cmt_no;
+ orphan_added = 1;
+ }
+
+ err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync);
+ if (err)
+ goto out_release;
+ if (!sync) {
+ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
+
+ ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
+ ubifs_wbuf_add_ino_nolock(wbuf, dir->i_ino);
+ }
+ release_head(c, BASEHD);
+ kfree(dent);
+ ubifs_add_auth_dirt(c, lnum);
+
+ if (deletion) {
+ if (fname_name(nm) == NULL)
+ err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash);
+ else
+ err = ubifs_tnc_remove_nm(c, &dent_key, nm);
+ if (err)
+ goto out_ro;
+ err = ubifs_add_dirt(c, lnum, dlen);
+ } else
+ err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
+ hash_dent, nm);
+ if (err)
+ goto out_ro;
+
+ /*
+ * Note, we do not remove the inode from TNC even if the last reference
+ * to it has just been deleted, because the inode may still be opened.
+ * Instead, the inode has been added to orphan lists and the orphan
+ * subsystem will take further care about it.
+ */
+ ino_key_init(c, &ino_key, inode->i_ino);
+ ino_offs = dent_offs + aligned_dlen;
+ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
+ if (err)
+ goto out_ro;
+
+ ino_key_init(c, &ino_key, dir->i_ino);
+ ino_offs += aligned_ilen;
+ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
+ UBIFS_INO_NODE_SZ + host_ui->data_len, hash_ino_host);
+ if (err)
+ goto out_ro;
+
+ if (in_orphan && inode->i_nlink)
+ ubifs_delete_orphan(c, inode->i_ino);
+
+ finish_reservation(c);
+ spin_lock(&ui->ui_lock);
+ ui->synced_i_size = ui->ui_size;
+ spin_unlock(&ui->ui_lock);
+ if (xent) {
+ spin_lock(&host_ui->ui_lock);
+ host_ui->synced_i_size = host_ui->ui_size;
+ spin_unlock(&host_ui->ui_lock);
+ }
+ mark_inode_clean(c, ui);
+ mark_inode_clean(c, host_ui);
+ return 0;
+
+out_finish:
+ finish_reservation(c);
+out_free:
+ kfree(dent);
+ return err;
+
+out_release:
+ release_head(c, BASEHD);
+ kfree(dent);
+out_ro:
+ ubifs_ro_mode(c, err);
+ if (orphan_added)
+ ubifs_delete_orphan(c, inode->i_ino);
+ finish_reservation(c);
+ return err;
+}
+
+/**
+ * ubifs_jnl_write_data - write a data node to the journal.
+ * @c: UBIFS file-system description object
+ * @inode: inode the data node belongs to
+ * @key: node key
+ * @buf: buffer to write
+ * @len: data length (must not exceed %UBIFS_BLOCK_SIZE)
+ *
+ * This function writes a data node to the journal. Returns %0 if the data node
+ * was successfully written, and a negative error code in case of failure.
+ */
+int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
+ const union ubifs_key *key, const void *buf, int len)
+{
+ struct ubifs_data_node *data;
+ int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
+ int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
+ int write_len;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ bool encrypted = IS_ENCRYPTED(inode);
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
+ (unsigned long)key_inum(c, key), key_block(c, key), len);
+ ubifs_assert(c, len <= UBIFS_BLOCK_SIZE);
+
+ if (encrypted)
+ dlen += UBIFS_CIPHER_BLOCK_SIZE;
+
+ auth_len = ubifs_auth_node_sz(c);
+
+ data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
+ if (!data) {
+ /*
+ * Fall-back to the write reserve buffer. Note, we might be
+ * currently on the memory reclaim path, when the kernel is
+ * trying to free some memory by writing out dirty pages. The
+ * write reserve buffer helps us to guarantee that we are
+ * always able to write the data.
+ */
+ allocated = 0;
+ mutex_lock(&c->write_reserve_mutex);
+ data = c->write_reserve_buf;
+ }
+
+ data->ch.node_type = UBIFS_DATA_NODE;
+ key_write(c, key, &data->key);
+ data->size = cpu_to_le32(len);
+
+ if (!(ui->flags & UBIFS_COMPR_FL))
+ /* Compression is disabled for this inode */
+ compr_type = UBIFS_COMPR_NONE;
+ else
+ compr_type = ui->compr_type;
+
+ out_len = compr_len = dlen - UBIFS_DATA_NODE_SZ;
+ ubifs_compress(c, buf, len, &data->data, &compr_len, &compr_type);
+ ubifs_assert(c, compr_len <= UBIFS_BLOCK_SIZE);
+
+ if (encrypted) {
+ err = ubifs_encrypt(inode, data, compr_len, &out_len, key_block(c, key));
+ if (err)
+ goto out_free;
+
+ } else {
+ data->compr_size = 0;
+ out_len = compr_len;
+ }
+
+ dlen = UBIFS_DATA_NODE_SZ + out_len;
+ if (ubifs_authenticated(c))
+ write_len = ALIGN(dlen, 8) + auth_len;
+ else
+ write_len = dlen;
+
+ data->compr_type = cpu_to_le16(compr_type);
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, DATAHD, write_len);
+ if (err)
+ goto out_free;
+
+ ubifs_prepare_node(c, data, dlen, 0);
+ err = write_head(c, DATAHD, data, write_len, &lnum, &offs, 0);
+ if (err)
+ goto out_release;
+
+ err = ubifs_node_calc_hash(c, data, hash);
+ if (err)
+ goto out_release;
+
+ ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
+ release_head(c, DATAHD);
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
+ if (err)
+ goto out_ro;
+
+ finish_reservation(c);
+ if (!allocated)
+ mutex_unlock(&c->write_reserve_mutex);
+ else
+ kfree(data);
+ return 0;
+
+out_release:
+ release_head(c, DATAHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+out_free:
+ if (!allocated)
+ mutex_unlock(&c->write_reserve_mutex);
+ else
+ kfree(data);
+ return err;
+}
+
+/**
+ * ubifs_jnl_write_inode - flush inode to the journal.
+ * @c: UBIFS file-system description object
+ * @inode: inode to flush
+ *
+ * This function writes inode @inode to the journal. If the inode is
+ * synchronous, it also synchronizes the write-buffer. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
+{
+ int err, lnum, offs;
+ struct ubifs_ino_node *ino, *ino_start;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ int sync = 0, write_len = 0, ilen = UBIFS_INO_NODE_SZ;
+ int last_reference = !inode->i_nlink;
+ int kill_xattrs = ui->xattr_cnt && last_reference;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
+
+ /*
+ * If the inode is being deleted, do not write the attached data. No
+ * need to synchronize the write-buffer either.
+ */
+ if (!last_reference) {
+ ilen += ui->data_len;
+ sync = IS_SYNC(inode);
+ } else if (kill_xattrs) {
+ write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt;
+ }
+
+ if (ubifs_authenticated(c))
+ write_len += ALIGN(ilen, 8) + ubifs_auth_node_sz(c);
+ else
+ write_len += ilen;
+
+ ino_start = ino = kmalloc(write_len, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, write_len);
+ if (err)
+ goto out_free;
+
+ if (kill_xattrs) {
+ union ubifs_key key;
+ struct fscrypt_name nm = {0};
+ struct inode *xino;
+ struct ubifs_dent_node *xent, *pxent = NULL;
+
+ if (ui->xattr_cnt > ubifs_xattr_max_cnt(c)) {
+ err = -EPERM;
+ ubifs_err(c, "Cannot delete inode, it has too much xattrs!");
+ goto out_release;
+ }
+
+ lowest_xent_key(c, &key, inode->i_ino);
+ while (1) {
+ xent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(xent)) {
+ err = PTR_ERR(xent);
+ if (err == -ENOENT)
+ break;
+
+ kfree(pxent);
+ goto out_release;
+ }
+
+ fname_name(&nm) = xent->name;
+ fname_len(&nm) = le16_to_cpu(xent->nlen);
+
+ xino = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum));
+ if (IS_ERR(xino)) {
+ err = PTR_ERR(xino);
+ ubifs_err(c, "dead directory entry '%s', error %d",
+ xent->name, err);
+ ubifs_ro_mode(c, err);
+ kfree(pxent);
+ kfree(xent);
+ goto out_release;
+ }
+ ubifs_assert(c, ubifs_inode(xino)->xattr);
+
+ clear_nlink(xino);
+ pack_inode(c, ino, xino, 0);
+ ino = (void *)ino + UBIFS_INO_NODE_SZ;
+ iput(xino);
+
+ kfree(pxent);
+ pxent = xent;
+ key_read(c, &xent->key, &key);
+ }
+ kfree(pxent);
+ }
+
+ pack_inode(c, ino, inode, 1);
+ err = ubifs_node_calc_hash(c, ino, hash);
+ if (err)
+ goto out_release;
+
+ err = write_head(c, BASEHD, ino_start, write_len, &lnum, &offs, sync);
+ if (err)
+ goto out_release;
+ if (!sync)
+ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
+ inode->i_ino);
+ release_head(c, BASEHD);
+
+ if (last_reference) {
+ err = ubifs_tnc_remove_ino(c, inode->i_ino);
+ if (err)
+ goto out_ro;
+ ubifs_delete_orphan(c, inode->i_ino);
+ err = ubifs_add_dirt(c, lnum, write_len);
+ } else {
+ union ubifs_key key;
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ ino_key_init(c, &key, inode->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
+ }
+ if (err)
+ goto out_ro;
+
+ finish_reservation(c);
+ spin_lock(&ui->ui_lock);
+ ui->synced_i_size = ui->ui_size;
+ spin_unlock(&ui->ui_lock);
+ kfree(ino_start);
+ return 0;
+
+out_release:
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+out_free:
+ kfree(ino_start);
+ return err;
+}
+
+/**
+ * ubifs_jnl_delete_inode - delete an inode.
+ * @c: UBIFS file-system description object
+ * @inode: inode to delete
+ *
+ * This function deletes inode @inode which includes removing it from orphans,
+ * deleting it from TNC and, in some cases, writing a deletion inode to the
+ * journal.
+ *
+ * When regular file inodes are unlinked or a directory inode is removed, the
+ * 'ubifs_jnl_update()' function writes a corresponding deletion inode and
+ * direntry to the media, and adds the inode to orphans. After this, when the
+ * last reference to this inode has been dropped, this function is called. In
+ * general, it has to write one more deletion inode to the media, because if
+ * a commit happened between 'ubifs_jnl_update()' and
+ * 'ubifs_jnl_delete_inode()', the deletion inode is not in the journal
+ * anymore, and in fact it might not be on the flash anymore, because it might
+ * have been garbage-collected already. And for optimization reasons UBIFS does
+ * not read the orphan area if it has been unmounted cleanly, so it would have
+ * no indication in the journal that there is a deleted inode which has to be
+ * removed from TNC.
+ *
+ * However, if there was no commit between 'ubifs_jnl_update()' and
+ * 'ubifs_jnl_delete_inode()', then there is no need to write the deletion
+ * inode to the media for the second time. And this is quite a typical case.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
+{
+ int err;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ubifs_assert(c, inode->i_nlink == 0);
+
+ if (ui->xattr_cnt || ui->del_cmtno != c->cmt_no)
+ /* A commit happened for sure or inode hosts xattrs */
+ return ubifs_jnl_write_inode(c, inode);
+
+ down_read(&c->commit_sem);
+ /*
+ * Check commit number again, because the first test has been done
+ * without @c->commit_sem, so a commit might have happened.
+ */
+ if (ui->del_cmtno != c->cmt_no) {
+ up_read(&c->commit_sem);
+ return ubifs_jnl_write_inode(c, inode);
+ }
+
+ err = ubifs_tnc_remove_ino(c, inode->i_ino);
+ if (err)
+ ubifs_ro_mode(c, err);
+ else
+ ubifs_delete_orphan(c, inode->i_ino);
+ up_read(&c->commit_sem);
+ return err;
+}
+
+/**
+ * ubifs_jnl_xrename - cross rename two directory entries.
+ * @c: UBIFS file-system description object
+ * @fst_dir: parent inode of 1st directory entry to exchange
+ * @fst_inode: 1st inode to exchange
+ * @fst_nm: name of 1st inode to exchange
+ * @snd_dir: parent inode of 2nd directory entry to exchange
+ * @snd_inode: 2nd inode to exchange
+ * @snd_nm: name of 2nd inode to exchange
+ * @sync: non-zero if the write-buffer has to be synchronized
+ *
+ * This function implements the cross rename operation which may involve
+ * writing 2 inodes and 2 directory entries. It marks the written inodes as clean
+ * and returns zero on success. In case of failure, a negative error code is
+ * returned.
+ */
+int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
+ const struct inode *fst_inode,
+ const struct fscrypt_name *fst_nm,
+ const struct inode *snd_dir,
+ const struct inode *snd_inode,
+ const struct fscrypt_name *snd_nm, int sync)
+{
+ union ubifs_key key;
+ struct ubifs_dent_node *dent1, *dent2;
+ int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ;
+ int aligned_dlen1, aligned_dlen2;
+ int twoparents = (fst_dir != snd_dir);
+ void *p;
+ u8 hash_dent1[UBIFS_HASH_ARR_SZ];
+ u8 hash_dent2[UBIFS_HASH_ARR_SZ];
+ u8 hash_p1[UBIFS_HASH_ARR_SZ];
+ u8 hash_p2[UBIFS_HASH_ARR_SZ];
+
+ ubifs_assert(c, ubifs_inode(fst_dir)->data_len == 0);
+ ubifs_assert(c, ubifs_inode(snd_dir)->data_len == 0);
+ ubifs_assert(c, mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex));
+ ubifs_assert(c, mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex));
+
+ dlen1 = UBIFS_DENT_NODE_SZ + fname_len(snd_nm) + 1;
+ dlen2 = UBIFS_DENT_NODE_SZ + fname_len(fst_nm) + 1;
+ aligned_dlen1 = ALIGN(dlen1, 8);
+ aligned_dlen2 = ALIGN(dlen2, 8);
+
+ len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8);
+ if (twoparents)
+ len += plen;
+
+ len += ubifs_auth_node_sz(c);
+
+ dent1 = kzalloc(len, GFP_NOFS);
+ if (!dent1)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, len);
+ if (err)
+ goto out_free;
+
+ /* Make new dent for 1st entry */
+ dent1->ch.node_type = UBIFS_DENT_NODE;
+ dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, snd_nm);
+ dent1->inum = cpu_to_le64(fst_inode->i_ino);
+ dent1->type = get_dent_type(fst_inode->i_mode);
+ dent1->nlen = cpu_to_le16(fname_len(snd_nm));
+ memcpy(dent1->name, fname_name(snd_nm), fname_len(snd_nm));
+ dent1->name[fname_len(snd_nm)] = '\0';
+ set_dent_cookie(c, dent1);
+ zero_dent_node_unused(dent1);
+ ubifs_prep_grp_node(c, dent1, dlen1, 0);
+ err = ubifs_node_calc_hash(c, dent1, hash_dent1);
+ if (err)
+ goto out_release;
+
+ /* Make new dent for 2nd entry */
+ dent2 = (void *)dent1 + aligned_dlen1;
+ dent2->ch.node_type = UBIFS_DENT_NODE;
+ dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, fst_nm);
+ dent2->inum = cpu_to_le64(snd_inode->i_ino);
+ dent2->type = get_dent_type(snd_inode->i_mode);
+ dent2->nlen = cpu_to_le16(fname_len(fst_nm));
+ memcpy(dent2->name, fname_name(fst_nm), fname_len(fst_nm));
+ dent2->name[fname_len(fst_nm)] = '\0';
+ set_dent_cookie(c, dent2);
+ zero_dent_node_unused(dent2);
+ ubifs_prep_grp_node(c, dent2, dlen2, 0);
+ err = ubifs_node_calc_hash(c, dent2, hash_dent2);
+ if (err)
+ goto out_release;
+
+ p = (void *)dent2 + aligned_dlen2;
+ if (!twoparents) {
+ pack_inode(c, p, fst_dir, 1);
+ err = ubifs_node_calc_hash(c, p, hash_p1);
+ if (err)
+ goto out_release;
+ } else {
+ pack_inode(c, p, fst_dir, 0);
+ err = ubifs_node_calc_hash(c, p, hash_p1);
+ if (err)
+ goto out_release;
+ p += ALIGN(plen, 8);
+ pack_inode(c, p, snd_dir, 1);
+ err = ubifs_node_calc_hash(c, p, hash_p2);
+ if (err)
+ goto out_release;
+ }
+
+ err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
+ if (err)
+ goto out_release;
+ if (!sync) {
+ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
+
+ ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino);
+ ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino);
+ }
+ release_head(c, BASEHD);
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
+ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
+ if (err)
+ goto out_ro;
+
+ offs += aligned_dlen1;
+ dent_key_init(c, &key, fst_dir->i_ino, fst_nm);
+ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, fst_nm);
+ if (err)
+ goto out_ro;
+
+ offs += aligned_dlen2;
+
+ ino_key_init(c, &key, fst_dir->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p1);
+ if (err)
+ goto out_ro;
+
+ if (twoparents) {
+ offs += ALIGN(plen, 8);
+ ino_key_init(c, &key, snd_dir->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p2);
+ if (err)
+ goto out_ro;
+ }
+
+ finish_reservation(c);
+
+ mark_inode_clean(c, ubifs_inode(fst_dir));
+ if (twoparents)
+ mark_inode_clean(c, ubifs_inode(snd_dir));
+ kfree(dent1);
+ return 0;
+
+out_release:
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+out_free:
+ kfree(dent1);
+ return err;
+}
+
+/**
+ * ubifs_jnl_rename - rename a directory entry.
+ * @c: UBIFS file-system description object
+ * @old_dir: parent inode of directory entry to rename
+ * @old_inode: directory entry's inode to rename
+ * @old_nm: name of the old directory entry to rename
+ * @new_dir: parent inode of directory entry to rename
+ * @new_inode: new directory entry's inode (or directory entry's inode to
+ * replace)
+ * @new_nm: new name of the new directory entry
+ * @whiteout: whiteout inode
+ * @sync: non-zero if the write-buffer has to be synchronized
+ * @delete_orphan: indicates an orphan entry deletion for @whiteout
+ *
+ * This function implements the re-name operation which may involve writing up
+ * to 4 inodes(new inode, whiteout inode, old and new parent directory inodes)
+ * and 2 directory entries. It marks the written inodes as clean and returns
+ * zero on success. In case of failure, a negative error code is returned.
+ */
+int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
+ const struct inode *old_inode,
+ const struct fscrypt_name *old_nm,
+ const struct inode *new_dir,
+ const struct inode *new_inode,
+ const struct fscrypt_name *new_nm,
+ const struct inode *whiteout, int sync, int delete_orphan)
+{
+ void *p;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *dent2;
+ int err, dlen1, dlen2, ilen, wlen, lnum, offs, len, orphan_added = 0;
+ int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ;
+ int last_reference = !!(new_inode && new_inode->i_nlink == 0);
+ int move = (old_dir != new_dir);
+ struct ubifs_inode *new_ui, *whiteout_ui;
+ u8 hash_old_dir[UBIFS_HASH_ARR_SZ];
+ u8 hash_new_dir[UBIFS_HASH_ARR_SZ];
+ u8 hash_new_inode[UBIFS_HASH_ARR_SZ];
+ u8 hash_whiteout_inode[UBIFS_HASH_ARR_SZ];
+ u8 hash_dent1[UBIFS_HASH_ARR_SZ];
+ u8 hash_dent2[UBIFS_HASH_ARR_SZ];
+
+ ubifs_assert(c, ubifs_inode(old_dir)->data_len == 0);
+ ubifs_assert(c, ubifs_inode(new_dir)->data_len == 0);
+ ubifs_assert(c, mutex_is_locked(&ubifs_inode(old_dir)->ui_mutex));
+ ubifs_assert(c, mutex_is_locked(&ubifs_inode(new_dir)->ui_mutex));
+
+ dlen1 = UBIFS_DENT_NODE_SZ + fname_len(new_nm) + 1;
+ dlen2 = UBIFS_DENT_NODE_SZ + fname_len(old_nm) + 1;
+ if (new_inode) {
+ new_ui = ubifs_inode(new_inode);
+ ubifs_assert(c, mutex_is_locked(&new_ui->ui_mutex));
+ ilen = UBIFS_INO_NODE_SZ;
+ if (!last_reference)
+ ilen += new_ui->data_len;
+ } else
+ ilen = 0;
+
+ if (whiteout) {
+ whiteout_ui = ubifs_inode(whiteout);
+ ubifs_assert(c, mutex_is_locked(&whiteout_ui->ui_mutex));
+ ubifs_assert(c, whiteout->i_nlink == 1);
+ ubifs_assert(c, !whiteout_ui->dirty);
+ wlen = UBIFS_INO_NODE_SZ;
+ wlen += whiteout_ui->data_len;
+ } else
+ wlen = 0;
+
+ aligned_dlen1 = ALIGN(dlen1, 8);
+ aligned_dlen2 = ALIGN(dlen2, 8);
+ len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) +
+ ALIGN(wlen, 8) + ALIGN(plen, 8);
+ if (move)
+ len += plen;
+
+ len += ubifs_auth_node_sz(c);
+
+ dent = kzalloc(len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, len);
+ if (err)
+ goto out_free;
+
+ /* Make new dent */
+ dent->ch.node_type = UBIFS_DENT_NODE;
+ dent_key_init_flash(c, &dent->key, new_dir->i_ino, new_nm);
+ dent->inum = cpu_to_le64(old_inode->i_ino);
+ dent->type = get_dent_type(old_inode->i_mode);
+ dent->nlen = cpu_to_le16(fname_len(new_nm));
+ memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
+ dent->name[fname_len(new_nm)] = '\0';
+ set_dent_cookie(c, dent);
+ zero_dent_node_unused(dent);
+ ubifs_prep_grp_node(c, dent, dlen1, 0);
+ err = ubifs_node_calc_hash(c, dent, hash_dent1);
+ if (err)
+ goto out_release;
+
+ dent2 = (void *)dent + aligned_dlen1;
+ dent2->ch.node_type = UBIFS_DENT_NODE;
+ dent_key_init_flash(c, &dent2->key, old_dir->i_ino, old_nm);
+
+ if (whiteout) {
+ dent2->inum = cpu_to_le64(whiteout->i_ino);
+ dent2->type = get_dent_type(whiteout->i_mode);
+ } else {
+ /* Make deletion dent */
+ dent2->inum = 0;
+ dent2->type = DT_UNKNOWN;
+ }
+ dent2->nlen = cpu_to_le16(fname_len(old_nm));
+ memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
+ dent2->name[fname_len(old_nm)] = '\0';
+ set_dent_cookie(c, dent2);
+ zero_dent_node_unused(dent2);
+ ubifs_prep_grp_node(c, dent2, dlen2, 0);
+ err = ubifs_node_calc_hash(c, dent2, hash_dent2);
+ if (err)
+ goto out_release;
+
+ p = (void *)dent2 + aligned_dlen2;
+ if (new_inode) {
+ pack_inode(c, p, new_inode, 0);
+ err = ubifs_node_calc_hash(c, p, hash_new_inode);
+ if (err)
+ goto out_release;
+
+ p += ALIGN(ilen, 8);
+ }
+
+ if (whiteout) {
+ pack_inode(c, p, whiteout, 0);
+ err = ubifs_node_calc_hash(c, p, hash_whiteout_inode);
+ if (err)
+ goto out_release;
+
+ p += ALIGN(wlen, 8);
+ }
+
+ if (!move) {
+ pack_inode(c, p, old_dir, 1);
+ err = ubifs_node_calc_hash(c, p, hash_old_dir);
+ if (err)
+ goto out_release;
+ } else {
+ pack_inode(c, p, old_dir, 0);
+ err = ubifs_node_calc_hash(c, p, hash_old_dir);
+ if (err)
+ goto out_release;
+
+ p += ALIGN(plen, 8);
+ pack_inode(c, p, new_dir, 1);
+ err = ubifs_node_calc_hash(c, p, hash_new_dir);
+ if (err)
+ goto out_release;
+ }
+
+ if (last_reference) {
+ err = ubifs_add_orphan(c, new_inode->i_ino);
+ if (err) {
+ release_head(c, BASEHD);
+ goto out_finish;
+ }
+ new_ui->del_cmtno = c->cmt_no;
+ orphan_added = 1;
+ }
+
+ err = write_head(c, BASEHD, dent, len, &lnum, &offs, sync);
+ if (err)
+ goto out_release;
+ if (!sync) {
+ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
+
+ ubifs_wbuf_add_ino_nolock(wbuf, new_dir->i_ino);
+ ubifs_wbuf_add_ino_nolock(wbuf, old_dir->i_ino);
+ if (new_inode)
+ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
+ new_inode->i_ino);
+ if (whiteout)
+ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
+ whiteout->i_ino);
+ }
+ release_head(c, BASEHD);
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ dent_key_init(c, &key, new_dir->i_ino, new_nm);
+ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
+ if (err)
+ goto out_ro;
+
+ offs += aligned_dlen1;
+ if (whiteout) {
+ dent_key_init(c, &key, old_dir->i_ino, old_nm);
+ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
+ if (err)
+ goto out_ro;
+ } else {
+ err = ubifs_add_dirt(c, lnum, dlen2);
+ if (err)
+ goto out_ro;
+
+ dent_key_init(c, &key, old_dir->i_ino, old_nm);
+ err = ubifs_tnc_remove_nm(c, &key, old_nm);
+ if (err)
+ goto out_ro;
+ }
+
+ offs += aligned_dlen2;
+ if (new_inode) {
+ ino_key_init(c, &key, new_inode->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash_new_inode);
+ if (err)
+ goto out_ro;
+ offs += ALIGN(ilen, 8);
+ }
+
+ if (whiteout) {
+ ino_key_init(c, &key, whiteout->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, wlen,
+ hash_whiteout_inode);
+ if (err)
+ goto out_ro;
+ offs += ALIGN(wlen, 8);
+ }
+
+ ino_key_init(c, &key, old_dir->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
+ if (err)
+ goto out_ro;
+
+ if (move) {
+ offs += ALIGN(plen, 8);
+ ino_key_init(c, &key, new_dir->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_new_dir);
+ if (err)
+ goto out_ro;
+ }
+
+ if (delete_orphan)
+ ubifs_delete_orphan(c, whiteout->i_ino);
+
+ finish_reservation(c);
+ if (new_inode) {
+ mark_inode_clean(c, new_ui);
+ spin_lock(&new_ui->ui_lock);
+ new_ui->synced_i_size = new_ui->ui_size;
+ spin_unlock(&new_ui->ui_lock);
+ }
+ /*
+ * No need to mark whiteout inode clean.
+ * Whiteout doesn't have non-zero size, no need to update
+ * synced_i_size for whiteout_ui.
+ */
+ mark_inode_clean(c, ubifs_inode(old_dir));
+ if (move)
+ mark_inode_clean(c, ubifs_inode(new_dir));
+ kfree(dent);
+ return 0;
+
+out_release:
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ if (orphan_added)
+ ubifs_delete_orphan(c, new_inode->i_ino);
+out_finish:
+ finish_reservation(c);
+out_free:
+ kfree(dent);
+ return err;
+}
+
+/**
+ * truncate_data_node - re-compress/encrypt a truncated data node.
+ * @c: UBIFS file-system description object
+ * @inode: inode which refers to the data node
+ * @block: data block number
+ * @dn: data node to re-compress
+ * @new_len: new length
+ * @dn_size: size of the data node @dn in memory
+ *
+ * This function is used when an inode is truncated and the last data node of
+ * the inode has to be re-compressed/encrypted and re-written.
+ */
+static int truncate_data_node(const struct ubifs_info *c, const struct inode *inode,
+ unsigned int block, struct ubifs_data_node *dn,
+ int *new_len, int dn_size)
+{
+ void *buf;
+ int err, dlen, compr_type, out_len, data_size;
+
+ out_len = le32_to_cpu(dn->size);
+ buf = kmalloc_array(out_len, WORST_COMPR_FACTOR, GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+
+ dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+ data_size = dn_size - UBIFS_DATA_NODE_SZ;
+ compr_type = le16_to_cpu(dn->compr_type);
+
+ if (IS_ENCRYPTED(inode)) {
+ err = ubifs_decrypt(inode, dn, &dlen, block);
+ if (err)
+ goto out;
+ }
+
+ if (compr_type == UBIFS_COMPR_NONE) {
+ out_len = *new_len;
+ } else {
+ err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
+ if (err)
+ goto out;
+
+ ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
+ }
+
+ if (IS_ENCRYPTED(inode)) {
+ err = ubifs_encrypt(inode, dn, out_len, &data_size, block);
+ if (err)
+ goto out;
+
+ out_len = data_size;
+ } else {
+ dn->compr_size = 0;
+ }
+
+ ubifs_assert(c, out_len <= UBIFS_BLOCK_SIZE);
+ dn->compr_type = cpu_to_le16(compr_type);
+ dn->size = cpu_to_le32(*new_len);
+ *new_len = UBIFS_DATA_NODE_SZ + out_len;
+ err = 0;
+out:
+ kfree(buf);
+ return err;
+}
+
+/**
+ * ubifs_jnl_truncate - update the journal for a truncation.
+ * @c: UBIFS file-system description object
+ * @inode: inode to truncate
+ * @old_size: old size
+ * @new_size: new size
+ *
+ * When the size of a file decreases due to truncation, a truncation node is
+ * written, the journal tree is updated, and the last data block is re-written
+ * if it has been affected. The inode is also updated in order to synchronize
+ * the new inode size.
+ *
+ * This function marks the inode as clean and returns zero on success. In case
+ * of failure, a negative error code is returned.
+ */
+int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
+ loff_t old_size, loff_t new_size)
+{
+ union ubifs_key key, to_key;
+ struct ubifs_ino_node *ino;
+ struct ubifs_trun_node *trun;
+ struct ubifs_data_node *dn;
+ int err, dlen, len, lnum, offs, bit, sz, sync = IS_SYNC(inode);
+ int dn_size;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ ino_t inum = inode->i_ino;
+ unsigned int blk;
+ u8 hash_ino[UBIFS_HASH_ARR_SZ];
+ u8 hash_dn[UBIFS_HASH_ARR_SZ];
+
+ dbg_jnl("ino %lu, size %lld -> %lld",
+ (unsigned long)inum, old_size, new_size);
+ ubifs_assert(c, !ui->data_len);
+ ubifs_assert(c, S_ISREG(inode->i_mode));
+ ubifs_assert(c, mutex_is_locked(&ui->ui_mutex));
+
+ dn_size = COMPRESSED_DATA_NODE_BUF_SZ;
+
+ if (IS_ENCRYPTED(inode))
+ dn_size += UBIFS_CIPHER_BLOCK_SIZE;
+
+ sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
+ dn_size + ubifs_auth_node_sz(c);
+
+ ino = kmalloc(sz, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ trun = (void *)ino + UBIFS_INO_NODE_SZ;
+ trun->ch.node_type = UBIFS_TRUN_NODE;
+ trun->inum = cpu_to_le32(inum);
+ trun->old_size = cpu_to_le64(old_size);
+ trun->new_size = cpu_to_le64(new_size);
+ zero_trun_node_unused(trun);
+
+ dlen = new_size & (UBIFS_BLOCK_SIZE - 1);
+ if (dlen) {
+ /* Get last data block so it can be truncated */
+ dn = (void *)trun + UBIFS_TRUN_NODE_SZ;
+ blk = new_size >> UBIFS_BLOCK_SHIFT;
+ data_key_init(c, &key, inum, blk);
+ dbg_jnlk(&key, "last block key ");
+ err = ubifs_tnc_lookup(c, &key, dn);
+ if (err == -ENOENT)
+ dlen = 0; /* Not found (so it is a hole) */
+ else if (err)
+ goto out_free;
+ else {
+ int dn_len = le32_to_cpu(dn->size);
+
+ if (dn_len <= 0 || dn_len > UBIFS_BLOCK_SIZE) {
+ ubifs_err(c, "bad data node (block %u, inode %lu)",
+ blk, inode->i_ino);
+ ubifs_dump_node(c, dn, dn_size);
+ err = -EUCLEAN;
+ goto out_free;
+ }
+
+ if (dn_len <= dlen)
+ dlen = 0; /* Nothing to do */
+ else {
+ err = truncate_data_node(c, inode, blk, dn,
+ &dlen, dn_size);
+ if (err)
+ goto out_free;
+ }
+ }
+ }
+
+ /* Must make reservation before allocating sequence numbers */
+ len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
+
+ if (ubifs_authenticated(c))
+ len += ALIGN(dlen, 8) + ubifs_auth_node_sz(c);
+ else
+ len += dlen;
+
+ err = make_reservation(c, BASEHD, len);
+ if (err)
+ goto out_free;
+
+ pack_inode(c, ino, inode, 0);
+ err = ubifs_node_calc_hash(c, ino, hash_ino);
+ if (err)
+ goto out_release;
+
+ ubifs_prep_grp_node(c, trun, UBIFS_TRUN_NODE_SZ, dlen ? 0 : 1);
+ if (dlen) {
+ ubifs_prep_grp_node(c, dn, dlen, 1);
+ err = ubifs_node_calc_hash(c, dn, hash_dn);
+ if (err)
+ goto out_release;
+ }
+
+ err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
+ if (err)
+ goto out_release;
+ if (!sync)
+ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
+ release_head(c, BASEHD);
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ if (dlen) {
+ sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
+ err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
+ if (err)
+ goto out_ro;
+ }
+
+ ino_key_init(c, &key, inum);
+ err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ, hash_ino);
+ if (err)
+ goto out_ro;
+
+ err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ);
+ if (err)
+ goto out_ro;
+
+ bit = new_size & (UBIFS_BLOCK_SIZE - 1);
+ blk = (new_size >> UBIFS_BLOCK_SHIFT) + (bit ? 1 : 0);
+ data_key_init(c, &key, inum, blk);
+
+ bit = old_size & (UBIFS_BLOCK_SIZE - 1);
+ blk = (old_size >> UBIFS_BLOCK_SHIFT) - (bit ? 0 : 1);
+ data_key_init(c, &to_key, inum, blk);
+
+ err = ubifs_tnc_remove_range(c, &key, &to_key);
+ if (err)
+ goto out_ro;
+
+ finish_reservation(c);
+ spin_lock(&ui->ui_lock);
+ ui->synced_i_size = ui->ui_size;
+ spin_unlock(&ui->ui_lock);
+ mark_inode_clean(c, ui);
+ kfree(ino);
+ return 0;
+
+out_release:
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+out_free:
+ kfree(ino);
+ return err;
+}
+
+
+/**
+ * ubifs_jnl_delete_xattr - delete an extended attribute.
+ * @c: UBIFS file-system description object
+ * @host: host inode
+ * @inode: extended attribute inode
+ * @nm: extended attribute entry name
+ *
+ * This function delete an extended attribute which is very similar to
+ * un-linking regular files - it writes a deletion xentry, a deletion inode and
+ * updates the target inode. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
+ const struct inode *inode,
+ const struct fscrypt_name *nm)
+{
+ int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, write_len;
+ struct ubifs_dent_node *xent;
+ struct ubifs_ino_node *ino;
+ union ubifs_key xent_key, key1, key2;
+ int sync = IS_DIRSYNC(host);
+ struct ubifs_inode *host_ui = ubifs_inode(host);
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ ubifs_assert(c, inode->i_nlink == 0);
+ ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
+
+ /*
+ * Since we are deleting the inode, we do not bother to attach any data
+ * to it and assume its length is %UBIFS_INO_NODE_SZ.
+ */
+ xlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
+ aligned_xlen = ALIGN(xlen, 8);
+ hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
+ len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
+
+ write_len = len + ubifs_auth_node_sz(c);
+
+ xent = kzalloc(write_len, GFP_NOFS);
+ if (!xent)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, write_len);
+ if (err) {
+ kfree(xent);
+ return err;
+ }
+
+ xent->ch.node_type = UBIFS_XENT_NODE;
+ xent_key_init(c, &xent_key, host->i_ino, nm);
+ key_write(c, &xent_key, xent->key);
+ xent->inum = 0;
+ xent->type = get_dent_type(inode->i_mode);
+ xent->nlen = cpu_to_le16(fname_len(nm));
+ memcpy(xent->name, fname_name(nm), fname_len(nm));
+ xent->name[fname_len(nm)] = '\0';
+ zero_dent_node_unused(xent);
+ ubifs_prep_grp_node(c, xent, xlen, 0);
+
+ ino = (void *)xent + aligned_xlen;
+ pack_inode(c, ino, inode, 0);
+ ino = (void *)ino + UBIFS_INO_NODE_SZ;
+ pack_inode(c, ino, host, 1);
+ err = ubifs_node_calc_hash(c, ino, hash);
+ if (err)
+ goto out_release;
+
+ err = write_head(c, BASEHD, xent, write_len, &lnum, &xent_offs, sync);
+ if (!sync && !err)
+ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
+ release_head(c, BASEHD);
+
+ ubifs_add_auth_dirt(c, lnum);
+ kfree(xent);
+ if (err)
+ goto out_ro;
+
+ /* Remove the extended attribute entry from TNC */
+ err = ubifs_tnc_remove_nm(c, &xent_key, nm);
+ if (err)
+ goto out_ro;
+ err = ubifs_add_dirt(c, lnum, xlen);
+ if (err)
+ goto out_ro;
+
+ /*
+ * Remove all nodes belonging to the extended attribute inode from TNC.
+ * Well, there actually must be only one node - the inode itself.
+ */
+ lowest_ino_key(c, &key1, inode->i_ino);
+ highest_ino_key(c, &key2, inode->i_ino);
+ err = ubifs_tnc_remove_range(c, &key1, &key2);
+ if (err)
+ goto out_ro;
+ err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ);
+ if (err)
+ goto out_ro;
+
+ /* And update TNC with the new host inode position */
+ ino_key_init(c, &key1, host->i_ino);
+ err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen, hash);
+ if (err)
+ goto out_ro;
+
+ finish_reservation(c);
+ spin_lock(&host_ui->ui_lock);
+ host_ui->synced_i_size = host_ui->ui_size;
+ spin_unlock(&host_ui->ui_lock);
+ mark_inode_clean(c, host_ui);
+ return 0;
+
+out_release:
+ kfree(xent);
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+ return err;
+}
+
+/**
+ * ubifs_jnl_change_xattr - change an extended attribute.
+ * @c: UBIFS file-system description object
+ * @inode: extended attribute inode
+ * @host: host inode
+ *
+ * This function writes the updated version of an extended attribute inode and
+ * the host inode to the journal (to the base head). The host inode is written
+ * after the extended attribute inode in order to guarantee that the extended
+ * attribute will be flushed when the inode is synchronized by 'fsync()' and
+ * consequently, the write-buffer is synchronized. This function returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
+ const struct inode *host)
+{
+ int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
+ struct ubifs_inode *host_ui = ubifs_inode(host);
+ struct ubifs_ino_node *ino;
+ union ubifs_key key;
+ int sync = IS_DIRSYNC(host);
+ u8 hash_host[UBIFS_HASH_ARR_SZ];
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ dbg_jnl("ino %lu, ino %lu", host->i_ino, inode->i_ino);
+ ubifs_assert(c, inode->i_nlink > 0);
+ ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
+
+ len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
+ len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
+ aligned_len1 = ALIGN(len1, 8);
+ aligned_len = aligned_len1 + ALIGN(len2, 8);
+
+ aligned_len += ubifs_auth_node_sz(c);
+
+ ino = kzalloc(aligned_len, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, aligned_len);
+ if (err)
+ goto out_free;
+
+ pack_inode(c, ino, host, 0);
+ err = ubifs_node_calc_hash(c, ino, hash_host);
+ if (err)
+ goto out_release;
+ pack_inode(c, (void *)ino + aligned_len1, inode, 1);
+ err = ubifs_node_calc_hash(c, (void *)ino + aligned_len1, hash);
+ if (err)
+ goto out_release;
+
+ err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0);
+ if (!sync && !err) {
+ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
+
+ ubifs_wbuf_add_ino_nolock(wbuf, host->i_ino);
+ ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
+ }
+ release_head(c, BASEHD);
+ if (err)
+ goto out_ro;
+
+ ubifs_add_auth_dirt(c, lnum);
+
+ ino_key_init(c, &key, host->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
+ if (err)
+ goto out_ro;
+
+ ino_key_init(c, &key, inode->i_ino);
+ err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2, hash);
+ if (err)
+ goto out_ro;
+
+ finish_reservation(c);
+ spin_lock(&host_ui->ui_lock);
+ host_ui->synced_i_size = host_ui->ui_size;
+ spin_unlock(&host_ui->ui_lock);
+ mark_inode_clean(c, host_ui);
+ kfree(ino);
+ return 0;
+
+out_release:
+ release_head(c, BASEHD);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+out_free:
+ kfree(ino);
+ return err;
+}
+
diff --git a/ubifs-utils/libubifs/key.h b/ubifs-utils/libubifs/key.h
new file mode 100644
index 00000000..8142d9d6
--- /dev/null
+++ b/ubifs-utils/libubifs/key.h
@@ -0,0 +1,543 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This header contains various key-related definitions and helper function.
+ * UBIFS allows several key schemes, so we access key fields only via these
+ * helpers. At the moment only one key scheme is supported.
+ *
+ * Simple key scheme
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * Keys are 64-bits long. First 32-bits are inode number (parent inode number
+ * in case of direntry key). Next 3 bits are node type. The last 29 bits are
+ * 4KiB offset in case of inode node, and direntry hash in case of a direntry
+ * node. We use "r5" hash borrowed from reiserfs.
+ */
+
+/*
+ * Lot's of the key helpers require a struct ubifs_info *c as the first parameter.
+ * But we are not using it at all currently. That's designed for future extensions of
+ * different c->key_format. But right now, there is only one key type, UBIFS_SIMPLE_KEY_FMT.
+ */
+
+#ifndef __UBIFS_KEY_H__
+#define __UBIFS_KEY_H__
+
+/**
+ * key_mask_hash - mask a valid hash value.
+ * @val: value to be masked
+ *
+ * We use hash values as offset in directories, so values %0 and %1 are
+ * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This
+ * function makes sure the reserved values are not used.
+ */
+static inline uint32_t key_mask_hash(uint32_t hash)
+{
+ hash &= UBIFS_S_KEY_HASH_MASK;
+ if (unlikely(hash <= 2))
+ hash += 3;
+ return hash;
+}
+
+/**
+ * key_r5_hash - R5 hash function (borrowed from reiserfs).
+ * @s: direntry name
+ * @len: name length
+ */
+static inline uint32_t key_r5_hash(const char *s, int len)
+{
+ uint32_t a = 0;
+ const signed char *str = (const signed char *)s;
+
+ while (len--) {
+ a += *str << 4;
+ a += *str >> 4;
+ a *= 11;
+ str++;
+ }
+
+ return key_mask_hash(a);
+}
+
+/**
+ * key_test_hash - testing hash function.
+ * @str: direntry name
+ * @len: name length
+ */
+static inline uint32_t key_test_hash(const char *str, int len)
+{
+ uint32_t a = 0;
+
+ len = min_t(uint32_t, len, 4);
+ memcpy(&a, str, len);
+ return key_mask_hash(a);
+}
+
+/**
+ * ino_key_init - initialize inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void ino_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * ino_key_init_flash - initialize on-flash inode key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: inode number
+ */
+static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum)
+{
+ union ubifs_key *key = k;
+
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_ino_key - get the lowest possible inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void lowest_ino_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = 0;
+}
+
+/**
+ * highest_ino_key - get the highest possible inode key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void highest_ino_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = 0xffffffff;
+}
+
+/**
+ * dent_key_init - initialize directory entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: parent inode number
+ * @nm: direntry name and length. Not a string when encrypted!
+ */
+static inline void dent_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ const struct fscrypt_name *nm)
+{
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
+
+ ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * dent_key_init_hash - initialize directory entry key without re-calculating
+ * hash function.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: parent inode number
+ * @hash: direntry name hash
+ */
+static inline void dent_key_init_hash(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ uint32_t hash)
+{
+ ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * dent_key_init_flash - initialize on-flash directory entry key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: parent inode number
+ * @nm: direntry name and length
+ */
+static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum,
+ const struct fscrypt_name *nm)
+{
+ union ubifs_key *key = k;
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
+
+ ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(hash |
+ (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_dent_key - get the lowest possible directory entry key.
+ * @c: UBIFS file-system description object
+ * @key: where to store the lowest key
+ * @inum: parent inode number
+ */
+static inline void lowest_dent_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
+}
+
+/**
+ * xent_key_init - initialize extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ const struct fscrypt_name *nm)
+{
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
+
+ ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
+ * xent_key_init_flash - initialize on-flash extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @k: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
+ ino_t inum, const struct fscrypt_name *nm)
+{
+ union ubifs_key *key = k;
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
+
+ ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->j32[0] = cpu_to_le32(inum);
+ key->j32[1] = cpu_to_le32(hash |
+ (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
+ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * lowest_xent_key - get the lowest possible extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: where to store the lowest key
+ * @inum: host inode number
+ */
+static inline void lowest_xent_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS;
+}
+
+/**
+ * data_key_init - initialize data key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ * @block: block number
+ */
+static inline void data_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ unsigned int block)
+{
+ ubifs_assert(c, !(block & ~UBIFS_S_KEY_BLOCK_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
+}
+
+/**
+ * highest_data_key - get the highest possible data key for an inode.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ */
+static inline void highest_data_key(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
+}
+
+/**
+ * trun_key_init - initialize truncation node key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: inode number
+ *
+ * Note, UBIFS does not have truncation keys on the media and this function is
+ * only used for purposes of replay.
+ */
+static inline void trun_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum)
+{
+ key->u32[0] = inum;
+ key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * invalid_key_init - initialize invalid node key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ *
+ * This is a helper function which marks a @key object as invalid.
+ */
+static inline void invalid_key_init(const struct ubifs_info *c,
+ union ubifs_key *key)
+{
+ key->u32[0] = 0xDEADBEAF;
+ key->u32[1] = UBIFS_INVALID_KEY;
+}
+
+/**
+ * key_type - get key type.
+ * @c: UBIFS file-system description object
+ * @key: key to get type of
+ */
+static inline int key_type(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * key_type_flash - get type of a on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: key to get type of
+ */
+static inline int key_type_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) >> UBIFS_S_KEY_BLOCK_BITS;
+}
+
+/**
+ * key_inum - fetch inode number from key.
+ * @c: UBIFS file-system description object
+ * @k: key to fetch inode number from
+ */
+static inline ino_t key_inum(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return key->u32[0];
+}
+
+/**
+ * key_inum_flash - fetch inode number from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: key to fetch inode number from
+ */
+static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[0]);
+}
+
+/**
+ * key_hash - get directory entry hash.
+ * @c: UBIFS file-system description object
+ * @key: the key to get hash from
+ */
+static inline uint32_t key_hash(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
+}
+
+/**
+ * key_hash_flash - get directory entry hash from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: the key to get hash from
+ */
+static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK;
+}
+
+/**
+ * key_block - get data block number.
+ * @c: UBIFS file-system description object
+ * @key: the key to get the block number from
+ */
+static inline unsigned int key_block(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
+}
+
+/**
+ * key_block_flash - get data block number from an on-flash formatted key.
+ * @c: UBIFS file-system description object
+ * @k: the key to get the block number from
+ */
+static inline unsigned int key_block_flash(const struct ubifs_info *c,
+ const void *k)
+{
+ const union ubifs_key *key = k;
+
+ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_BLOCK_MASK;
+}
+
+/**
+ * key_read - transform a key to in-memory format.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_read(const struct ubifs_info *c, const void *from,
+ union ubifs_key *to)
+{
+ const union ubifs_key *f = from;
+
+ to->u32[0] = le32_to_cpu(f->j32[0]);
+ to->u32[1] = le32_to_cpu(f->j32[1]);
+}
+
+/**
+ * key_write - transform a key from in-memory format.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_write(const struct ubifs_info *c,
+ const union ubifs_key *from, void *to)
+{
+ union ubifs_key *t = to;
+
+ t->j32[0] = cpu_to_le32(from->u32[0]);
+ t->j32[1] = cpu_to_le32(from->u32[1]);
+ memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+}
+
+/**
+ * key_write_idx - transform a key from in-memory format for the index.
+ * @c: UBIFS file-system description object
+ * @from: the key to transform
+ * @to: the key to store the result
+ */
+static inline void key_write_idx(const struct ubifs_info *c,
+ const union ubifs_key *from, void *to)
+{
+ union ubifs_key *t = to;
+
+ t->j32[0] = cpu_to_le32(from->u32[0]);
+ t->j32[1] = cpu_to_le32(from->u32[1]);
+}
+
+/**
+ * key_copy - copy a key.
+ * @c: UBIFS file-system description object
+ * @from: the key to copy from
+ * @to: the key to copy to
+ */
+static inline void key_copy(const struct ubifs_info *c,
+ const union ubifs_key *from, union ubifs_key *to)
+{
+ to->u64[0] = from->u64[0];
+}
+
+/**
+ * keys_cmp - compare keys.
+ * @c: UBIFS file-system description object
+ * @key1: the first key to compare
+ * @key2: the second key to compare
+ *
+ * This function compares 2 keys and returns %-1 if @key1 is less than
+ * @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2.
+ */
+static inline int keys_cmp(const struct ubifs_info *c,
+ const union ubifs_key *key1,
+ const union ubifs_key *key2)
+{
+ if (key1->u32[0] < key2->u32[0])
+ return -1;
+ if (key1->u32[0] > key2->u32[0])
+ return 1;
+ if (key1->u32[1] < key2->u32[1])
+ return -1;
+ if (key1->u32[1] > key2->u32[1])
+ return 1;
+
+ return 0;
+}
+
+/**
+ * keys_eq - determine if keys are equivalent.
+ * @c: UBIFS file-system description object
+ * @key1: the first key to compare
+ * @key2: the second key to compare
+ *
+ * This function compares 2 keys and returns %1 if @key1 is equal to @key2 and
+ * %0 if not.
+ */
+static inline int keys_eq(const struct ubifs_info *c,
+ const union ubifs_key *key1,
+ const union ubifs_key *key2)
+{
+ if (key1->u32[0] != key2->u32[0])
+ return 0;
+ if (key1->u32[1] != key2->u32[1])
+ return 0;
+ return 1;
+}
+
+/**
+ * is_hash_key - is a key vulnerable to hash collisions.
+ * @c: UBIFS file-system description object
+ * @key: key
+ *
+ * This function returns %1 if @key is a hashed key or %0 otherwise.
+ */
+static inline int is_hash_key(const struct ubifs_info *c,
+ const union ubifs_key *key)
+{
+ int type = key_type(c, key);
+
+ return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY;
+}
+
+/**
+ * key_max_inode_size - get maximum file size allowed by current key format.
+ * @c: UBIFS file-system description object
+ */
+static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
+{
+ switch (c->key_fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
+ default:
+ return 0;
+ }
+}
+
+#endif /* !__UBIFS_KEY_H__ */
diff --git a/ubifs-utils/libubifs/log.c b/ubifs-utils/libubifs/log.c
new file mode 100644
index 00000000..b6ac9c42
--- /dev/null
+++ b/ubifs-utils/libubifs/log.c
@@ -0,0 +1,762 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file is a part of UBIFS journal implementation and contains various
+ * functions which manipulate the log. The log is a fixed area on the flash
+ * which does not contain any data but refers to buds. The log is a part of the
+ * journal.
+ */
+
+#include "ubifs.h"
+
+static int dbg_check_bud_bytes(struct ubifs_info *c);
+
+/**
+ * ubifs_search_bud - search bud LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number to search
+ *
+ * This function searches bud LEB @lnum. Returns bud description object in case
+ * of success and %NULL if there is no bud with this LEB number.
+ */
+struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum)
+{
+ struct rb_node *p;
+ struct ubifs_bud *bud;
+
+ spin_lock(&c->buds_lock);
+ p = c->buds.rb_node;
+ while (p) {
+ bud = rb_entry(p, struct ubifs_bud, rb);
+ if (lnum < bud->lnum)
+ p = p->rb_left;
+ else if (lnum > bud->lnum)
+ p = p->rb_right;
+ else {
+ spin_unlock(&c->buds_lock);
+ return bud;
+ }
+ }
+ spin_unlock(&c->buds_lock);
+ return NULL;
+}
+
+/**
+ * ubifs_get_wbuf - get the wbuf associated with a LEB, if there is one.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number to search
+ *
+ * This functions returns the wbuf for @lnum or %NULL if there is not one.
+ */
+struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum)
+{
+ struct rb_node *p;
+ struct ubifs_bud *bud;
+ int jhead;
+
+ if (!c->jheads)
+ return NULL;
+
+ spin_lock(&c->buds_lock);
+ p = c->buds.rb_node;
+ while (p) {
+ bud = rb_entry(p, struct ubifs_bud, rb);
+ if (lnum < bud->lnum)
+ p = p->rb_left;
+ else if (lnum > bud->lnum)
+ p = p->rb_right;
+ else {
+ jhead = bud->jhead;
+ spin_unlock(&c->buds_lock);
+ return &c->jheads[jhead].wbuf;
+ }
+ }
+ spin_unlock(&c->buds_lock);
+ return NULL;
+}
+
+/**
+ * empty_log_bytes - calculate amount of empty space in the log.
+ * @c: UBIFS file-system description object
+ */
+static inline long long empty_log_bytes(const struct ubifs_info *c)
+{
+ long long h, t;
+
+ h = (long long)c->lhead_lnum * c->leb_size + c->lhead_offs;
+ t = (long long)c->ltail_lnum * c->leb_size;
+
+ if (h > t)
+ return c->log_bytes - h + t;
+ else if (h != t)
+ return t - h;
+ else if (c->lhead_lnum != c->ltail_lnum)
+ return 0;
+ else
+ return c->log_bytes;
+}
+
+/**
+ * ubifs_add_bud - add bud LEB to the tree of buds and its journal head list.
+ * @c: UBIFS file-system description object
+ * @bud: the bud to add
+ */
+void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud)
+{
+ struct rb_node **p, *parent = NULL;
+ struct ubifs_bud *b;
+ struct ubifs_jhead *jhead;
+
+ spin_lock(&c->buds_lock);
+ p = &c->buds.rb_node;
+ while (*p) {
+ parent = *p;
+ b = rb_entry(parent, struct ubifs_bud, rb);
+ ubifs_assert(c, bud->lnum != b->lnum);
+ if (bud->lnum < b->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&bud->rb, parent, p);
+ rb_insert_color(&bud->rb, &c->buds);
+ if (c->jheads) {
+ jhead = &c->jheads[bud->jhead];
+ list_add_tail(&bud->list, &jhead->buds_list);
+ } else
+ ubifs_assert(c, c->replaying && c->ro_mount);
+
+ /*
+ * Note, although this is a new bud, we anyway account this space now,
+ * before any data has been written to it, because this is about to
+ * guarantee fixed mount time, and this bud will anyway be read and
+ * scanned.
+ */
+ c->bud_bytes += c->leb_size - bud->start;
+
+ dbg_log("LEB %d:%d, jhead %s, bud_bytes %lld", bud->lnum,
+ bud->start, dbg_jhead(bud->jhead), c->bud_bytes);
+ spin_unlock(&c->buds_lock);
+}
+
+/**
+ * ubifs_add_bud_to_log - add a new bud to the log.
+ * @c: UBIFS file-system description object
+ * @jhead: journal head the bud belongs to
+ * @lnum: LEB number of the bud
+ * @offs: starting offset of the bud
+ *
+ * This function writes a reference node for the new bud LEB @lnum to the log,
+ * and adds it to the buds trees. It also makes sure that log size does not
+ * exceed the 'c->max_bud_bytes' limit. Returns zero in case of success,
+ * %-EAGAIN if commit is required, and a negative error code in case of
+ * failure.
+ */
+int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
+{
+ int err;
+ struct ubifs_bud *bud;
+ struct ubifs_ref_node *ref;
+
+ bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS);
+ if (!bud)
+ return -ENOMEM;
+ ref = kzalloc(c->ref_node_alsz, GFP_NOFS);
+ if (!ref) {
+ kfree(bud);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&c->log_mutex);
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error) {
+ err = -EROFS;
+ goto out_unlock;
+ }
+
+ /* Make sure we have enough space in the log */
+ if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) {
+ dbg_log("not enough log space - %lld, required %d",
+ empty_log_bytes(c), c->min_log_bytes);
+ ubifs_commit_required(c);
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /*
+ * Make sure the amount of space in buds will not exceed the
+ * 'c->max_bud_bytes' limit, because we want to guarantee mount time
+ * limits.
+ *
+ * It is not necessary to hold @c->buds_lock when reading @c->bud_bytes
+ * because we are holding @c->log_mutex. All @c->bud_bytes take place
+ * when both @c->log_mutex and @c->bud_bytes are locked.
+ */
+ if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) {
+ dbg_log("bud bytes %lld (%lld max), require commit",
+ c->bud_bytes, c->max_bud_bytes);
+ ubifs_commit_required(c);
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /*
+ * If the journal is full enough - start background commit. Note, it is
+ * OK to read 'c->cmt_state' without spinlock because integer reads
+ * are atomic in the kernel.
+ */
+ if (c->bud_bytes >= c->bg_bud_bytes &&
+ c->cmt_state == COMMIT_RESTING) {
+ dbg_log("bud bytes %lld (%lld max), initiate BG commit",
+ c->bud_bytes, c->max_bud_bytes);
+ ubifs_request_bg_commit(c);
+ }
+
+ bud->lnum = lnum;
+ bud->start = offs;
+ bud->jhead = jhead;
+ bud->log_hash = NULL;
+
+ ref->ch.node_type = UBIFS_REF_NODE;
+ ref->lnum = cpu_to_le32(bud->lnum);
+ ref->offs = cpu_to_le32(bud->start);
+ ref->jhead = cpu_to_le32(jhead);
+
+ if (c->lhead_offs > c->leb_size - c->ref_node_alsz) {
+ c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ ubifs_assert(c, c->lhead_lnum != c->ltail_lnum);
+ c->lhead_offs = 0;
+ }
+
+ if (c->lhead_offs == 0) {
+ /* Must ensure next log LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->lhead_lnum);
+ if (err)
+ goto out_unlock;
+ }
+
+ if (bud->start == 0) {
+ /*
+ * Before writing the LEB reference which refers an empty LEB
+ * to the log, we have to make sure it is mapped, because
+ * otherwise we'd risk to refer an LEB with garbage in case of
+ * an unclean reboot, because the target LEB might have been
+ * unmapped, but not yet physically erased.
+ */
+ err = ubifs_leb_map(c, bud->lnum);
+ if (err)
+ goto out_unlock;
+ }
+
+ dbg_log("write ref LEB %d:%d",
+ c->lhead_lnum, c->lhead_offs);
+ err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum,
+ c->lhead_offs);
+ if (err)
+ goto out_unlock;
+
+ err = ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
+ if (err)
+ goto out_unlock;
+
+ err = ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);
+ if (err)
+ goto out_unlock;
+
+ c->lhead_offs += c->ref_node_alsz;
+
+ ubifs_add_bud(c, bud);
+
+ mutex_unlock(&c->log_mutex);
+ kfree(ref);
+ return 0;
+
+out_unlock:
+ mutex_unlock(&c->log_mutex);
+ kfree(ref);
+ kfree(bud);
+ return err;
+}
+
+/**
+ * remove_buds - remove used buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function removes use buds from the buds tree. It does not remove the
+ * buds which are pointed to by journal heads.
+ */
+static void remove_buds(struct ubifs_info *c)
+{
+ struct rb_node *p;
+
+ ubifs_assert(c, list_empty(&c->old_buds));
+ c->cmt_bud_bytes = 0;
+ spin_lock(&c->buds_lock);
+ p = rb_first(&c->buds);
+ while (p) {
+ struct rb_node *p1 = p;
+ struct ubifs_bud *bud;
+ struct ubifs_wbuf *wbuf;
+
+ p = rb_next(p);
+ bud = rb_entry(p1, struct ubifs_bud, rb);
+ wbuf = &c->jheads[bud->jhead].wbuf;
+
+ if (wbuf->lnum == bud->lnum) {
+ /*
+ * Do not remove buds which are pointed to by journal
+ * heads (non-closed buds).
+ */
+ c->cmt_bud_bytes += wbuf->offs - bud->start;
+ dbg_log("preserve %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
+ bud->lnum, bud->start, dbg_jhead(bud->jhead),
+ wbuf->offs - bud->start, c->cmt_bud_bytes);
+ bud->start = wbuf->offs;
+ } else {
+ c->cmt_bud_bytes += c->leb_size - bud->start;
+ dbg_log("remove %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld",
+ bud->lnum, bud->start, dbg_jhead(bud->jhead),
+ c->leb_size - bud->start, c->cmt_bud_bytes);
+ rb_erase(p1, &c->buds);
+ /*
+ * If the commit does not finish, the recovery will need
+ * to replay the journal, in which case the old buds
+ * must be unchanged. Do not release them until post
+ * commit i.e. do not allow them to be garbage
+ * collected.
+ */
+ list_move(&bud->list, &c->old_buds);
+ }
+ }
+ spin_unlock(&c->buds_lock);
+}
+
+/**
+ * ubifs_log_start_commit - start commit.
+ * @c: UBIFS file-system description object
+ * @ltail_lnum: return new log tail LEB number
+ *
+ * The commit operation starts with writing "commit start" node to the log and
+ * reference nodes for all journal heads which will define new journal after
+ * the commit has been finished. The commit start and reference nodes are
+ * written in one go to the nearest empty log LEB (hence, when commit is
+ * finished UBIFS may safely unmap all the previous log LEBs). This function
+ * returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
+{
+ void *buf;
+ struct ubifs_cs_node *cs;
+ struct ubifs_ref_node *ref;
+ int err, i, max_len, len;
+
+ err = dbg_check_bud_bytes(c);
+ if (err)
+ return err;
+
+ max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ;
+ max_len = ALIGN(max_len, c->min_io_size);
+ buf = cs = kmalloc(max_len, GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+
+ cs->ch.node_type = UBIFS_CS_NODE;
+ cs->cmt_no = cpu_to_le64(c->cmt_no);
+ ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
+
+ err = ubifs_shash_init(c, c->log_hash);
+ if (err)
+ goto out;
+
+ err = ubifs_shash_update(c, c->log_hash, cs, UBIFS_CS_NODE_SZ);
+ if (err < 0)
+ goto out;
+
+ /*
+ * Note, we do not lock 'c->log_mutex' because this is the commit start
+ * phase and we are exclusively using the log. And we do not lock
+ * write-buffer because nobody can write to the file-system at this
+ * phase.
+ */
+
+ len = UBIFS_CS_NODE_SZ;
+ for (i = 0; i < c->jhead_cnt; i++) {
+ int lnum = c->jheads[i].wbuf.lnum;
+ int offs = c->jheads[i].wbuf.offs;
+
+ if (lnum == -1 || offs == c->leb_size)
+ continue;
+
+ dbg_log("add ref to LEB %d:%d for jhead %s",
+ lnum, offs, dbg_jhead(i));
+ ref = buf + len;
+ ref->ch.node_type = UBIFS_REF_NODE;
+ ref->lnum = cpu_to_le32(lnum);
+ ref->offs = cpu_to_le32(offs);
+ ref->jhead = cpu_to_le32(i);
+
+ ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
+ len += UBIFS_REF_NODE_SZ;
+
+ err = ubifs_shash_update(c, c->log_hash, ref,
+ UBIFS_REF_NODE_SZ);
+ if (err)
+ goto out;
+ ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
+ }
+
+ ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
+
+ /* Switch to the next log LEB */
+ if (c->lhead_offs) {
+ c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ ubifs_assert(c, c->lhead_lnum != c->ltail_lnum);
+ c->lhead_offs = 0;
+ }
+
+ /* Must ensure next LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->lhead_lnum);
+ if (err)
+ goto out;
+
+ len = ALIGN(len, c->min_io_size);
+ dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len);
+ err = ubifs_leb_write(c, c->lhead_lnum, cs, 0, len);
+ if (err)
+ goto out;
+
+ *ltail_lnum = c->lhead_lnum;
+
+ c->lhead_offs += len;
+ ubifs_assert(c, c->lhead_offs < c->leb_size);
+
+ remove_buds(c);
+
+ /*
+ * We have started the commit and now users may use the rest of the log
+ * for new writes.
+ */
+ c->min_log_bytes = 0;
+
+out:
+ kfree(buf);
+ return err;
+}
+
+/**
+ * ubifs_log_end_commit - end commit.
+ * @c: UBIFS file-system description object
+ * @ltail_lnum: new log tail LEB number
+ *
+ * This function is called on when the commit operation was finished. It
+ * moves log tail to new position and updates the master node so that it stores
+ * the new log tail LEB number. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum)
+{
+ int err;
+
+ /*
+ * At this phase we have to lock 'c->log_mutex' because UBIFS allows FS
+ * writes during commit. Its only short "commit" start phase when
+ * writers are blocked.
+ */
+ mutex_lock(&c->log_mutex);
+
+ dbg_log("old tail was LEB %d:0, new tail is LEB %d:0",
+ c->ltail_lnum, ltail_lnum);
+
+ c->ltail_lnum = ltail_lnum;
+ /*
+ * The commit is finished and from now on it must be guaranteed that
+ * there is always enough space for the next commit.
+ */
+ c->min_log_bytes = c->leb_size;
+
+ spin_lock(&c->buds_lock);
+ c->bud_bytes -= c->cmt_bud_bytes;
+ spin_unlock(&c->buds_lock);
+
+ err = dbg_check_bud_bytes(c);
+ if (err)
+ goto out;
+
+ err = ubifs_write_master(c);
+
+out:
+ mutex_unlock(&c->log_mutex);
+ return err;
+}
+
+/**
+ * ubifs_log_post_commit - things to do after commit is completed.
+ * @c: UBIFS file-system description object
+ * @old_ltail_lnum: old log tail LEB number
+ *
+ * Release buds only after commit is completed, because they must be unchanged
+ * if recovery is needed.
+ *
+ * Unmap log LEBs only after commit is completed, because they may be needed for
+ * recovery.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
+{
+ int lnum, err = 0;
+
+ while (!list_empty(&c->old_buds)) {
+ struct ubifs_bud *bud;
+
+ bud = list_entry(c->old_buds.next, struct ubifs_bud, list);
+ err = ubifs_return_leb(c, bud->lnum);
+ if (err)
+ return err;
+ list_del(&bud->list);
+ kfree(bud->log_hash);
+ kfree(bud);
+ }
+ mutex_lock(&c->log_mutex);
+ for (lnum = old_ltail_lnum; lnum != c->ltail_lnum;
+ lnum = ubifs_next_log_lnum(c, lnum)) {
+ dbg_log("unmap log LEB %d", lnum);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ goto out;
+ }
+out:
+ mutex_unlock(&c->log_mutex);
+ return err;
+}
+
+/**
+ * struct done_ref - references that have been done.
+ * @rb: rb-tree node
+ * @lnum: LEB number
+ */
+struct done_ref {
+ struct rb_node rb;
+ int lnum;
+};
+
+/**
+ * done_already - determine if a reference has been done already.
+ * @done_tree: rb-tree to store references that have been done
+ * @lnum: LEB number of reference
+ *
+ * This function returns %1 if the reference has been done, %0 if not, otherwise
+ * a negative error code is returned.
+ */
+static int done_already(struct rb_root *done_tree, int lnum)
+{
+ struct rb_node **p = &done_tree->rb_node, *parent = NULL;
+ struct done_ref *dr;
+
+ while (*p) {
+ parent = *p;
+ dr = rb_entry(parent, struct done_ref, rb);
+ if (lnum < dr->lnum)
+ p = &(*p)->rb_left;
+ else if (lnum > dr->lnum)
+ p = &(*p)->rb_right;
+ else
+ return 1;
+ }
+
+ dr = kzalloc(sizeof(struct done_ref), GFP_NOFS);
+ if (!dr)
+ return -ENOMEM;
+
+ dr->lnum = lnum;
+
+ rb_link_node(&dr->rb, parent, p);
+ rb_insert_color(&dr->rb, done_tree);
+
+ return 0;
+}
+
+/**
+ * destroy_done_tree - destroy the done tree.
+ * @done_tree: done tree to destroy
+ */
+static void destroy_done_tree(struct rb_root *done_tree)
+{
+ struct done_ref *dr, *n;
+
+ rbtree_postorder_for_each_entry_safe(dr, n, done_tree, rb)
+ kfree(dr);
+}
+
+/**
+ * add_node - add a node to the consolidated log.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to which to add
+ * @lnum: LEB number to which to write is passed and returned here
+ * @offs: offset to where to write is passed and returned here
+ * @node: node to add
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs,
+ void *node)
+{
+ struct ubifs_ch *ch = node;
+ int len = le32_to_cpu(ch->len), remains = c->leb_size - *offs;
+
+ if (len > remains) {
+ int sz = ALIGN(*offs, c->min_io_size), err;
+
+ ubifs_pad(c, buf + *offs, sz - *offs);
+ err = ubifs_leb_change(c, *lnum, buf, sz);
+ if (err)
+ return err;
+ *lnum = ubifs_next_log_lnum(c, *lnum);
+ *offs = 0;
+ }
+ memcpy(buf + *offs, node, len);
+ *offs += ALIGN(len, 8);
+ return 0;
+}
+
+/**
+ * ubifs_consolidate_log - consolidate the log.
+ * @c: UBIFS file-system description object
+ *
+ * Repeated failed commits could cause the log to be full, but at least 1 LEB is
+ * needed for commit. This function rewrites the reference nodes in the log
+ * omitting duplicates, and failed CS nodes, and leaving no gaps.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_consolidate_log(struct ubifs_info *c)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ struct rb_root done_tree = RB_ROOT;
+ int lnum, err, first = 1, write_lnum, offs = 0;
+ void *buf;
+
+ dbg_rcvry("log tail LEB %d, log head LEB %d", c->ltail_lnum,
+ c->lhead_lnum);
+ buf = vmalloc(c->leb_size);
+ if (!buf)
+ return -ENOMEM;
+ lnum = c->ltail_lnum;
+ write_lnum = lnum;
+ while (1) {
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ goto out_free;
+ }
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ switch (snod->type) {
+ case UBIFS_REF_NODE: {
+ struct ubifs_ref_node *ref = snod->node;
+ int ref_lnum = le32_to_cpu(ref->lnum);
+
+ err = done_already(&done_tree, ref_lnum);
+ if (err < 0)
+ goto out_scan;
+ if (err != 1) {
+ err = add_node(c, buf, &write_lnum,
+ &offs, snod->node);
+ if (err)
+ goto out_scan;
+ }
+ break;
+ }
+ case UBIFS_CS_NODE:
+ if (!first)
+ break;
+ err = add_node(c, buf, &write_lnum, &offs,
+ snod->node);
+ if (err)
+ goto out_scan;
+ first = 0;
+ break;
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ if (lnum == c->lhead_lnum)
+ break;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ }
+ if (offs) {
+ int sz = ALIGN(offs, c->min_io_size);
+
+ ubifs_pad(c, buf + offs, sz - offs);
+ err = ubifs_leb_change(c, write_lnum, buf, sz);
+ if (err)
+ goto out_free;
+ offs = ALIGN(offs, c->min_io_size);
+ }
+ destroy_done_tree(&done_tree);
+ vfree(buf);
+ if (write_lnum == c->lhead_lnum) {
+ ubifs_err(c, "log is too full");
+ return -EINVAL;
+ }
+ /* Unmap remaining LEBs */
+ lnum = write_lnum;
+ do {
+ lnum = ubifs_next_log_lnum(c, lnum);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ } while (lnum != c->lhead_lnum);
+ c->lhead_lnum = write_lnum;
+ c->lhead_offs = offs;
+ dbg_rcvry("new log head at %d:%d", c->lhead_lnum, c->lhead_offs);
+ return 0;
+
+out_scan:
+ ubifs_scan_destroy(sleb);
+out_free:
+ destroy_done_tree(&done_tree);
+ vfree(buf);
+ return err;
+}
+
+/**
+ * dbg_check_bud_bytes - make sure bud bytes calculation are all right.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure the amount of flash space used by closed buds
+ * ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in
+ * case of failure.
+ */
+static int dbg_check_bud_bytes(struct ubifs_info *c)
+{
+ int i, err = 0;
+ struct ubifs_bud *bud;
+ long long bud_bytes = 0;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ spin_lock(&c->buds_lock);
+ for (i = 0; i < c->jhead_cnt; i++)
+ list_for_each_entry(bud, &c->jheads[i].buds_list, list)
+ bud_bytes += c->leb_size - bud->start;
+
+ if (c->bud_bytes != bud_bytes) {
+ ubifs_err(c, "bad bud_bytes %lld, calculated %lld",
+ c->bud_bytes, bud_bytes);
+ err = -EINVAL;
+ }
+ spin_unlock(&c->buds_lock);
+
+ return err;
+}
diff --git a/ubifs-utils/libubifs/lprops.c b/ubifs-utils/libubifs/lprops.c
new file mode 100644
index 00000000..6d6cd85c
--- /dev/null
+++ b/ubifs-utils/libubifs/lprops.c
@@ -0,0 +1,1307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the functions that access LEB properties and their
+ * categories. LEBs are categorized based on the needs of UBIFS, and the
+ * categories are stored as either heaps or lists to provide a fast way of
+ * finding a LEB in a particular category. For example, UBIFS may need to find
+ * an empty LEB for the journal, or a very dirty LEB for garbage collection.
+ */
+
+#include "ubifs.h"
+
+/**
+ * get_heap_comp_val - get the LEB properties value for heap comparisons.
+ * @lprops: LEB properties
+ * @cat: LEB category
+ */
+static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
+{
+ switch (cat) {
+ case LPROPS_FREE:
+ return lprops->free;
+ case LPROPS_DIRTY_IDX:
+ return lprops->free + lprops->dirty;
+ default:
+ return lprops->dirty;
+ }
+}
+
+/**
+ * move_up_lpt_heap - move a new heap entry up as far as possible.
+ * @c: UBIFS file-system description object
+ * @heap: LEB category heap
+ * @lprops: LEB properties to move
+ * @cat: LEB category
+ *
+ * New entries to a heap are added at the bottom and then moved up until the
+ * parent's value is greater. In the case of LPT's category heaps, the value
+ * is either the amount of free space or the amount of dirty space, depending
+ * on the category.
+ */
+static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ struct ubifs_lprops *lprops, int cat)
+{
+ int val1, val2, hpos;
+
+ hpos = lprops->hpos;
+ if (!hpos)
+ return; /* Already top of the heap */
+ val1 = get_heap_comp_val(lprops, cat);
+ /* Compare to parent and, if greater, move up the heap */
+ do {
+ int ppos = (hpos - 1) / 2;
+
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val2 >= val1)
+ return;
+ /* Greater than parent so move up */
+ heap->arr[ppos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[ppos];
+ heap->arr[ppos] = lprops;
+ lprops->hpos = ppos;
+ hpos = ppos;
+ } while (hpos);
+}
+
+/**
+ * adjust_lpt_heap - move a changed heap entry up or down the heap.
+ * @c: UBIFS file-system description object
+ * @heap: LEB category heap
+ * @lprops: LEB properties to move
+ * @hpos: heap position of @lprops
+ * @cat: LEB category
+ *
+ * Changed entries in a heap are moved up or down until the parent's value is
+ * greater. In the case of LPT's category heaps, the value is either the amount
+ * of free space or the amount of dirty space, depending on the category.
+ */
+static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
+ struct ubifs_lprops *lprops, int hpos, int cat)
+{
+ int val1, val2, val3, cpos;
+
+ val1 = get_heap_comp_val(lprops, cat);
+ /* Compare to parent and, if greater than parent, move up the heap */
+ if (hpos) {
+ int ppos = (hpos - 1) / 2;
+
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val1 > val2) {
+ /* Greater than parent so move up */
+ while (1) {
+ heap->arr[ppos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[ppos];
+ heap->arr[ppos] = lprops;
+ lprops->hpos = ppos;
+ hpos = ppos;
+ if (!hpos)
+ return;
+ ppos = (hpos - 1) / 2;
+ val2 = get_heap_comp_val(heap->arr[ppos], cat);
+ if (val1 <= val2)
+ return;
+ /* Still greater than parent so keep going */
+ }
+ }
+ }
+
+ /* Not greater than parent, so compare to children */
+ while (1) {
+ /* Compare to left child */
+ cpos = hpos * 2 + 1;
+ if (cpos >= heap->cnt)
+ return;
+ val2 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 < val2) {
+ /* Less than left child, so promote biggest child */
+ if (cpos + 1 < heap->cnt) {
+ val3 = get_heap_comp_val(heap->arr[cpos + 1],
+ cat);
+ if (val3 > val2)
+ cpos += 1; /* Right child is bigger */
+ }
+ heap->arr[cpos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[cpos];
+ heap->arr[cpos] = lprops;
+ lprops->hpos = cpos;
+ hpos = cpos;
+ continue;
+ }
+ /* Compare to right child */
+ cpos += 1;
+ if (cpos >= heap->cnt)
+ return;
+ val3 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 < val3) {
+ /* Less than right child, so promote right child */
+ heap->arr[cpos]->hpos = hpos;
+ heap->arr[hpos] = heap->arr[cpos];
+ heap->arr[cpos] = lprops;
+ lprops->hpos = cpos;
+ hpos = cpos;
+ continue;
+ }
+ return;
+ }
+}
+
+/**
+ * add_to_lpt_heap - add LEB properties to a LEB category heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to add
+ * @cat: LEB category
+ *
+ * This function returns %1 if @lprops is added to the heap for LEB category
+ * @cat, otherwise %0 is returned because the heap is full.
+ */
+static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat)
+{
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ if (heap->cnt >= heap->max_cnt) {
+ const int b = LPT_HEAP_SZ / 2 - 1;
+ int cpos, val1, val2;
+
+ /* Compare to some other LEB on the bottom of heap */
+ /* Pick a position kind of randomly */
+ cpos = (((size_t)lprops >> 4) & b) + b;
+ ubifs_assert(c, cpos >= b);
+ ubifs_assert(c, cpos < LPT_HEAP_SZ);
+ ubifs_assert(c, cpos < heap->cnt);
+
+ val1 = get_heap_comp_val(lprops, cat);
+ val2 = get_heap_comp_val(heap->arr[cpos], cat);
+ if (val1 > val2) {
+ struct ubifs_lprops *lp;
+
+ lp = heap->arr[cpos];
+ lp->flags &= ~LPROPS_CAT_MASK;
+ lp->flags |= LPROPS_UNCAT;
+ list_add(&lp->list, &c->uncat_list);
+ lprops->hpos = cpos;
+ heap->arr[cpos] = lprops;
+ move_up_lpt_heap(c, heap, lprops, cat);
+ dbg_check_heap(c, heap, cat, lprops->hpos);
+ return 1; /* Added to heap */
+ }
+ dbg_check_heap(c, heap, cat, -1);
+ return 0; /* Not added to heap */
+ } else {
+ lprops->hpos = heap->cnt++;
+ heap->arr[lprops->hpos] = lprops;
+ move_up_lpt_heap(c, heap, lprops, cat);
+ dbg_check_heap(c, heap, cat, lprops->hpos);
+ return 1; /* Added to heap */
+ }
+}
+
+/**
+ * remove_from_lpt_heap - remove LEB properties from a LEB category heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to remove
+ * @cat: LEB category
+ */
+static void remove_from_lpt_heap(struct ubifs_info *c,
+ struct ubifs_lprops *lprops, int cat)
+{
+ struct ubifs_lpt_heap *heap;
+ int hpos = lprops->hpos;
+
+ heap = &c->lpt_heap[cat - 1];
+ ubifs_assert(c, hpos >= 0 && hpos < heap->cnt);
+ ubifs_assert(c, heap->arr[hpos] == lprops);
+ heap->cnt -= 1;
+ if (hpos < heap->cnt) {
+ heap->arr[hpos] = heap->arr[heap->cnt];
+ heap->arr[hpos]->hpos = hpos;
+ adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
+ }
+ dbg_check_heap(c, heap, cat, -1);
+}
+
+/**
+ * lpt_heap_replace - replace lprops in a category heap.
+ * @c: UBIFS file-system description object
+ * @new_lprops: LEB properties with which to replace
+ * @cat: LEB category
+ *
+ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
+ * and the lprops that the pnode contains. When that happens, references in
+ * the category heaps to those lprops must be updated to point to the new
+ * lprops. This function does that.
+ */
+static void lpt_heap_replace(struct ubifs_info *c,
+ struct ubifs_lprops *new_lprops, int cat)
+{
+ struct ubifs_lpt_heap *heap;
+ int hpos = new_lprops->hpos;
+
+ heap = &c->lpt_heap[cat - 1];
+ heap->arr[hpos] = new_lprops;
+}
+
+/**
+ * ubifs_add_to_cat - add LEB properties to a category list or heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to add
+ * @cat: LEB category to which to add
+ *
+ * LEB properties are categorized to enable fast find operations.
+ */
+void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat)
+{
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ if (add_to_lpt_heap(c, lprops, cat))
+ break;
+ /* No more room on heap so make it un-categorized */
+ cat = LPROPS_UNCAT;
+ fallthrough;
+ case LPROPS_UNCAT:
+ list_add(&lprops->list, &c->uncat_list);
+ break;
+ case LPROPS_EMPTY:
+ list_add(&lprops->list, &c->empty_list);
+ break;
+ case LPROPS_FREEABLE:
+ list_add(&lprops->list, &c->freeable_list);
+ c->freeable_cnt += 1;
+ break;
+ case LPROPS_FRDI_IDX:
+ list_add(&lprops->list, &c->frdi_idx_list);
+ break;
+ default:
+ ubifs_assert(c, 0);
+ }
+
+ lprops->flags &= ~LPROPS_CAT_MASK;
+ lprops->flags |= cat;
+ c->in_a_category_cnt += 1;
+ ubifs_assert(c, c->in_a_category_cnt <= c->main_lebs);
+}
+
+/**
+ * ubifs_remove_from_cat - remove LEB properties from a category list or heap.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to remove
+ * @cat: LEB category from which to remove
+ *
+ * LEB properties are categorized to enable fast find operations.
+ */
+static void ubifs_remove_from_cat(struct ubifs_info *c,
+ struct ubifs_lprops *lprops, int cat)
+{
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ remove_from_lpt_heap(c, lprops, cat);
+ break;
+ case LPROPS_FREEABLE:
+ c->freeable_cnt -= 1;
+ ubifs_assert(c, c->freeable_cnt >= 0);
+ fallthrough;
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FRDI_IDX:
+ ubifs_assert(c, !list_empty(&lprops->list));
+ list_del(&lprops->list);
+ break;
+ default:
+ ubifs_assert(c, 0);
+ }
+
+ c->in_a_category_cnt -= 1;
+ ubifs_assert(c, c->in_a_category_cnt >= 0);
+}
+
+/**
+ * ubifs_replace_cat - replace lprops in a category list or heap.
+ * @c: UBIFS file-system description object
+ * @old_lprops: LEB properties to replace
+ * @new_lprops: LEB properties with which to replace
+ *
+ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
+ * and the lprops that the pnode contains. When that happens, references in
+ * category lists and heaps must be replaced. This function does that.
+ */
+void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
+ struct ubifs_lprops *new_lprops)
+{
+ int cat;
+
+ cat = new_lprops->flags & LPROPS_CAT_MASK;
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ lpt_heap_replace(c, new_lprops, cat);
+ break;
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ list_replace(&old_lprops->list, &new_lprops->list);
+ break;
+ default:
+ ubifs_assert(c, 0);
+ }
+}
+
+/**
+ * ubifs_ensure_cat - ensure LEB properties are categorized.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties
+ *
+ * A LEB may have fallen off of the bottom of a heap, and ended up as
+ * un-categorized even though it has enough space for us now. If that is the
+ * case this function will put the LEB back onto a heap.
+ */
+void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ int cat = lprops->flags & LPROPS_CAT_MASK;
+
+ if (cat != LPROPS_UNCAT)
+ return;
+ cat = ubifs_categorize_lprops(c, lprops);
+ if (cat == LPROPS_UNCAT)
+ return;
+ ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT);
+ ubifs_add_to_cat(c, lprops, cat);
+}
+
+/**
+ * ubifs_categorize_lprops - categorize LEB properties.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to categorize
+ *
+ * LEB properties are categorized to enable fast find operations. This function
+ * returns the LEB category to which the LEB properties belong. Note however
+ * that if the LEB category is stored as a heap and the heap is full, the
+ * LEB properties may have their category changed to %LPROPS_UNCAT.
+ */
+int ubifs_categorize_lprops(const struct ubifs_info *c,
+ const struct ubifs_lprops *lprops)
+{
+ if (lprops->flags & LPROPS_TAKEN)
+ return LPROPS_UNCAT;
+
+ if (lprops->free == c->leb_size) {
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ return LPROPS_EMPTY;
+ }
+
+ if (lprops->free + lprops->dirty == c->leb_size) {
+ if (lprops->flags & LPROPS_INDEX)
+ return LPROPS_FRDI_IDX;
+ else
+ return LPROPS_FREEABLE;
+ }
+
+ if (lprops->flags & LPROPS_INDEX) {
+ if (lprops->dirty + lprops->free >= c->min_idx_node_sz)
+ return LPROPS_DIRTY_IDX;
+ } else {
+ if (lprops->dirty >= c->dead_wm &&
+ lprops->dirty > lprops->free)
+ return LPROPS_DIRTY;
+ if (lprops->free > 0)
+ return LPROPS_FREE;
+ }
+
+ return LPROPS_UNCAT;
+}
+
+/**
+ * change_category - change LEB properties category.
+ * @c: UBIFS file-system description object
+ * @lprops: LEB properties to re-categorize
+ *
+ * LEB properties are categorized to enable fast find operations. When the LEB
+ * properties change they must be re-categorized.
+ */
+static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ int old_cat = lprops->flags & LPROPS_CAT_MASK;
+ int new_cat = ubifs_categorize_lprops(c, lprops);
+
+ if (old_cat == new_cat) {
+ struct ubifs_lpt_heap *heap;
+
+ /* lprops on a heap now must be moved up or down */
+ if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
+ return; /* Not on a heap */
+ heap = &c->lpt_heap[new_cat - 1];
+ adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat);
+ } else {
+ ubifs_remove_from_cat(c, lprops, old_cat);
+ ubifs_add_to_cat(c, lprops, new_cat);
+ }
+}
+
+/**
+ * ubifs_calc_dark - calculate LEB dark space size.
+ * @c: the UBIFS file-system description object
+ * @spc: amount of free and dirty space in the LEB
+ *
+ * This function calculates and returns amount of dark space in an LEB which
+ * has @spc bytes of free and dirty space.
+ *
+ * UBIFS is trying to account the space which might not be usable, and this
+ * space is called "dark space". For example, if an LEB has only %512 free
+ * bytes, it is dark space, because it cannot fit a large data node.
+ */
+int ubifs_calc_dark(const struct ubifs_info *c, int spc)
+{
+ ubifs_assert(c, !(spc & 7));
+
+ if (spc < c->dark_wm)
+ return spc;
+
+ /*
+ * If we have slightly more space then the dark space watermark, we can
+ * anyway safely assume it we'll be able to write a node of the
+ * smallest size there.
+ */
+ if (spc - c->dark_wm < MIN_WRITE_SZ)
+ return spc - MIN_WRITE_SZ;
+
+ return c->dark_wm;
+}
+
+/**
+ * is_lprops_dirty - determine if LEB properties are dirty.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to test
+ */
+static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
+{
+ struct ubifs_pnode *pnode;
+ int pos;
+
+ pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1);
+ pnode = (struct ubifs_pnode *)container_of(lprops - pos,
+ struct ubifs_pnode,
+ lprops[0]);
+ return !test_bit(COW_CNODE, &pnode->flags) &&
+ test_bit(DIRTY_CNODE, &pnode->flags);
+}
+
+/**
+ * ubifs_change_lp - change LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lp: LEB properties to change
+ * @free: new free space amount
+ * @dirty: new dirty space amount
+ * @flags: new flags
+ * @idx_gc_cnt: change to the count of @idx_gc list
+ *
+ * This function changes LEB properties (@free, @dirty or @flag). However, the
+ * property which has the %LPROPS_NC value is not changed. Returns a pointer to
+ * the updated LEB properties on success and a negative error code on failure.
+ *
+ * Note, the LEB properties may have had to be copied (due to COW) and
+ * consequently the pointer returned may not be the same as the pointer
+ * passed.
+ */
+const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
+ const struct ubifs_lprops *lp,
+ int free, int dirty, int flags,
+ int idx_gc_cnt)
+{
+ /*
+ * This is the only function that is allowed to change lprops, so we
+ * discard the "const" qualifier.
+ */
+ struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
+
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d",
+ lprops->lnum, free, dirty, flags);
+
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+ ubifs_assert(c, c->lst.empty_lebs >= 0 &&
+ c->lst.empty_lebs <= c->main_lebs);
+ ubifs_assert(c, c->freeable_cnt >= 0);
+ ubifs_assert(c, c->freeable_cnt <= c->main_lebs);
+ ubifs_assert(c, c->lst.taken_empty_lebs >= 0);
+ ubifs_assert(c, c->lst.taken_empty_lebs <= c->lst.empty_lebs);
+ ubifs_assert(c, !(c->lst.total_free & 7) && !(c->lst.total_dirty & 7));
+ ubifs_assert(c, !(c->lst.total_dead & 7) && !(c->lst.total_dark & 7));
+ ubifs_assert(c, !(c->lst.total_used & 7));
+ ubifs_assert(c, free == LPROPS_NC || free >= 0);
+ ubifs_assert(c, dirty == LPROPS_NC || dirty >= 0);
+
+ if (!is_lprops_dirty(c, lprops)) {
+ lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum);
+ if (IS_ERR(lprops))
+ return lprops;
+ } else
+ ubifs_assert(c, lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum));
+
+ ubifs_assert(c, !(lprops->free & 7) && !(lprops->dirty & 7));
+
+ spin_lock(&c->space_lock);
+ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
+ c->lst.taken_empty_lebs -= 1;
+
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ int old_spc;
+
+ old_spc = lprops->free + lprops->dirty;
+ if (old_spc < c->dead_wm)
+ c->lst.total_dead -= old_spc;
+ else
+ c->lst.total_dark -= ubifs_calc_dark(c, old_spc);
+
+ c->lst.total_used -= c->leb_size - old_spc;
+ }
+
+ if (free != LPROPS_NC) {
+ free = ALIGN(free, 8);
+ c->lst.total_free += free - lprops->free;
+
+ /* Increase or decrease empty LEBs counter if needed */
+ if (free == c->leb_size) {
+ if (lprops->free != c->leb_size)
+ c->lst.empty_lebs += 1;
+ } else if (lprops->free == c->leb_size)
+ c->lst.empty_lebs -= 1;
+ lprops->free = free;
+ }
+
+ if (dirty != LPROPS_NC) {
+ dirty = ALIGN(dirty, 8);
+ c->lst.total_dirty += dirty - lprops->dirty;
+ lprops->dirty = dirty;
+ }
+
+ if (flags != LPROPS_NC) {
+ /* Take care about indexing LEBs counter if needed */
+ if ((lprops->flags & LPROPS_INDEX)) {
+ if (!(flags & LPROPS_INDEX))
+ c->lst.idx_lebs -= 1;
+ } else if (flags & LPROPS_INDEX)
+ c->lst.idx_lebs += 1;
+ lprops->flags = flags;
+ }
+
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ int new_spc;
+
+ new_spc = lprops->free + lprops->dirty;
+ if (new_spc < c->dead_wm)
+ c->lst.total_dead += new_spc;
+ else
+ c->lst.total_dark += ubifs_calc_dark(c, new_spc);
+
+ c->lst.total_used += c->leb_size - new_spc;
+ }
+
+ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
+ c->lst.taken_empty_lebs += 1;
+
+ change_category(c, lprops);
+ c->idx_gc_cnt += idx_gc_cnt;
+ spin_unlock(&c->space_lock);
+ return lprops;
+}
+
+/**
+ * ubifs_get_lp_stats - get lprops statistics.
+ * @c: UBIFS file-system description object
+ * @lst: return statistics
+ */
+void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
+{
+ spin_lock(&c->space_lock);
+ memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats));
+ spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_change_one_lp - change LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to change properties for
+ * @free: amount of free space
+ * @dirty: amount of dirty space
+ * @flags_set: flags to set
+ * @flags_clean: flags to clean
+ * @idx_gc_cnt: change to the count of idx_gc list
+ *
+ * This function changes properties of LEB @lnum. It is a helper wrapper over
+ * 'ubifs_change_lp()' which hides lprops get/release. The arguments are the
+ * same as in case of 'ubifs_change_lp()'. Returns zero in case of success and
+ * a negative error code in case of failure.
+ */
+int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean, int idx_gc_cnt)
+{
+ int err = 0, flags;
+ const struct ubifs_lprops *lp;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ flags = (lp->flags | flags_set) & ~flags_clean;
+ lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
+ if (IS_ERR(lp))
+ err = PTR_ERR(lp);
+
+out:
+ ubifs_release_lprops(c);
+ if (err)
+ ubifs_err(c, "cannot change properties of LEB %d, error %d",
+ lnum, err);
+ return err;
+}
+
+/**
+ * ubifs_update_one_lp - update LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to change properties for
+ * @free: amount of free space
+ * @dirty: amount of dirty space to add
+ * @flags_set: flags to set
+ * @flags_clean: flags to clean
+ *
+ * This function is the same as 'ubifs_change_one_lp()' but @dirty is added to
+ * current dirty space, not substitutes it.
+ */
+int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean)
+{
+ int err = 0, flags;
+ const struct ubifs_lprops *lp;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ flags = (lp->flags | flags_set) & ~flags_clean;
+ lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
+ if (IS_ERR(lp))
+ err = PTR_ERR(lp);
+
+out:
+ ubifs_release_lprops(c);
+ if (err)
+ ubifs_err(c, "cannot update properties of LEB %d, error %d",
+ lnum, err);
+ return err;
+}
+
+/**
+ * ubifs_read_one_lp - read LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to read properties for
+ * @lp: where to store read properties
+ *
+ * This helper function reads properties of a LEB @lnum and stores them in @lp.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
+{
+ int err = 0;
+ const struct ubifs_lprops *lpp;
+
+ ubifs_get_lprops(c);
+
+ lpp = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lpp)) {
+ err = PTR_ERR(lpp);
+ ubifs_err(c, "cannot read properties of LEB %d, error %d",
+ lnum, err);
+ goto out;
+ }
+
+ memcpy(lp, lpp, sizeof(struct ubifs_lprops));
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_fast_find_free - try to find a LEB with free space quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a LEB with free space or %NULL if
+ * the function is unable to find a LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ if (heap->cnt == 0)
+ return NULL;
+
+ lprops = heap->arr[0];
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_empty - try to find an empty LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for an empty LEB or %NULL if the
+ * function is unable to find an empty LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->empty_list))
+ return NULL;
+
+ lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ ubifs_assert(c, lprops->free == c->leb_size);
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_freeable - try to find a freeable LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a freeable LEB or %NULL if the
+ * function is unable to find a freeable LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->freeable_list))
+ return NULL;
+
+ lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
+ ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
+ ubifs_assert(c, c->freeable_cnt > 0);
+ return lprops;
+}
+
+/**
+ * ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB properties for a freeable index LEB or %NULL if the
+ * function is unable to find a freeable index LEB quickly.
+ */
+const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+
+ if (list_empty(&c->frdi_idx_list))
+ return NULL;
+
+ lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list);
+ ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
+ ubifs_assert(c, (lprops->flags & LPROPS_INDEX));
+ ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
+ return lprops;
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+/**
+ * dbg_check_cats - check category heaps and lists.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_cats(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct list_head *pos;
+ int i, cat;
+
+ if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
+ return 0;
+
+ list_for_each_entry(lprops, &c->empty_list, list) {
+ if (lprops->free != c->leb_size) {
+ ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ }
+
+ i = 0;
+ list_for_each_entry(lprops, &c->freeable_list, list) {
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ i += 1;
+ }
+ if (i != c->freeable_cnt) {
+ ubifs_err(c, "freeable list count %d expected %d", i,
+ c->freeable_cnt);
+ return -EINVAL;
+ }
+
+ i = 0;
+ list_for_each(pos, &c->idx_gc)
+ i += 1;
+ if (i != c->idx_gc_cnt) {
+ ubifs_err(c, "idx_gc list count %d expected %d", i,
+ c->idx_gc_cnt);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ if (!(lprops->flags & LPROPS_INDEX)) {
+ ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
+ lprops->lnum, lprops->free, lprops->dirty,
+ lprops->flags);
+ return -EINVAL;
+ }
+ }
+
+ for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ for (i = 0; i < heap->cnt; i++) {
+ lprops = heap->arr[i];
+ if (!lprops) {
+ ubifs_err(c, "null ptr in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ if (lprops->hpos != i) {
+ ubifs_err(c, "bad ptr in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ ubifs_err(c, "taken LEB in LPT heap cat %d", cat);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
+ int add_pos)
+{
+ int i = 0, j, err = 0;
+
+ if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
+ return;
+
+ for (i = 0; i < heap->cnt; i++) {
+ struct ubifs_lprops *lprops = heap->arr[i];
+ struct ubifs_lprops *lp;
+
+ if (i != add_pos)
+ if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
+ err = 1;
+ goto out;
+ }
+ if (lprops->hpos != i) {
+ err = 2;
+ goto out;
+ }
+ lp = ubifs_lpt_lookup(c, lprops->lnum);
+ if (IS_ERR(lp)) {
+ err = 3;
+ goto out;
+ }
+ if (lprops != lp) {
+ ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
+ (size_t)lprops, (size_t)lp, lprops->lnum,
+ lp->lnum);
+ err = 4;
+ goto out;
+ }
+ for (j = 0; j < i; j++) {
+ lp = heap->arr[j];
+ if (lp == lprops) {
+ err = 5;
+ goto out;
+ }
+ if (lp->lnum == lprops->lnum) {
+ err = 6;
+ goto out;
+ }
+ }
+ }
+out:
+ if (err) {
+ ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err);
+ dump_stack();
+ ubifs_dump_heap(c, heap, cat);
+ }
+}
+
+/**
+ * scan_check_cb - scan callback.
+ * @c: the UBIFS file-system description object
+ * @lp: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @lst: lprops statistics to update
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_check_cb(struct ubifs_info *c,
+ const struct ubifs_lprops *lp, int in_tree,
+ struct ubifs_lp_stats *lst)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
+ void *buf = NULL;
+
+ cat = lp->flags & LPROPS_CAT_MASK;
+ if (cat != LPROPS_UNCAT) {
+ cat = ubifs_categorize_lprops(c, lp);
+ if (cat != (lp->flags & LPROPS_CAT_MASK)) {
+ ubifs_err(c, "bad LEB category %d expected %d",
+ (lp->flags & LPROPS_CAT_MASK), cat);
+ return -EINVAL;
+ }
+ }
+
+ /* Check lp is on its category list (if it has one) */
+ if (in_tree) {
+ struct list_head *list = NULL;
+
+ switch (cat) {
+ case LPROPS_EMPTY:
+ list = &c->empty_list;
+ break;
+ case LPROPS_FREEABLE:
+ list = &c->freeable_list;
+ break;
+ case LPROPS_FRDI_IDX:
+ list = &c->frdi_idx_list;
+ break;
+ case LPROPS_UNCAT:
+ list = &c->uncat_list;
+ break;
+ }
+ if (list) {
+ struct ubifs_lprops *lprops;
+ int found = 0;
+
+ list_for_each_entry(lprops, list, list) {
+ if (lprops == lp) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ubifs_err(c, "bad LPT list (category %d)", cat);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Check lp is on its category heap (if it has one) */
+ if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
+ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
+
+ if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
+ lp != heap->arr[lp->hpos]) {
+ ubifs_err(c, "bad LPT heap (category %d)", cat);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * After an unclean unmount, empty and freeable LEBs
+ * may contain garbage - do not scan them.
+ */
+ if (lp->free == c->leb_size) {
+ lst->empty_lebs += 1;
+ lst->total_free += c->leb_size;
+ lst->total_dark += ubifs_calc_dark(c, c->leb_size);
+ return LPT_SCAN_CONTINUE;
+ }
+ if (lp->free + lp->dirty == c->leb_size &&
+ !(lp->flags & LPROPS_INDEX)) {
+ lst->total_free += lp->free;
+ lst->total_dirty += lp->dirty;
+ lst->total_dark += ubifs_calc_dark(c, c->leb_size);
+ return LPT_SCAN_CONTINUE;
+ }
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ ret = PTR_ERR(sleb);
+ if (ret == -EUCLEAN) {
+ ubifs_dump_lprops(c);
+ ubifs_dump_budg(c, &c->bi);
+ }
+ goto out;
+ }
+
+ is_idx = -1;
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ int found, level = 0;
+
+ cond_resched();
+
+ if (is_idx == -1)
+ is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
+
+ if (is_idx && snod->type != UBIFS_IDX_NODE) {
+ ubifs_err(c, "indexing node in data LEB %d:%d",
+ lnum, snod->offs);
+ goto out_destroy;
+ }
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_idx_node *idx = snod->node;
+
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ level = le16_to_cpu(idx->level);
+ }
+
+ found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
+ snod->offs, is_idx);
+ if (found) {
+ if (found < 0)
+ goto out_destroy;
+ used += ALIGN(snod->len, 8);
+ }
+ }
+
+ free = c->leb_size - sleb->endpt;
+ dirty = sleb->endpt - used;
+
+ if (free > c->leb_size || free < 0 || dirty > c->leb_size ||
+ dirty < 0) {
+ ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d",
+ lnum, free, dirty);
+ goto out_destroy;
+ }
+
+ if (lp->free + lp->dirty == c->leb_size &&
+ free + dirty == c->leb_size)
+ if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
+ (!is_idx && free == c->leb_size) ||
+ lp->free == c->leb_size) {
+ /*
+ * Empty or freeable LEBs could contain index
+ * nodes from an uncompleted commit due to an
+ * unclean unmount. Or they could be empty for
+ * the same reason. Or it may simply not have been
+ * unmapped.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ is_idx = 0;
+ }
+
+ if (is_idx && lp->free + lp->dirty == free + dirty &&
+ lnum != c->ihead_lnum) {
+ /*
+ * After an unclean unmount, an index LEB could have a different
+ * amount of free space than the value recorded by lprops. That
+ * is because the in-the-gaps method may use free space or
+ * create free space (as a side-effect of using ubi_leb_change
+ * and not writing the whole LEB). The incorrect free space
+ * value is not a problem because the index is only ever
+ * allocated empty LEBs, so there will never be an attempt to
+ * write to the free space at the end of an index LEB - except
+ * by the in-the-gaps method for which it is not a problem.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ }
+
+ if (lp->free != free || lp->dirty != dirty)
+ goto out_print;
+
+ if (is_idx && !(lp->flags & LPROPS_INDEX)) {
+ if (free == c->leb_size)
+ /* Free but not unmapped LEB, it's fine */
+ is_idx = 0;
+ else {
+ ubifs_err(c, "indexing node without indexing flag");
+ goto out_print;
+ }
+ }
+
+ if (!is_idx && (lp->flags & LPROPS_INDEX)) {
+ ubifs_err(c, "data node with indexing flag");
+ goto out_print;
+ }
+
+ if (free == c->leb_size)
+ lst->empty_lebs += 1;
+
+ if (is_idx)
+ lst->idx_lebs += 1;
+
+ if (!(lp->flags & LPROPS_INDEX))
+ lst->total_used += c->leb_size - free - dirty;
+ lst->total_free += free;
+ lst->total_dirty += dirty;
+
+ if (!(lp->flags & LPROPS_INDEX)) {
+ int spc = free + dirty;
+
+ if (spc < c->dead_wm)
+ lst->total_dead += spc;
+ else
+ lst->total_dark += ubifs_calc_dark(c, spc);
+ }
+
+ ubifs_scan_destroy(sleb);
+ vfree(buf);
+ return LPT_SCAN_CONTINUE;
+
+out_print:
+ ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
+ lnum, lp->free, lp->dirty, lp->flags, free, dirty);
+ ubifs_dump_leb(c, lnum);
+out_destroy:
+ ubifs_scan_destroy(sleb);
+ ret = -EINVAL;
+out:
+ vfree(buf);
+ return ret;
+}
+
+/**
+ * dbg_check_lprops - check all LEB properties.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks all LEB properties and makes sure they are all correct.
+ * It returns zero if everything is fine, %-EINVAL if there is an inconsistency
+ * and other negative error codes in case of other errors. This function is
+ * called while the file system is locked (because of commit start), so no
+ * additional locking is required. Note that locking the LPT mutex would cause
+ * a circular lock dependency with the TNC mutex.
+ */
+int dbg_check_lprops(struct ubifs_info *c)
+{
+ int i, err;
+ struct ubifs_lp_stats lst;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ /*
+ * As we are going to scan the media, the write buffers have to be
+ * synchronized.
+ */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+
+ memset(&lst, 0, sizeof(struct ubifs_lp_stats));
+ err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
+ (ubifs_lpt_scan_callback)scan_check_cb,
+ &lst);
+ if (err && err != -ENOSPC)
+ goto out;
+
+ if (lst.empty_lebs != c->lst.empty_lebs ||
+ lst.idx_lebs != c->lst.idx_lebs ||
+ lst.total_free != c->lst.total_free ||
+ lst.total_dirty != c->lst.total_dirty ||
+ lst.total_used != c->lst.total_used) {
+ ubifs_err(c, "bad overall accounting");
+ ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
+ lst.empty_lebs, lst.idx_lebs, lst.total_free,
+ lst.total_dirty, lst.total_used);
+ ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
+ c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
+ c->lst.total_dirty, c->lst.total_used);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (lst.total_dead != c->lst.total_dead ||
+ lst.total_dark != c->lst.total_dark) {
+ ubifs_err(c, "bad dead/dark space accounting");
+ ubifs_err(c, "calculated: total_dead %lld, total_dark %lld",
+ lst.total_dead, lst.total_dark);
+ ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld",
+ c->lst.total_dead, c->lst.total_dark);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = dbg_check_cats(c);
+out:
+ return err;
+}
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
new file mode 100644
index 00000000..1889170b
--- /dev/null
+++ b/ubifs-utils/libubifs/lpt.c
@@ -0,0 +1,2451 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the LEB properties tree (LPT) area. The LPT area
+ * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and
+ * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits
+ * between the log and the orphan area.
+ *
+ * The LPT area is like a miniature self-contained file system. It is required
+ * that it never runs out of space, is fast to access and update, and scales
+ * logarithmically. The LEB properties tree is implemented as a wandering tree
+ * much like the TNC, and the LPT area has its own garbage collection.
+ *
+ * The LPT has two slightly different forms called the "small model" and the
+ * "big model". The small model is used when the entire LEB properties table
+ * can be written into a single eraseblock. In that case, garbage collection
+ * consists of just writing the whole table, which therefore makes all other
+ * eraseblocks reusable. In the case of the big model, dirty eraseblocks are
+ * selected for garbage collection, which consists of marking the clean nodes in
+ * that LEB as dirty, and then only the dirty nodes are written out. Also, in
+ * the case of the big model, a table of LEB numbers is saved so that the entire
+ * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first
+ * mounted.
+ */
+
+#include "ubifs.h"
+#include <linux/crc16.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+
+/**
+ * do_calc_lpt_geom - calculate sizes for the LPT area.
+ * @c: the UBIFS file-system description object
+ *
+ * Calculate the sizes of LPT bit fields, nodes, and tree, based on the
+ * properties of the flash and whether LPT is "big" (c->big_lpt).
+ */
+static void do_calc_lpt_geom(struct ubifs_info *c)
+{
+ int i, n, bits, per_leb_wastage, max_pnode_cnt;
+ long long sz, tot_wastage;
+
+ n = c->main_lebs + c->max_leb_cnt - c->leb_cnt;
+ max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+
+ c->lpt_hght = 1;
+ n = UBIFS_LPT_FANOUT;
+ while (n < max_pnode_cnt) {
+ c->lpt_hght += 1;
+ n <<= UBIFS_LPT_FANOUT_SHIFT;
+ }
+
+ c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+
+ n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT);
+ c->nnode_cnt = n;
+ for (i = 1; i < c->lpt_hght; i++) {
+ n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
+ c->nnode_cnt += n;
+ }
+
+ c->space_bits = fls(c->leb_size) - 3;
+ c->lpt_lnum_bits = fls(c->lpt_lebs);
+ c->lpt_offs_bits = fls(c->leb_size - 1);
+ c->lpt_spc_bits = fls(c->leb_size);
+
+ n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT);
+ c->pcnt_bits = fls(n - 1);
+
+ c->lnum_bits = fls(c->max_leb_cnt - 1);
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ (c->big_lpt ? c->pcnt_bits : 0) +
+ (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT;
+ c->pnode_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ (c->big_lpt ? c->pcnt_bits : 0) +
+ (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT;
+ c->nnode_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ c->lpt_lebs * c->lpt_spc_bits * 2;
+ c->ltab_sz = (bits + 7) / 8;
+
+ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
+ c->lnum_bits * c->lsave_cnt;
+ c->lsave_sz = (bits + 7) / 8;
+
+ /* Calculate the minimum LPT size */
+ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
+ c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
+ c->lpt_sz += c->ltab_sz;
+ if (c->big_lpt)
+ c->lpt_sz += c->lsave_sz;
+
+ /* Add wastage */
+ sz = c->lpt_sz;
+ per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz);
+ sz += per_leb_wastage;
+ tot_wastage = per_leb_wastage;
+ while (sz > c->leb_size) {
+ sz += per_leb_wastage;
+ sz -= c->leb_size;
+ tot_wastage += per_leb_wastage;
+ }
+ tot_wastage += ALIGN(sz, c->min_io_size) - sz;
+ c->lpt_sz += tot_wastage;
+}
+
+/**
+ * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_calc_lpt_geom(struct ubifs_info *c)
+{
+ int lebs_needed;
+ long long sz;
+
+ do_calc_lpt_geom(c);
+
+ /* Verify that lpt_lebs is big enough */
+ sz = c->lpt_sz * 2; /* Must have at least 2 times the size */
+ lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
+ if (lebs_needed > c->lpt_lebs) {
+ ubifs_err(c, "too few LPT LEBs");
+ return -EINVAL;
+ }
+
+ /* Verify that ltab fits in a single LEB (since ltab is a single node */
+ if (c->ltab_sz > c->leb_size) {
+ ubifs_err(c, "LPT ltab too big");
+ return -EINVAL;
+ }
+
+ c->check_lpt_free = c->big_lpt;
+ return 0;
+}
+
+/**
+ * calc_dflt_lpt_geom - calculate default LPT geometry.
+ * @c: the UBIFS file-system description object
+ * @main_lebs: number of main area LEBs is passed and returned here
+ * @big_lpt: whether the LPT area is "big" is returned here
+ *
+ * The size of the LPT area depends on parameters that themselves are dependent
+ * on the size of the LPT area. This function, successively recalculates the LPT
+ * area geometry until the parameters and resultant geometry are consistent.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs,
+ int *big_lpt)
+{
+ int i, lebs_needed;
+ long long sz;
+
+ /* Start by assuming the minimum number of LPT LEBs */
+ c->lpt_lebs = UBIFS_MIN_LPT_LEBS;
+ c->main_lebs = *main_lebs - c->lpt_lebs;
+ if (c->main_lebs <= 0)
+ return -EINVAL;
+
+ /* And assume we will use the small LPT model */
+ c->big_lpt = 0;
+
+ /*
+ * Calculate the geometry based on assumptions above and then see if it
+ * makes sense
+ */
+ do_calc_lpt_geom(c);
+
+ /* Small LPT model must have lpt_sz < leb_size */
+ if (c->lpt_sz > c->leb_size) {
+ /* Nope, so try again using big LPT model */
+ c->big_lpt = 1;
+ do_calc_lpt_geom(c);
+ }
+
+ /* Now check there are enough LPT LEBs */
+ for (i = 0; i < 64 ; i++) {
+ sz = c->lpt_sz * 4; /* Allow 4 times the size */
+ lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
+ if (lebs_needed > c->lpt_lebs) {
+ /* Not enough LPT LEBs so try again with more */
+ c->lpt_lebs = lebs_needed;
+ c->main_lebs = *main_lebs - c->lpt_lebs;
+ if (c->main_lebs <= 0)
+ return -EINVAL;
+ do_calc_lpt_geom(c);
+ continue;
+ }
+ if (c->ltab_sz > c->leb_size) {
+ ubifs_err(c, "LPT ltab too big");
+ return -EINVAL;
+ }
+ *main_lebs = c->main_lebs;
+ *big_lpt = c->big_lpt;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * pack_bits - pack bit fields end-to-end.
+ * @c: UBIFS file-system description object
+ * @addr: address at which to pack (passed and next address returned)
+ * @pos: bit position at which to pack (passed and next position returned)
+ * @val: value to pack
+ * @nrbits: number of bits of value to pack (1-32)
+ */
+static void pack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, uint32_t val, int nrbits)
+{
+ uint8_t *p = *addr;
+ int b = *pos;
+
+ ubifs_assert(c, nrbits > 0);
+ ubifs_assert(c, nrbits <= 32);
+ ubifs_assert(c, *pos >= 0);
+ ubifs_assert(c, *pos < 8);
+ ubifs_assert(c, (val >> nrbits) == 0 || nrbits == 32);
+ if (b) {
+ *p |= ((uint8_t)val) << b;
+ nrbits += b;
+ if (nrbits > 8) {
+ *++p = (uint8_t)(val >>= (8 - b));
+ if (nrbits > 16) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 24) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 32)
+ *++p = (uint8_t)(val >>= 8);
+ }
+ }
+ }
+ } else {
+ *p = (uint8_t)val;
+ if (nrbits > 8) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 16) {
+ *++p = (uint8_t)(val >>= 8);
+ if (nrbits > 24)
+ *++p = (uint8_t)(val >>= 8);
+ }
+ }
+ }
+ b = nrbits & 7;
+ if (b == 0)
+ p++;
+ *addr = p;
+ *pos = b;
+}
+
+/**
+ * ubifs_unpack_bits - unpack bit fields.
+ * @c: UBIFS file-system description object
+ * @addr: address at which to unpack (passed and next address returned)
+ * @pos: bit position at which to unpack (passed and next position returned)
+ * @nrbits: number of bits of value to unpack (1-32)
+ *
+ * This functions returns the value unpacked.
+ */
+uint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, int nrbits)
+{
+ const int k = 32 - nrbits;
+ uint8_t *p = *addr;
+ int b = *pos;
+ uint32_t val;
+ const int bytes = (nrbits + b + 7) >> 3;
+
+ ubifs_assert(c, nrbits > 0);
+ ubifs_assert(c, nrbits <= 32);
+ ubifs_assert(c, *pos >= 0);
+ ubifs_assert(c, *pos < 8);
+ if (b) {
+ switch (bytes) {
+ case 2:
+ val = p[1];
+ break;
+ case 3:
+ val = p[1] | ((uint32_t)p[2] << 8);
+ break;
+ case 4:
+ val = p[1] | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[3] << 16);
+ break;
+ case 5:
+ val = p[1] | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[3] << 16) |
+ ((uint32_t)p[4] << 24);
+ }
+ val <<= (8 - b);
+ val |= *p >> b;
+ nrbits += b;
+ } else {
+ switch (bytes) {
+ case 1:
+ val = p[0];
+ break;
+ case 2:
+ val = p[0] | ((uint32_t)p[1] << 8);
+ break;
+ case 3:
+ val = p[0] | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16);
+ break;
+ case 4:
+ val = p[0] | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16) |
+ ((uint32_t)p[3] << 24);
+ break;
+ }
+ }
+ val <<= k;
+ val >>= k;
+ b = nrbits & 7;
+ p += nrbits >> 3;
+ *addr = p;
+ *pos = b;
+ ubifs_assert(c, (val >> nrbits) == 0 || nrbits - b == 32);
+ return val;
+}
+
+/**
+ * ubifs_pack_pnode - pack all the bit fields of a pnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @pnode: pnode to pack
+ */
+void ubifs_pack_pnode(struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(c, &addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS);
+ if (c->big_lpt)
+ pack_bits(c, &addr, &pos, pnode->num, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ pack_bits(c, &addr, &pos, pnode->lprops[i].free >> 3,
+ c->space_bits);
+ pack_bits(c, &addr, &pos, pnode->lprops[i].dirty >> 3,
+ c->space_bits);
+ if (pnode->lprops[i].flags & LPROPS_INDEX)
+ pack_bits(c, &addr, &pos, 1, 1);
+ else
+ pack_bits(c, &addr, &pos, 0, 1);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->pnode_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_nnode - pack all the bit fields of a nnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @nnode: nnode to pack
+ */
+void ubifs_pack_nnode(struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(c, &addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS);
+ if (c->big_lpt)
+ pack_bits(c, &addr, &pos, nnode->num, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum = nnode->nbranch[i].lnum;
+
+ if (lnum == 0)
+ lnum = c->lpt_last + 1;
+ pack_bits(c, &addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits);
+ pack_bits(c, &addr, &pos, nnode->nbranch[i].offs,
+ c->lpt_offs_bits);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->nnode_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_ltab - pack the LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @ltab: LPT's own lprops table to pack
+ */
+void ubifs_pack_ltab(struct ubifs_info *c, void *buf,
+ struct ubifs_lpt_lprops *ltab)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(c, &addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS);
+ for (i = 0; i < c->lpt_lebs; i++) {
+ pack_bits(c, &addr, &pos, ltab[i].free, c->lpt_spc_bits);
+ pack_bits(c, &addr, &pos, ltab[i].dirty, c->lpt_spc_bits);
+ }
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->ltab_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_pack_lsave - pack the LPT's save table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer into which to pack
+ * @lsave: LPT's save table to pack
+ */
+void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0;
+ uint16_t crc;
+
+ pack_bits(c, &addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS);
+ for (i = 0; i < c->lsave_cnt; i++)
+ pack_bits(c, &addr, &pos, lsave[i], c->lnum_bits);
+ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ c->lsave_sz - UBIFS_LPT_CRC_BYTES);
+ addr = buf;
+ pos = 0;
+ pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS);
+}
+
+/**
+ * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to which to add dirty space
+ * @dirty: amount of dirty space to add
+ */
+void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty)
+{
+ if (!dirty || !lnum)
+ return;
+ dbg_lp("LEB %d add %d to %d",
+ lnum, dirty, c->ltab[lnum - c->lpt_first].dirty);
+ ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].dirty += dirty;
+}
+
+/**
+ * set_ltab - set LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @free: amount of free space
+ * @dirty: amount of dirty space
+ */
+static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
+{
+ dbg_lp("LEB %d free %d dirty %d to %d %d",
+ lnum, c->ltab[lnum - c->lpt_first].free,
+ c->ltab[lnum - c->lpt_first].dirty, free, dirty);
+ ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].free = free;
+ c->ltab[lnum - c->lpt_first].dirty = dirty;
+}
+
+/**
+ * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode for which to add dirt
+ */
+void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode)
+{
+ struct ubifs_nnode *np = nnode->parent;
+
+ if (np)
+ ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum,
+ c->nnode_sz);
+ else {
+ ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz);
+ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) {
+ c->lpt_drty_flgs |= LTAB_DIRTY;
+ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz);
+ }
+ }
+}
+
+/**
+ * add_pnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode for which to add dirt
+ */
+static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
+ c->pnode_sz);
+}
+
+/**
+ * calc_nnode_num - calculate nnode number.
+ * @row: the row in the tree (root is zero)
+ * @col: the column in the row (leftmost is zero)
+ *
+ * The nnode number is a number that uniquely identifies a nnode and can be used
+ * easily to traverse the tree from the root to that nnode.
+ *
+ * This function calculates and returns the nnode number for the nnode at @row
+ * and @col.
+ */
+static int calc_nnode_num(int row, int col)
+{
+ int num, bits;
+
+ num = 1;
+ while (row--) {
+ bits = (col & (UBIFS_LPT_FANOUT - 1));
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= bits;
+ }
+ return num;
+}
+
+/**
+ * calc_nnode_num_from_parent - calculate nnode number.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * The nnode number is a number that uniquely identifies a nnode and can be used
+ * easily to traverse the tree from the root to that nnode.
+ *
+ * This function calculates and returns the nnode number based on the parent's
+ * nnode number and the index in parent.
+ */
+static int calc_nnode_num_from_parent(const struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ int num, shft;
+
+ if (!parent)
+ return 1;
+ shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT;
+ num = parent->num ^ (1 << shft);
+ num |= (UBIFS_LPT_FANOUT + iip) << shft;
+ return num;
+}
+
+/**
+ * calc_pnode_num_from_parent - calculate pnode number.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * The pnode number is a number that uniquely identifies a pnode and can be used
+ * easily to traverse the tree from the root to that pnode.
+ *
+ * This function calculates and returns the pnode number based on the parent's
+ * nnode number and the index in parent.
+ */
+static int calc_pnode_num_from_parent(const struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0;
+
+ for (i = 0; i < n; i++) {
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= pnum & (UBIFS_LPT_FANOUT - 1);
+ pnum >>= UBIFS_LPT_FANOUT_SHIFT;
+ }
+ num <<= UBIFS_LPT_FANOUT_SHIFT;
+ num |= iip;
+ return num;
+}
+
+/**
+ * ubifs_create_lpt - create lpt acccording to lprops array.
+ * @c: UBIFS file-system description object
+ * @lps: array of logical eraseblock properties
+ * @lp_cnt: the length of @lps
+ * @hash: hash of the LPT is returned here
+ *
+ * This function creates lpt, the pnode will be initialized based on
+ * corresponding elements in @lps. If there are no corresponding lprops
+ * (eg. @lp_cnt is smaller than @c->main_lebs), the LEB property is set
+ * as free state.
+ */
+int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
+ u8 *hash)
+{
+ int lnum, err = 0, i, j, cnt, len, alen, row;
+ int blnum, boffs, bsz, bcnt;
+ struct ubifs_pnode *pnode = NULL;
+ struct ubifs_nnode *nnode = NULL;
+ void *buf = NULL, *p;
+ struct ubifs_lpt_lprops *ltab = NULL;
+ int *lsave = NULL;
+ struct shash_desc *desc;
+
+ desc = ubifs_hash_get_desc(c);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_KERNEL);
+ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL);
+ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL);
+ buf = vmalloc(c->leb_size);
+ ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops),
+ c->lpt_lebs));
+ if (!pnode || !nnode || !buf || !ltab || !lsave) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ubifs_assert(c, !c->ltab);
+ c->ltab = ltab; /* Needed by set_ltab */
+
+ /* Initialize LPT's own lprops */
+ for (i = 0; i < c->lpt_lebs; i++) {
+ ltab[i].free = c->leb_size;
+ ltab[i].dirty = 0;
+ ltab[i].tgc = 0;
+ ltab[i].cmt = 0;
+ }
+
+ lnum = c->lpt_first;
+ p = buf;
+ len = 0;
+ /* Number of leaf nodes (pnodes) */
+ cnt = c->pnode_cnt;
+
+ /*
+ * To calculate the internal node branches, we keep information about
+ * the level below.
+ */
+ blnum = lnum; /* LEB number of level below */
+ boffs = 0; /* Offset of level below */
+ bcnt = cnt; /* Number of nodes in level below */
+ bsz = c->pnode_sz; /* Size of nodes in level below */
+
+ /* Add all pnodes */
+ for (i = 0; i < cnt; i++) {
+ if (len + c->pnode_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+ /* Fill in the pnode */
+ for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
+ int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j;
+
+ if (k < lp_cnt) {
+ pnode->lprops[j].free = lps[k].free;
+ pnode->lprops[j].dirty = lps[k].dirty;
+ pnode->lprops[j].flags = lps[k].flags;
+ } else {
+ pnode->lprops[j].free = c->leb_size;
+ pnode->lprops[j].dirty = 0;
+ pnode->lprops[j].flags = 0;
+ }
+ }
+ ubifs_pack_pnode(c, p, pnode);
+ err = ubifs_shash_update(c, desc, p, c->pnode_sz);
+ if (err)
+ goto out;
+
+ p += c->pnode_sz;
+ len += c->pnode_sz;
+ /*
+ * pnodes are simply numbered left to right starting at zero,
+ * which means the pnode number can be used easily to traverse
+ * down the tree to the corresponding pnode.
+ */
+ pnode->num += 1;
+ }
+
+ row = 0;
+ for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT)
+ row += 1;
+ /* Add all nnodes, one level at a time */
+ while (1) {
+ /* Number of internal nodes (nnodes) at next level */
+ cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ if (len + c->nnode_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen,
+ alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+ /* Only 1 nnode at this level, so it is the root */
+ if (cnt == 1) {
+ c->lpt_lnum = lnum;
+ c->lpt_offs = len;
+ }
+ /* Set branches to the level below */
+ for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
+ if (bcnt) {
+ if (boffs + bsz > c->leb_size) {
+ blnum += 1;
+ boffs = 0;
+ }
+ nnode->nbranch[j].lnum = blnum;
+ nnode->nbranch[j].offs = boffs;
+ boffs += bsz;
+ bcnt--;
+ } else {
+ nnode->nbranch[j].lnum = 0;
+ nnode->nbranch[j].offs = 0;
+ }
+ }
+ nnode->num = calc_nnode_num(row, i);
+ ubifs_pack_nnode(c, p, nnode);
+ p += c->nnode_sz;
+ len += c->nnode_sz;
+ }
+ /* Only 1 nnode at this level, so it is the root */
+ if (cnt == 1)
+ break;
+ /* Update the information about the level below */
+ bcnt = cnt;
+ bsz = c->nnode_sz;
+ row -= 1;
+ }
+
+ if (c->big_lpt) {
+ /* Need to add LPT's save table */
+ if (len + c->lsave_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+
+ c->lsave_lnum = lnum;
+ c->lsave_offs = len;
+
+ for (i = 0; i < c->lsave_cnt && i < c->main_lebs; i++)
+ lsave[i] = c->main_first + i;
+ for (; i < c->lsave_cnt; i++)
+ lsave[i] = c->main_first;
+
+ ubifs_pack_lsave(c, p, lsave);
+ p += c->lsave_sz;
+ len += c->lsave_sz;
+ }
+
+ /* Need to add LPT's own LEB properties table */
+ if (len + c->ltab_sz > c->leb_size) {
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum++, buf, alen);
+ if (err)
+ goto out;
+ p = buf;
+ len = 0;
+ }
+
+ c->ltab_lnum = lnum;
+ c->ltab_offs = len;
+
+ /* Update ltab before packing it */
+ len += c->ltab_sz;
+ alen = ALIGN(len, c->min_io_size);
+ set_ltab(c, lnum, c->leb_size - alen, alen - len);
+
+ ubifs_pack_ltab(c, p, ltab);
+ p += c->ltab_sz;
+
+ /* Write remaining buffer */
+ memset(p, 0xff, alen - len);
+ err = ubifs_leb_change(c, lnum, buf, alen);
+ if (err)
+ goto out;
+
+ err = ubifs_shash_final(c, desc, hash);
+ if (err)
+ goto out;
+
+ c->nhead_lnum = lnum;
+ c->nhead_offs = ALIGN(len, c->min_io_size);
+
+ dbg_lp("space_bits %d", c->space_bits);
+ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
+ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
+ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
+ dbg_lp("pcnt_bits %d", c->pcnt_bits);
+ dbg_lp("lnum_bits %d", c->lnum_bits);
+ dbg_lp("pnode_sz %d", c->pnode_sz);
+ dbg_lp("nnode_sz %d", c->nnode_sz);
+ dbg_lp("ltab_sz %d", c->ltab_sz);
+ dbg_lp("lsave_sz %d", c->lsave_sz);
+ dbg_lp("lsave_cnt %d", c->lsave_cnt);
+ dbg_lp("lpt_hght %d", c->lpt_hght);
+ dbg_lp("big_lpt %u", c->big_lpt);
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+out:
+ c->ltab = NULL;
+ kfree(desc);
+ kfree(lsave);
+ vfree(ltab);
+ vfree(buf);
+ kfree(nnode);
+ kfree(pnode);
+ return err;
+}
+
+/**
+ * ubifs_create_dflt_lpt - create default LPT.
+ * @c: UBIFS file-system description object
+ * @main_lebs: number of main area LEBs is passed and returned here
+ * @lpt_first: LEB number of first LPT LEB
+ * @lpt_lebs: number of LEBs for LPT is passed and returned here
+ * @big_lpt: use big LPT model is passed and returned here
+ * @hash: hash of the LPT is returned here
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
+ int *lpt_lebs, int *big_lpt, u8 *hash)
+{
+ int node_sz, iopos, err = 0;
+ struct ubifs_lprops lps[2];
+
+ err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
+ if (err)
+ return err;
+ *lpt_lebs = c->lpt_lebs;
+
+ /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */
+ c->lpt_first = lpt_first;
+ /* Needed by 'set_ltab()' */
+ c->lpt_last = lpt_first + c->lpt_lebs - 1;
+ /* Needed by 'ubifs_pack_lsave()' */
+ c->main_first = c->leb_cnt - *main_lebs;
+
+ /*
+ * The first pnode contains the LEB properties for the LEBs that contain
+ * the root inode node and the root index node of the index tree.
+ */
+ node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8);
+ iopos = ALIGN(node_sz, c->min_io_size);
+ lps[0].free = c->leb_size - iopos;
+ lps[0].dirty = iopos - node_sz;
+ lps[0].flags = LPROPS_INDEX;
+
+ node_sz = UBIFS_INO_NODE_SZ;
+ iopos = ALIGN(node_sz, c->min_io_size);
+ lps[1].free = c->leb_size - iopos;
+ lps[1].dirty = iopos - node_sz;
+ lps[1].flags = 0;
+
+ return ubifs_create_lpt(c, lps, 2, hash);
+}
+
+/**
+ * update_cats - add LEB properties of a pnode to LEB category lists and heaps.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode
+ *
+ * When a pnode is loaded into memory, the LEB properties it contains are added,
+ * by this function, to the LEB category lists and heaps.
+ */
+static void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ int i;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK;
+ int lnum = pnode->lprops[i].lnum;
+
+ if (!lnum)
+ return;
+ ubifs_add_to_cat(c, &pnode->lprops[i], cat);
+ }
+}
+
+/**
+ * replace_cats - add LEB properties of a pnode to LEB category lists and heaps.
+ * @c: UBIFS file-system description object
+ * @old_pnode: pnode copied
+ * @new_pnode: pnode copy
+ *
+ * During commit it is sometimes necessary to copy a pnode
+ * (see dirty_cow_pnode). When that happens, references in
+ * category lists and heaps must be replaced. This function does that.
+ */
+static void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode,
+ struct ubifs_pnode *new_pnode)
+{
+ int i;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (!new_pnode->lprops[i].lnum)
+ return;
+ ubifs_replace_cat(c, &old_pnode->lprops[i],
+ &new_pnode->lprops[i]);
+ }
+}
+
+/**
+ * check_lpt_crc - check LPT node crc is correct.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing node
+ * @len: length of node
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int check_lpt_crc(const struct ubifs_info *c, void *buf, int len)
+{
+ int pos = 0;
+ uint8_t *addr = buf;
+ uint16_t crc, calc_crc;
+
+ crc = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_CRC_BITS);
+ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ len - UBIFS_LPT_CRC_BYTES);
+ if (crc != calc_crc) {
+ ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx",
+ crc, calc_crc);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * check_lpt_type - check LPT node type is correct.
+ * @c: UBIFS file-system description object
+ * @addr: address of type bit field is passed and returned updated here
+ * @pos: position of type bit field is passed and returned updated here
+ * @type: expected type
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int check_lpt_type(const struct ubifs_info *c, uint8_t **addr,
+ int *pos, int type)
+{
+ int node_type;
+
+ node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS);
+ if (node_type != type) {
+ ubifs_err(c, "invalid type (%d) in LPT node type %d",
+ node_type, type);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * unpack_pnode - unpack a pnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing packed pnode to unpack
+ * @pnode: pnode structure to fill
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_pnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE);
+ if (err)
+ return err;
+ if (c->big_lpt)
+ pnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = ubifs_unpack_bits(c, &addr, &pos, c->space_bits);
+ lprops->free <<= 3;
+ lprops->dirty = ubifs_unpack_bits(c, &addr, &pos, c->space_bits);
+ lprops->dirty <<= 3;
+
+ if (ubifs_unpack_bits(c, &addr, &pos, 1))
+ lprops->flags = LPROPS_INDEX;
+ else
+ lprops->flags = 0;
+ lprops->flags |= ubifs_categorize_lprops(c, lprops);
+ }
+ err = check_lpt_crc(c, buf, c->pnode_sz);
+ return err;
+}
+
+/**
+ * ubifs_unpack_nnode - unpack a nnode.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing packed nnode to unpack
+ * @nnode: nnode structure to fill
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE);
+ if (err)
+ return err;
+ if (c->big_lpt)
+ nnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum;
+
+ lnum = ubifs_unpack_bits(c, &addr, &pos, c->lpt_lnum_bits) +
+ c->lpt_first;
+ if (lnum == c->lpt_last + 1)
+ lnum = 0;
+ nnode->nbranch[i].lnum = lnum;
+ nnode->nbranch[i].offs = ubifs_unpack_bits(c, &addr, &pos,
+ c->lpt_offs_bits);
+ }
+ err = check_lpt_crc(c, buf, c->nnode_sz);
+ return err;
+}
+
+/**
+ * unpack_ltab - unpack the LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer from which to unpack
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_ltab(const struct ubifs_info *c, void *buf)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB);
+ if (err)
+ return err;
+ for (i = 0; i < c->lpt_lebs; i++) {
+ int free = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits);
+ int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits);
+
+ if (free < 0 || free > c->leb_size || dirty < 0 ||
+ dirty > c->leb_size || free + dirty > c->leb_size)
+ return -EINVAL;
+
+ c->ltab[i].free = free;
+ c->ltab[i].dirty = dirty;
+ c->ltab[i].tgc = 0;
+ c->ltab[i].cmt = 0;
+ }
+ err = check_lpt_crc(c, buf, c->ltab_sz);
+ return err;
+}
+
+/**
+ * unpack_lsave - unpack the LPT's save table.
+ * @c: UBIFS file-system description object
+ * @buf: buffer from which to unpack
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int unpack_lsave(const struct ubifs_info *c, void *buf)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int i, pos = 0, err;
+
+ err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE);
+ if (err)
+ return err;
+ for (i = 0; i < c->lsave_cnt; i++) {
+ int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits);
+
+ if (lnum < c->main_first || lnum >= c->leb_cnt)
+ return -EINVAL;
+ c->lsave[i] = lnum;
+ }
+ err = check_lpt_crc(c, buf, c->lsave_sz);
+ return err;
+}
+
+/**
+ * validate_nnode - validate a nnode.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode to validate
+ * @parent: parent nnode (or NULL for the root nnode)
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i, lvl, max_offs;
+
+ if (c->big_lpt) {
+ int num = calc_nnode_num_from_parent(c, parent, iip);
+
+ if (nnode->num != num)
+ return -EINVAL;
+ }
+ lvl = parent ? parent->level - 1 : c->lpt_hght;
+ if (lvl < 1)
+ return -EINVAL;
+ if (lvl == 1)
+ max_offs = c->leb_size - c->pnode_sz;
+ else
+ max_offs = c->leb_size - c->nnode_sz;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int lnum = nnode->nbranch[i].lnum;
+ int offs = nnode->nbranch[i].offs;
+
+ if (lnum == 0) {
+ if (offs != 0)
+ return -EINVAL;
+ continue;
+ }
+ if (lnum < c->lpt_first || lnum > c->lpt_last)
+ return -EINVAL;
+ if (offs < 0 || offs > max_offs)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * validate_pnode - validate a pnode.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to validate
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent, int iip)
+{
+ int i;
+
+ if (c->big_lpt) {
+ int num = calc_pnode_num_from_parent(c, parent, iip);
+
+ if (pnode->num != num)
+ return -EINVAL;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ int free = pnode->lprops[i].free;
+ int dirty = pnode->lprops[i].dirty;
+
+ if (free < 0 || free > c->leb_size || free % c->min_io_size ||
+ (free & 7))
+ return -EINVAL;
+ if (dirty < 0 || dirty > c->leb_size || (dirty & 7))
+ return -EINVAL;
+ if (dirty + free > c->leb_size)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * set_pnode_lnum - set LEB numbers on a pnode.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to update
+ *
+ * This function calculates the LEB numbers for the LEB properties it contains
+ * based on the pnode number.
+ */
+static void set_pnode_lnum(const struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ int i, lnum;
+
+ lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (lnum >= c->leb_cnt)
+ return;
+ pnode->lprops[i].lnum = lnum++;
+ }
+}
+
+/**
+ * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode (or NULL for the root)
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch = NULL;
+ struct ubifs_nnode *nnode = NULL;
+ void *buf = c->lpt_nod_buf;
+ int err, lnum, offs;
+
+ if (parent) {
+ branch = &parent->nbranch[iip];
+ lnum = branch->lnum;
+ offs = branch->offs;
+ } else {
+ lnum = c->lpt_lnum;
+ offs = c->lpt_offs;
+ }
+ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS);
+ if (!nnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (lnum == 0) {
+ /*
+ * This nnode was not written which just means that the LEB
+ * properties in the subtree below it describe empty LEBs. We
+ * make the nnode as though we had read it, which in fact means
+ * doing almost nothing.
+ */
+ if (c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ } else {
+ err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1);
+ if (err)
+ goto out;
+ err = ubifs_unpack_nnode(c, buf, nnode);
+ if (err)
+ goto out;
+ }
+ err = validate_nnode(c, nnode, parent, iip);
+ if (err)
+ goto out;
+ if (!c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ if (parent) {
+ branch->nnode = nnode;
+ nnode->level = parent->level - 1;
+ } else {
+ c->nroot = nnode;
+ nnode->level = c->lpt_hght;
+ }
+ nnode->parent = parent;
+ nnode->iip = iip;
+ return 0;
+
+out:
+ ubifs_err(c, "error %d reading nnode at %d:%d", err, lnum, offs);
+ dump_stack();
+ kfree(nnode);
+ return err;
+}
+
+/**
+ * read_pnode - read a pnode from flash and link it to the tree in memory.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode = NULL;
+ void *buf = c->lpt_nod_buf;
+ int err, lnum, offs;
+
+ branch = &parent->nbranch[iip];
+ lnum = branch->lnum;
+ offs = branch->offs;
+ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS);
+ if (!pnode)
+ return -ENOMEM;
+
+ if (lnum == 0) {
+ /*
+ * This pnode was not written which just means that the LEB
+ * properties in it describe empty LEBs. We make the pnode as
+ * though we had read it.
+ */
+ int i;
+
+ if (c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = c->leb_size;
+ lprops->flags = ubifs_categorize_lprops(c, lprops);
+ }
+ } else {
+ err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_pnode(c, buf, pnode);
+ if (err)
+ goto out;
+ }
+ err = validate_pnode(c, pnode, parent, iip);
+ if (err)
+ goto out;
+ if (!c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ branch->pnode = pnode;
+ pnode->parent = parent;
+ pnode->iip = iip;
+ set_pnode_lnum(c, pnode);
+ c->pnodes_have += 1;
+ return 0;
+
+out:
+ ubifs_err(c, "error %d reading pnode at %d:%d", err, lnum, offs);
+ ubifs_dump_pnode(c, pnode, parent, iip);
+ dump_stack();
+ ubifs_err(c, "calc num: %d", calc_pnode_num_from_parent(c, parent, iip));
+ kfree(pnode);
+ return err;
+}
+
+/**
+ * read_ltab - read LPT's own lprops table.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_ltab(struct ubifs_info *c)
+{
+ int err;
+ void *buf;
+
+ buf = vmalloc(c->ltab_sz);
+ if (!buf)
+ return -ENOMEM;
+ err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_ltab(c, buf);
+out:
+ vfree(buf);
+ return err;
+}
+
+/**
+ * read_lsave - read LPT's save table.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int read_lsave(struct ubifs_info *c)
+{
+ int err, i;
+ void *buf;
+
+ buf = vmalloc(c->lsave_sz);
+ if (!buf)
+ return -ENOMEM;
+ err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
+ c->lsave_sz, 1);
+ if (err)
+ goto out;
+ err = unpack_lsave(c, buf);
+ if (err)
+ goto out;
+ for (i = 0; i < c->lsave_cnt; i++) {
+ int lnum = c->lsave[i];
+ struct ubifs_lprops *lprops;
+
+ /*
+ * Due to automatic resizing, the values in the lsave table
+ * could be beyond the volume size - just ignore them.
+ */
+ if (lnum >= c->leb_cnt)
+ continue;
+ lprops = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+ }
+out:
+ vfree(buf);
+ return err;
+}
+
+/**
+ * ubifs_get_nnode - get a nnode.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode (or NULL for the root)
+ * @iip: index in parent
+ *
+ * This function returns a pointer to the nnode on success or a negative error
+ * code on failure.
+ */
+struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_nnode *nnode;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ nnode = branch->nnode;
+ if (nnode)
+ return nnode;
+ err = ubifs_read_nnode(c, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ return branch->nnode;
+}
+
+/**
+ * ubifs_get_pnode - get a pnode.
+ * @c: UBIFS file-system description object
+ * @parent: parent nnode
+ * @iip: index in parent
+ *
+ * This function returns a pointer to the pnode on success or a negative error
+ * code on failure.
+ */
+struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ pnode = branch->pnode;
+ if (pnode)
+ return pnode;
+ err = read_pnode(c, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ update_cats(c, branch->pnode);
+ return branch->pnode;
+}
+
+/**
+ * ubifs_pnode_lookup - lookup a pnode in the LPT.
+ * @c: UBIFS file-system description object
+ * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT)
+ *
+ * This function returns a pointer to the pnode on success or a negative
+ * error code on failure.
+ */
+struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
+{
+ int err, h, iip, shft;
+ struct ubifs_nnode *nnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ i <<= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = c->nroot;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ return ubifs_get_pnode(c, nnode, iip);
+}
+
+/**
+ * ubifs_lpt_lookup - lookup LEB properties in the LPT.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to lookup
+ *
+ * This function returns a pointer to the LEB properties on success or a
+ * negative error code on failure.
+ */
+struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
+{
+ int i, iip;
+ struct ubifs_pnode *pnode;
+
+ i = lnum - c->main_first;
+ pnode = ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
+ pnode->lprops[iip].free, pnode->lprops[iip].dirty,
+ pnode->lprops[iip].flags);
+ return &pnode->lprops[iip];
+}
+
+/**
+ * dirty_cow_nnode - ensure a nnode is not being committed.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode to check
+ *
+ * Returns dirtied nnode on success or negative error code on failure.
+ */
+static struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode)
+{
+ struct ubifs_nnode *n;
+ int i;
+
+ if (!test_bit(COW_CNODE, &nnode->flags)) {
+ /* nnode is not being committed */
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ }
+ return nnode;
+ }
+
+ /* nnode is being committed, so copy it */
+ n = kmemdup(nnode, sizeof(struct ubifs_nnode), GFP_NOFS);
+ if (unlikely(!n))
+ return ERR_PTR(-ENOMEM);
+
+ n->cnext = NULL;
+ __set_bit(DIRTY_CNODE, &n->flags);
+ __clear_bit(COW_CNODE, &n->flags);
+
+ /* The children now have new parent */
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_nbranch *branch = &n->nbranch[i];
+
+ if (branch->cnode)
+ branch->cnode->parent = n;
+ }
+
+ ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &nnode->flags));
+ __set_bit(OBSOLETE_CNODE, &nnode->flags);
+
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ if (nnode->parent)
+ nnode->parent->nbranch[n->iip].nnode = n;
+ else
+ c->nroot = n;
+ return n;
+}
+
+/**
+ * dirty_cow_pnode - ensure a pnode is not being committed.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to check
+ *
+ * Returns dirtied pnode on success or negative error code on failure.
+ */
+static struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ struct ubifs_pnode *p;
+
+ if (!test_bit(COW_CNODE, &pnode->flags)) {
+ /* pnode is not being committed */
+ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ }
+ return pnode;
+ }
+
+ /* pnode is being committed, so copy it */
+ p = kmemdup(pnode, sizeof(struct ubifs_pnode), GFP_NOFS);
+ if (unlikely(!p))
+ return ERR_PTR(-ENOMEM);
+
+ p->cnext = NULL;
+ __set_bit(DIRTY_CNODE, &p->flags);
+ __clear_bit(COW_CNODE, &p->flags);
+ replace_cats(c, pnode, p);
+
+ ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &pnode->flags));
+ __set_bit(OBSOLETE_CNODE, &pnode->flags);
+
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ pnode->parent->nbranch[p->iip].pnode = p;
+ return p;
+}
+
+/**
+ * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to lookup
+ *
+ * This function returns a pointer to the LEB properties on success or a
+ * negative error code on failure.
+ */
+struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
+{
+ int err, i, h, iip, shft;
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ nnode = c->nroot;
+ nnode = dirty_cow_nnode(c, nnode);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ i = lnum - c->main_first;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ nnode = dirty_cow_nnode(c, nnode);
+ if (IS_ERR(nnode))
+ return ERR_CAST(nnode);
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ pnode = ubifs_get_pnode(c, nnode, iip);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ pnode = dirty_cow_pnode(c, pnode);
+ if (IS_ERR(pnode))
+ return ERR_CAST(pnode);
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
+ pnode->lprops[iip].free, pnode->lprops[iip].dirty,
+ pnode->lprops[iip].flags);
+ ubifs_assert(c, test_bit(DIRTY_CNODE, &pnode->flags));
+ return &pnode->lprops[iip];
+}
+
+/**
+ * ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes
+ * @c: UBIFS file-system description object
+ * @hash: the returned hash of the LPT pnodes
+ *
+ * This function iterates over the LPT pnodes and creates a hash over them.
+ * Returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash)
+{
+ struct ubifs_nnode *nnode, *nn;
+ struct ubifs_cnode *cnode;
+ struct shash_desc *desc;
+ int iip = 0, i;
+ int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz);
+ void *buf;
+ int err;
+
+ if (!ubifs_authenticated(c))
+ return 0;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return err;
+ }
+
+ desc = ubifs_hash_get_desc(c);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ buf = kmalloc(bufsiz, GFP_NOFS);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ cnode = (struct ubifs_cnode *)c->nroot;
+
+ while (cnode) {
+ nnode = cnode->parent;
+ nn = (struct ubifs_nnode *)cnode;
+ if (cnode->level > 1) {
+ while (iip < UBIFS_LPT_FANOUT) {
+ if (nn->nbranch[iip].lnum == 0) {
+ /* Go right */
+ iip++;
+ continue;
+ }
+
+ nnode = ubifs_get_nnode(c, nn, iip);
+ if (IS_ERR(nnode)) {
+ err = PTR_ERR(nnode);
+ goto out;
+ }
+
+ /* Go down */
+ iip = 0;
+ cnode = (struct ubifs_cnode *)nnode;
+ break;
+ }
+ if (iip < UBIFS_LPT_FANOUT)
+ continue;
+ } else {
+ struct ubifs_pnode *pnode;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (nn->nbranch[i].lnum == 0)
+ continue;
+ pnode = ubifs_get_pnode(c, nn, i);
+ if (IS_ERR(pnode)) {
+ err = PTR_ERR(pnode);
+ goto out;
+ }
+
+ ubifs_pack_pnode(c, buf, pnode);
+ err = ubifs_shash_update(c, desc, buf,
+ c->pnode_sz);
+ if (err)
+ goto out;
+ }
+ }
+ /* Go up and to the right */
+ iip = cnode->iip + 1;
+ cnode = (struct ubifs_cnode *)nnode;
+ }
+
+ err = ubifs_shash_final(c, desc, hash);
+out:
+ kfree(desc);
+ kfree(buf);
+
+ return err;
+}
+
+/**
+ * lpt_check_hash - check the hash of the LPT.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates a hash over all pnodes in the LPT and compares it with
+ * the hash stored in the master node. Returns %0 on success and a negative error
+ * code on failure.
+ */
+static int lpt_check_hash(struct ubifs_info *c)
+{
+ int err;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ if (!ubifs_authenticated(c))
+ return 0;
+
+ err = ubifs_lpt_calc_hash(c, hash);
+ if (err)
+ return err;
+
+ if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) {
+ err = -EPERM;
+ ubifs_err(c, "Failed to authenticate LPT");
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+/**
+ * lpt_init_rd - initialize the LPT for reading.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_init_rd(struct ubifs_info *c)
+{
+ int err, i;
+
+ c->ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops),
+ c->lpt_lebs));
+ if (!c->ltab)
+ return -ENOMEM;
+
+ i = max_t(int, c->nnode_sz, c->pnode_sz);
+ c->lpt_nod_buf = kmalloc(i, GFP_KERNEL);
+ if (!c->lpt_nod_buf)
+ return -ENOMEM;
+
+ for (i = 0; i < LPROPS_HEAP_CNT; i++) {
+ c->lpt_heap[i].arr = kmalloc_array(LPT_HEAP_SZ,
+ sizeof(void *),
+ GFP_KERNEL);
+ if (!c->lpt_heap[i].arr)
+ return -ENOMEM;
+ c->lpt_heap[i].cnt = 0;
+ c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
+ }
+
+ c->dirty_idx.arr = kmalloc_array(LPT_HEAP_SZ, sizeof(void *),
+ GFP_KERNEL);
+ if (!c->dirty_idx.arr)
+ return -ENOMEM;
+ c->dirty_idx.cnt = 0;
+ c->dirty_idx.max_cnt = LPT_HEAP_SZ;
+
+ err = read_ltab(c);
+ if (err)
+ return err;
+
+ err = lpt_check_hash(c);
+ if (err)
+ return err;
+
+ dbg_lp("space_bits %d", c->space_bits);
+ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
+ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
+ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
+ dbg_lp("pcnt_bits %d", c->pcnt_bits);
+ dbg_lp("lnum_bits %d", c->lnum_bits);
+ dbg_lp("pnode_sz %d", c->pnode_sz);
+ dbg_lp("nnode_sz %d", c->nnode_sz);
+ dbg_lp("ltab_sz %d", c->ltab_sz);
+ dbg_lp("lsave_sz %d", c->lsave_sz);
+ dbg_lp("lsave_cnt %d", c->lsave_cnt);
+ dbg_lp("lpt_hght %d", c->lpt_hght);
+ dbg_lp("big_lpt %u", c->big_lpt);
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+
+ return 0;
+}
+
+/**
+ * lpt_init_wr - initialize the LPT for writing.
+ * @c: UBIFS file-system description object
+ *
+ * 'lpt_init_rd()' must have been called already.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_init_wr(struct ubifs_info *c)
+{
+ int err, i;
+
+ c->ltab_cmt = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops),
+ c->lpt_lebs));
+ if (!c->ltab_cmt)
+ return -ENOMEM;
+
+ c->lpt_buf = vmalloc(c->leb_size);
+ if (!c->lpt_buf)
+ return -ENOMEM;
+
+ if (c->big_lpt) {
+ c->lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_NOFS);
+ if (!c->lsave)
+ return -ENOMEM;
+ err = read_lsave(c);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < c->lpt_lebs; i++)
+ if (c->ltab[i].free == c->leb_size) {
+ err = ubifs_leb_unmap(c, i + c->lpt_first);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_lpt_init - initialize the LPT.
+ * @c: UBIFS file-system description object
+ * @rd: whether to initialize lpt for reading
+ * @wr: whether to initialize lpt for writing
+ *
+ * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true
+ * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is
+ * true.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr)
+{
+ int err;
+
+ if (rd) {
+ err = lpt_init_rd(c);
+ if (err)
+ goto out_err;
+ }
+
+ if (wr) {
+ err = lpt_init_wr(c);
+ if (err)
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ if (wr)
+ ubifs_lpt_free(c, 1);
+ if (rd)
+ ubifs_lpt_free(c, 0);
+ return err;
+}
+
+/**
+ * struct lpt_scan_node - somewhere to put nodes while we scan LPT.
+ * @nnode: where to keep a nnode
+ * @pnode: where to keep a pnode
+ * @cnode: where to keep a cnode
+ * @in_tree: is the node in the tree in memory
+ * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in
+ * the tree
+ * @ptr.pnode: ditto for pnode
+ * @ptr.cnode: ditto for cnode
+ */
+struct lpt_scan_node {
+ union {
+ struct ubifs_nnode nnode;
+ struct ubifs_pnode pnode;
+ struct ubifs_cnode cnode;
+ };
+ int in_tree;
+ union {
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct ubifs_cnode *cnode;
+ } ptr;
+};
+
+/**
+ * scan_get_nnode - for the scan, get a nnode from either the tree or flash.
+ * @c: the UBIFS file-system description object
+ * @path: where to put the nnode
+ * @parent: parent of the nnode
+ * @iip: index in parent of the nnode
+ *
+ * This function returns a pointer to the nnode on success or a negative error
+ * code on failure.
+ */
+static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c,
+ struct lpt_scan_node *path,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_nnode *nnode;
+ void *buf = c->lpt_nod_buf;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ nnode = branch->nnode;
+ if (nnode) {
+ path->in_tree = 1;
+ path->ptr.nnode = nnode;
+ return nnode;
+ }
+ nnode = &path->nnode;
+ path->in_tree = 0;
+ path->ptr.nnode = nnode;
+ memset(nnode, 0, sizeof(struct ubifs_nnode));
+ if (branch->lnum == 0) {
+ /*
+ * This nnode was not written which just means that the LEB
+ * properties in the subtree below it describe empty LEBs. We
+ * make the nnode as though we had read it, which in fact means
+ * doing almost nothing.
+ */
+ if (c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ } else {
+ err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
+ c->nnode_sz, 1);
+ if (err)
+ return ERR_PTR(err);
+ err = ubifs_unpack_nnode(c, buf, nnode);
+ if (err)
+ return ERR_PTR(err);
+ }
+ err = validate_nnode(c, nnode, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ if (!c->big_lpt)
+ nnode->num = calc_nnode_num_from_parent(c, parent, iip);
+ nnode->level = parent->level - 1;
+ nnode->parent = parent;
+ nnode->iip = iip;
+ return nnode;
+}
+
+/**
+ * scan_get_pnode - for the scan, get a pnode from either the tree or flash.
+ * @c: the UBIFS file-system description object
+ * @path: where to put the pnode
+ * @parent: parent of the pnode
+ * @iip: index in parent of the pnode
+ *
+ * This function returns a pointer to the pnode on success or a negative error
+ * code on failure.
+ */
+static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c,
+ struct lpt_scan_node *path,
+ struct ubifs_nnode *parent, int iip)
+{
+ struct ubifs_nbranch *branch;
+ struct ubifs_pnode *pnode;
+ void *buf = c->lpt_nod_buf;
+ int err;
+
+ branch = &parent->nbranch[iip];
+ pnode = branch->pnode;
+ if (pnode) {
+ path->in_tree = 1;
+ path->ptr.pnode = pnode;
+ return pnode;
+ }
+ pnode = &path->pnode;
+ path->in_tree = 0;
+ path->ptr.pnode = pnode;
+ memset(pnode, 0, sizeof(struct ubifs_pnode));
+ if (branch->lnum == 0) {
+ /*
+ * This pnode was not written which just means that the LEB
+ * properties in it describe empty LEBs. We make the pnode as
+ * though we had read it.
+ */
+ int i;
+
+ if (c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops * const lprops = &pnode->lprops[i];
+
+ lprops->free = c->leb_size;
+ lprops->flags = ubifs_categorize_lprops(c, lprops);
+ }
+ } else {
+ ubifs_assert(c, branch->lnum >= c->lpt_first &&
+ branch->lnum <= c->lpt_last);
+ ubifs_assert(c, branch->offs >= 0 && branch->offs < c->leb_size);
+ err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
+ c->pnode_sz, 1);
+ if (err)
+ return ERR_PTR(err);
+ err = unpack_pnode(c, buf, pnode);
+ if (err)
+ return ERR_PTR(err);
+ }
+ err = validate_pnode(c, pnode, parent, iip);
+ if (err)
+ return ERR_PTR(err);
+ if (!c->big_lpt)
+ pnode->num = calc_pnode_num_from_parent(c, parent, iip);
+ pnode->parent = parent;
+ pnode->iip = iip;
+ set_pnode_lnum(c, pnode);
+ return pnode;
+}
+
+/**
+ * ubifs_lpt_scan_nolock - scan the LPT.
+ * @c: the UBIFS file-system description object
+ * @start_lnum: LEB number from which to start scanning
+ * @end_lnum: LEB number at which to stop scanning
+ * @scan_cb: callback function called for each lprops
+ * @data: data to be passed to the callback function
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
+ ubifs_lpt_scan_callback scan_cb, void *data)
+{
+ int err = 0, i, h, iip, shft;
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct lpt_scan_node *path;
+
+ if (start_lnum == -1) {
+ start_lnum = end_lnum + 1;
+ if (start_lnum >= c->leb_cnt)
+ start_lnum = c->main_first;
+ }
+
+ ubifs_assert(c, start_lnum >= c->main_first && start_lnum < c->leb_cnt);
+ ubifs_assert(c, end_lnum >= c->main_first && end_lnum < c->leb_cnt);
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return err;
+ }
+
+ path = kmalloc_array(c->lpt_hght + 1, sizeof(struct lpt_scan_node),
+ GFP_NOFS);
+ if (!path)
+ return -ENOMEM;
+
+ path[0].ptr.nnode = c->nroot;
+ path[0].in_tree = 1;
+again:
+ /* Descend to the pnode containing start_lnum */
+ nnode = c->nroot;
+ i = start_lnum - c->main_first;
+ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+ for (h = 1; h < c->lpt_hght; h++) {
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ shft -= UBIFS_LPT_FANOUT_SHIFT;
+ nnode = scan_get_nnode(c, path + h, nnode, iip);
+ if (IS_ERR(nnode)) {
+ err = PTR_ERR(nnode);
+ goto out;
+ }
+ }
+ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+ pnode = scan_get_pnode(c, path + h, nnode, iip);
+ if (IS_ERR(pnode)) {
+ err = PTR_ERR(pnode);
+ goto out;
+ }
+ iip = (i & (UBIFS_LPT_FANOUT - 1));
+
+ /* Loop for each lprops */
+ while (1) {
+ struct ubifs_lprops *lprops = &pnode->lprops[iip];
+ int ret, lnum = lprops->lnum;
+
+ ret = scan_cb(c, lprops, path[h].in_tree, data);
+ if (ret < 0) {
+ err = ret;
+ goto out;
+ }
+ if (ret & LPT_SCAN_ADD) {
+ /* Add all the nodes in path to the tree in memory */
+ for (h = 1; h < c->lpt_hght; h++) {
+ const size_t sz = sizeof(struct ubifs_nnode);
+ struct ubifs_nnode *parent;
+
+ if (path[h].in_tree)
+ continue;
+ nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS);
+ if (!nnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ parent = nnode->parent;
+ parent->nbranch[nnode->iip].nnode = nnode;
+ path[h].ptr.nnode = nnode;
+ path[h].in_tree = 1;
+ path[h + 1].cnode.parent = nnode;
+ }
+ if (path[h].in_tree)
+ ubifs_ensure_cat(c, lprops);
+ else {
+ const size_t sz = sizeof(struct ubifs_pnode);
+ struct ubifs_nnode *parent;
+
+ pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS);
+ if (!pnode) {
+ err = -ENOMEM;
+ goto out;
+ }
+ parent = pnode->parent;
+ parent->nbranch[pnode->iip].pnode = pnode;
+ path[h].ptr.pnode = pnode;
+ path[h].in_tree = 1;
+ update_cats(c, pnode);
+ c->pnodes_have += 1;
+ }
+ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)
+ c->nroot, 0, 0);
+ if (err)
+ goto out;
+ err = dbg_check_cats(c);
+ if (err)
+ goto out;
+ }
+ if (ret & LPT_SCAN_STOP) {
+ err = 0;
+ break;
+ }
+ /* Get the next lprops */
+ if (lnum == end_lnum) {
+ /*
+ * We got to the end without finding what we were
+ * looking for
+ */
+ err = -ENOSPC;
+ goto out;
+ }
+ if (lnum + 1 >= c->leb_cnt) {
+ /* Wrap-around to the beginning */
+ start_lnum = c->main_first;
+ goto again;
+ }
+ if (iip + 1 < UBIFS_LPT_FANOUT) {
+ /* Next lprops is in the same pnode */
+ iip += 1;
+ continue;
+ }
+ /* We need to get the next pnode. Go up until we can go right */
+ iip = pnode->iip;
+ while (1) {
+ h -= 1;
+ ubifs_assert(c, h >= 0);
+ nnode = path[h].ptr.nnode;
+ if (iip + 1 < UBIFS_LPT_FANOUT)
+ break;
+ iip = nnode->iip;
+ }
+ /* Go right */
+ iip += 1;
+ /* Descend to the pnode */
+ h += 1;
+ for (; h < c->lpt_hght; h++) {
+ nnode = scan_get_nnode(c, path + h, nnode, iip);
+ if (IS_ERR(nnode)) {
+ err = PTR_ERR(nnode);
+ goto out;
+ }
+ iip = 0;
+ }
+ pnode = scan_get_pnode(c, path + h, nnode, iip);
+ if (IS_ERR(pnode)) {
+ err = PTR_ERR(pnode);
+ goto out;
+ }
+ iip = 0;
+ }
+out:
+ kfree(path);
+ return err;
+}
+
+/**
+ * dbg_chk_pnode - check a pnode.
+ * @c: the UBIFS file-system description object
+ * @pnode: pnode to check
+ * @col: pnode column
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+ int col)
+{
+ int i;
+
+ if (pnode->num != col) {
+ ubifs_err(c, "pnode num %d expected %d parent num %d iip %d",
+ pnode->num, col, pnode->parent->num, pnode->iip);
+ return -EINVAL;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_lprops *lp, *lprops = &pnode->lprops[i];
+ int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i +
+ c->main_first;
+ int found, cat = lprops->flags & LPROPS_CAT_MASK;
+ struct ubifs_lpt_heap *heap;
+ struct list_head *list = NULL;
+
+ if (lnum >= c->leb_cnt)
+ continue;
+ if (lprops->lnum != lnum) {
+ ubifs_err(c, "bad LEB number %d expected %d",
+ lprops->lnum, lnum);
+ return -EINVAL;
+ }
+ if (lprops->flags & LPROPS_TAKEN) {
+ if (cat != LPROPS_UNCAT) {
+ ubifs_err(c, "LEB %d taken but not uncat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ continue;
+ }
+ if (lprops->flags & LPROPS_INDEX) {
+ switch (cat) {
+ case LPROPS_UNCAT:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FRDI_IDX:
+ break;
+ default:
+ ubifs_err(c, "LEB %d index but cat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ } else {
+ switch (cat) {
+ case LPROPS_UNCAT:
+ case LPROPS_DIRTY:
+ case LPROPS_FREE:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ break;
+ default:
+ ubifs_err(c, "LEB %d not index but cat %d",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ }
+ switch (cat) {
+ case LPROPS_UNCAT:
+ list = &c->uncat_list;
+ break;
+ case LPROPS_EMPTY:
+ list = &c->empty_list;
+ break;
+ case LPROPS_FREEABLE:
+ list = &c->freeable_list;
+ break;
+ case LPROPS_FRDI_IDX:
+ list = &c->frdi_idx_list;
+ break;
+ }
+ found = 0;
+ switch (cat) {
+ case LPROPS_DIRTY:
+ case LPROPS_DIRTY_IDX:
+ case LPROPS_FREE:
+ heap = &c->lpt_heap[cat - 1];
+ if (lprops->hpos < heap->cnt &&
+ heap->arr[lprops->hpos] == lprops)
+ found = 1;
+ break;
+ case LPROPS_UNCAT:
+ case LPROPS_EMPTY:
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ list_for_each_entry(lp, list, list)
+ if (lprops == lp) {
+ found = 1;
+ break;
+ }
+ break;
+ }
+ if (!found) {
+ ubifs_err(c, "LEB %d cat %d not found in cat heap/list",
+ lprops->lnum, cat);
+ return -EINVAL;
+ }
+ switch (cat) {
+ case LPROPS_EMPTY:
+ if (lprops->free != c->leb_size) {
+ ubifs_err(c, "LEB %d cat %d free %d dirty %d",
+ lprops->lnum, cat, lprops->free,
+ lprops->dirty);
+ return -EINVAL;
+ }
+ break;
+ case LPROPS_FREEABLE:
+ case LPROPS_FRDI_IDX:
+ if (lprops->free + lprops->dirty != c->leb_size) {
+ ubifs_err(c, "LEB %d cat %d free %d dirty %d",
+ lprops->lnum, cat, lprops->free,
+ lprops->dirty);
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * dbg_check_lpt_nodes - check nnodes and pnodes.
+ * @c: the UBIFS file-system description object
+ * @cnode: next cnode (nnode or pnode) to check
+ * @row: row of cnode (root is zero)
+ * @col: column of cnode (leftmost is zero)
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
+ int row, int col)
+{
+ struct ubifs_nnode *nnode, *nn;
+ struct ubifs_cnode *cn;
+ int num, iip = 0, err;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ while (cnode) {
+ ubifs_assert(c, row >= 0);
+ nnode = cnode->parent;
+ if (cnode->level) {
+ /* cnode is a nnode */
+ num = calc_nnode_num(row, col);
+ if (cnode->num != num) {
+ ubifs_err(c, "nnode num %d expected %d parent num %d iip %d",
+ cnode->num, num,
+ (nnode ? nnode->num : 0), cnode->iip);
+ return -EINVAL;
+ }
+ nn = (struct ubifs_nnode *)cnode;
+ while (iip < UBIFS_LPT_FANOUT) {
+ cn = nn->nbranch[iip].cnode;
+ if (cn) {
+ /* Go down */
+ row += 1;
+ col <<= UBIFS_LPT_FANOUT_SHIFT;
+ col += iip;
+ iip = 0;
+ cnode = cn;
+ break;
+ }
+ /* Go right */
+ iip += 1;
+ }
+ if (iip < UBIFS_LPT_FANOUT)
+ continue;
+ } else {
+ struct ubifs_pnode *pnode;
+
+ /* cnode is a pnode */
+ pnode = (struct ubifs_pnode *)cnode;
+ err = dbg_chk_pnode(c, pnode, col);
+ if (err)
+ return err;
+ }
+ /* Go up and to the right */
+ row -= 1;
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ iip = cnode->iip + 1;
+ cnode = (struct ubifs_cnode *)nnode;
+ }
+ return 0;
+}
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
new file mode 100644
index 00000000..c4d07932
--- /dev/null
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -0,0 +1,1997 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements commit-related functionality of the LEB properties
+ * subsystem.
+ */
+
+#include <linux/crc16.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include "ubifs.h"
+
+static int dbg_populate_lsave(struct ubifs_info *c);
+
+/**
+ * first_dirty_cnode - find first dirty cnode.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode at which to start
+ *
+ * This function returns the first dirty cnode or %NULL if there is not one.
+ */
+static struct ubifs_cnode *first_dirty_cnode(const struct ubifs_info *c, struct ubifs_nnode *nnode)
+{
+ ubifs_assert(c, nnode);
+ while (1) {
+ int i, cont = 0;
+
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ struct ubifs_cnode *cnode;
+
+ cnode = nnode->nbranch[i].cnode;
+ if (cnode &&
+ test_bit(DIRTY_CNODE, &cnode->flags)) {
+ if (cnode->level == 0)
+ return cnode;
+ nnode = (struct ubifs_nnode *)cnode;
+ cont = 1;
+ break;
+ }
+ }
+ if (!cont)
+ return (struct ubifs_cnode *)nnode;
+ }
+}
+
+/**
+ * next_dirty_cnode - find next dirty cnode.
+ * @c: UBIFS file-system description object
+ * @cnode: cnode from which to begin searching
+ *
+ * This function returns the next dirty cnode or %NULL if there is not one.
+ */
+static struct ubifs_cnode *next_dirty_cnode(const struct ubifs_info *c, struct ubifs_cnode *cnode)
+{
+ struct ubifs_nnode *nnode;
+ int i;
+
+ ubifs_assert(c, cnode);
+ nnode = cnode->parent;
+ if (!nnode)
+ return NULL;
+ for (i = cnode->iip + 1; i < UBIFS_LPT_FANOUT; i++) {
+ cnode = nnode->nbranch[i].cnode;
+ if (cnode && test_bit(DIRTY_CNODE, &cnode->flags)) {
+ if (cnode->level == 0)
+ return cnode; /* cnode is a pnode */
+ /* cnode is a nnode */
+ return first_dirty_cnode(c, (struct ubifs_nnode *)cnode);
+ }
+ }
+ return (struct ubifs_cnode *)nnode;
+}
+
+/**
+ * get_cnodes_to_commit - create list of dirty cnodes to commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of cnodes to commit.
+ */
+static int get_cnodes_to_commit(struct ubifs_info *c)
+{
+ struct ubifs_cnode *cnode, *cnext;
+ int cnt = 0;
+
+ if (!c->nroot)
+ return 0;
+
+ if (!test_bit(DIRTY_CNODE, &c->nroot->flags))
+ return 0;
+
+ c->lpt_cnext = first_dirty_cnode(c, c->nroot);
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ cnt += 1;
+ while (1) {
+ ubifs_assert(c, !test_bit(COW_CNODE, &cnode->flags));
+ __set_bit(COW_CNODE, &cnode->flags);
+ cnext = next_dirty_cnode(c, cnode);
+ if (!cnext) {
+ cnode->cnext = c->lpt_cnext;
+ break;
+ }
+ cnode->cnext = cnext;
+ cnode = cnext;
+ cnt += 1;
+ }
+ dbg_cmt("committing %d cnodes", cnt);
+ dbg_lp("committing %d cnodes", cnt);
+ ubifs_assert(c, cnt == c->dirty_nn_cnt + c->dirty_pn_cnt);
+ return cnt;
+}
+
+/**
+ * upd_ltab - update LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @free: amount of free space
+ * @dirty: amount of dirty space to add
+ */
+static void upd_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
+{
+ dbg_lp("LEB %d free %d dirty %d to %d +%d",
+ lnum, c->ltab[lnum - c->lpt_first].free,
+ c->ltab[lnum - c->lpt_first].dirty, free, dirty);
+ ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last);
+ c->ltab[lnum - c->lpt_first].free = free;
+ c->ltab[lnum - c->lpt_first].dirty += dirty;
+}
+
+/**
+ * alloc_lpt_leb - allocate an LPT LEB that is empty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number is passed and returned here
+ *
+ * This function finds the next empty LEB in the ltab starting from @lnum. If a
+ * an empty LEB is found it is returned in @lnum and the function returns %0.
+ * Otherwise the function returns -ENOSPC. Note however, that LPT is designed
+ * never to run out of space.
+ */
+static int alloc_lpt_leb(struct ubifs_info *c, int *lnum)
+{
+ int i, n;
+
+ n = *lnum - c->lpt_first + 1;
+ for (i = n; i < c->lpt_lebs; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (c->ltab[i].free == c->leb_size) {
+ c->ltab[i].cmt = 1;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (c->ltab[i].free == c->leb_size) {
+ c->ltab[i].cmt = 1;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ }
+ return -ENOSPC;
+}
+
+/**
+ * layout_cnodes - layout cnodes for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int layout_cnodes(struct ubifs_info *c)
+{
+ int lnum, offs, len, alen, done_lsave, done_ltab, err;
+ struct ubifs_cnode *cnode;
+
+ err = dbg_chk_lpt_sz(c, 0, 0);
+ if (err)
+ return err;
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ lnum = c->nhead_lnum;
+ offs = c->nhead_offs;
+ /* Try to place lsave and ltab nicely */
+ done_lsave = !c->big_lpt;
+ done_ltab = 0;
+ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) {
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ if (offs + c->ltab_sz <= c->leb_size) {
+ done_ltab = 1;
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ do {
+ if (cnode->level) {
+ len = c->nnode_sz;
+ c->dirty_nn_cnt -= 1;
+ } else {
+ len = c->pnode_sz;
+ c->dirty_pn_cnt -= 1;
+ }
+ while (offs + len > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ /* Try to place lsave and ltab nicely */
+ if (!done_lsave) {
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ continue;
+ }
+ if (!done_ltab) {
+ done_ltab = 1;
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ continue;
+ }
+ break;
+ }
+ if (cnode->parent) {
+ cnode->parent->nbranch[cnode->iip].lnum = lnum;
+ cnode->parent->nbranch[cnode->iip].offs = offs;
+ } else {
+ c->lpt_lnum = lnum;
+ c->lpt_offs = offs;
+ }
+ offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
+ cnode = cnode->cnext;
+ } while (cnode && cnode != c->lpt_cnext);
+
+ /* Make sure to place LPT's save table */
+ if (!done_lsave) {
+ if (offs + c->lsave_sz > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ }
+ done_lsave = 1;
+ c->lsave_lnum = lnum;
+ c->lsave_offs = offs;
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ /* Make sure to place LPT's own lprops table */
+ if (!done_ltab) {
+ if (offs + c->ltab_sz > c->leb_size) {
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = alloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ }
+ c->ltab_lnum = lnum;
+ c->ltab_offs = offs;
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ alen = ALIGN(offs, c->min_io_size);
+ upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 4, alen - offs);
+ err = dbg_chk_lpt_sz(c, 3, alen);
+ if (err)
+ return err;
+ return 0;
+
+no_space:
+ ubifs_err(c, "LPT out of space at LEB %d:%d needing %d, done_ltab %d, done_lsave %d",
+ lnum, offs, len, done_ltab, done_lsave);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return err;
+}
+
+/**
+ * realloc_lpt_leb - allocate an LPT LEB that is empty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number is passed and returned here
+ *
+ * This function duplicates exactly the results of the function alloc_lpt_leb.
+ * It is used during end commit to reallocate the same LEB numbers that were
+ * allocated by alloc_lpt_leb during start commit.
+ *
+ * This function finds the next LEB that was allocated by the alloc_lpt_leb
+ * function starting from @lnum. If a LEB is found it is returned in @lnum and
+ * the function returns %0. Otherwise the function returns -ENOSPC.
+ * Note however, that LPT is designed never to run out of space.
+ */
+static int realloc_lpt_leb(struct ubifs_info *c, int *lnum)
+{
+ int i, n;
+
+ n = *lnum - c->lpt_first + 1;
+ for (i = n; i < c->lpt_lebs; i++)
+ if (c->ltab[i].cmt) {
+ c->ltab[i].cmt = 0;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+
+ for (i = 0; i < n; i++)
+ if (c->ltab[i].cmt) {
+ c->ltab[i].cmt = 0;
+ *lnum = i + c->lpt_first;
+ return 0;
+ }
+ return -ENOSPC;
+}
+
+/**
+ * write_cnodes - write cnodes for commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int write_cnodes(struct ubifs_info *c)
+{
+ int lnum, offs, len, from, err, wlen, alen, done_ltab, done_lsave;
+ struct ubifs_cnode *cnode;
+ void *buf = c->lpt_buf;
+
+ cnode = c->lpt_cnext;
+ if (!cnode)
+ return 0;
+ lnum = c->nhead_lnum;
+ offs = c->nhead_offs;
+ from = offs;
+ /* Ensure empty LEB is unmapped */
+ if (offs == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ /* Try to place lsave and ltab nicely */
+ done_lsave = !c->big_lpt;
+ done_ltab = 0;
+ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) {
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ if (offs + c->ltab_sz <= c->leb_size) {
+ done_ltab = 1;
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ /* Loop for each cnode */
+ do {
+ if (cnode->level)
+ len = c->nnode_sz;
+ else
+ len = c->pnode_sz;
+ while (offs + len > c->leb_size) {
+ wlen = offs - from;
+ if (wlen) {
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from,
+ alen);
+ if (err)
+ return err;
+ }
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ /* Try to place lsave and ltab nicely */
+ if (!done_lsave) {
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ continue;
+ }
+ if (!done_ltab) {
+ done_ltab = 1;
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ continue;
+ }
+ break;
+ }
+ if (cnode->level)
+ ubifs_pack_nnode(c, buf + offs,
+ (struct ubifs_nnode *)cnode);
+ else
+ ubifs_pack_pnode(c, buf + offs,
+ (struct ubifs_pnode *)cnode);
+ /*
+ * The reason for the barriers is the same as in case of TNC.
+ * See comment in 'write_index()'. 'dirty_cow_nnode()' and
+ * 'dirty_cow_pnode()' are the functions for which this is
+ * important.
+ */
+ clear_bit(DIRTY_CNODE, &cnode->flags);
+ smp_mb__before_atomic();
+ clear_bit(COW_CNODE, &cnode->flags);
+ smp_mb__after_atomic();
+ offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
+ cnode = cnode->cnext;
+ } while (cnode && cnode != c->lpt_cnext);
+
+ /* Make sure to place LPT's save table */
+ if (!done_lsave) {
+ if (offs + c->lsave_sz > c->leb_size) {
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ done_lsave = 1;
+ ubifs_pack_lsave(c, buf + offs, c->lsave);
+ offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
+ }
+
+ /* Make sure to place LPT's own lprops table */
+ if (!done_ltab) {
+ if (offs + c->ltab_sz > c->leb_size) {
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+ dbg_chk_lpt_sz(c, 2, c->leb_size - offs);
+ err = realloc_lpt_leb(c, &lnum);
+ if (err)
+ goto no_space;
+ offs = from = 0;
+ ubifs_assert(c, lnum >= c->lpt_first &&
+ lnum <= c->lpt_last);
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
+ offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
+ }
+
+ /* Write remaining data in buffer */
+ wlen = offs - from;
+ alen = ALIGN(wlen, c->min_io_size);
+ memset(buf + offs, 0xff, alen - wlen);
+ err = ubifs_leb_write(c, lnum, buf + from, from, alen);
+ if (err)
+ return err;
+
+ dbg_chk_lpt_sz(c, 4, alen - wlen);
+ err = dbg_chk_lpt_sz(c, 3, ALIGN(offs, c->min_io_size));
+ if (err)
+ return err;
+
+ c->nhead_lnum = lnum;
+ c->nhead_offs = ALIGN(offs, c->min_io_size);
+
+ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
+ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
+ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+
+ return 0;
+
+no_space:
+ ubifs_err(c, "LPT out of space mismatch at LEB %d:%d needing %d, done_ltab %d, done_lsave %d",
+ lnum, offs, len, done_ltab, done_lsave);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return err;
+}
+
+/**
+ * next_pnode_to_dirty - find next pnode to dirty.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode
+ *
+ * This function returns the next pnode to dirty or %NULL if there are no more
+ * pnodes. Note that pnodes that have never been written (lnum == 0) are
+ * skipped.
+ */
+static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
+ struct ubifs_pnode *pnode)
+{
+ struct ubifs_nnode *nnode;
+ int iip;
+
+ /* Try to go right */
+ nnode = pnode->parent;
+ for (iip = pnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ return ubifs_get_pnode(c, nnode, iip);
+ }
+
+ /* Go up while can't go right */
+ do {
+ iip = nnode->iip + 1;
+ nnode = nnode->parent;
+ if (!nnode)
+ return NULL;
+ for (; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ break;
+ }
+ } while (iip >= UBIFS_LPT_FANOUT);
+
+ /* Go right */
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return (void *)nnode;
+
+ /* Go down to level 1 */
+ while (nnode->level > 1) {
+ for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++) {
+ if (nnode->nbranch[iip].lnum)
+ break;
+ }
+ if (iip >= UBIFS_LPT_FANOUT) {
+ /*
+ * Should not happen, but we need to keep going
+ * if it does.
+ */
+ iip = 0;
+ }
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return (void *)nnode;
+ }
+
+ for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++)
+ if (nnode->nbranch[iip].lnum)
+ break;
+ if (iip >= UBIFS_LPT_FANOUT)
+ /* Should not happen, but we need to keep going if it does */
+ iip = 0;
+ return ubifs_get_pnode(c, nnode, iip);
+}
+
+/**
+ * add_pnode_dirt - add dirty space to LPT LEB properties.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode for which to add dirt
+ */
+static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
+ c->pnode_sz);
+}
+
+/**
+ * do_make_pnode_dirty - mark a pnode dirty.
+ * @c: UBIFS file-system description object
+ * @pnode: pnode to mark dirty
+ */
+static void do_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode)
+{
+ /* Assumes cnext list is empty i.e. not called during commit */
+ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
+ struct ubifs_nnode *nnode;
+
+ c->dirty_pn_cnt += 1;
+ add_pnode_dirt(c, pnode);
+ /* Mark parent and ancestors dirty too */
+ nnode = pnode->parent;
+ while (nnode) {
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ nnode = nnode->parent;
+ } else
+ break;
+ }
+ }
+}
+
+/**
+ * make_tree_dirty - mark the entire LEB properties tree dirty.
+ * @c: UBIFS file-system description object
+ *
+ * This function is used by the "small" LPT model to cause the entire LEB
+ * properties tree to be written. The "small" LPT model does not use LPT
+ * garbage collection because it is more efficient to write the entire tree
+ * (because it is small).
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_tree_dirty(struct ubifs_info *c)
+{
+ struct ubifs_pnode *pnode;
+
+ pnode = ubifs_pnode_lookup(c, 0);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+
+ while (pnode) {
+ do_make_pnode_dirty(c, pnode);
+ pnode = next_pnode_to_dirty(c, pnode);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ }
+ return 0;
+}
+
+/**
+ * need_write_all - determine if the LPT area is running out of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %1 if the LPT area is running out of free space and %0
+ * if it is not.
+ */
+static int need_write_all(struct ubifs_info *c)
+{
+ long long free = 0;
+ int i;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (i + c->lpt_first == c->nhead_lnum)
+ free += c->leb_size - c->nhead_offs;
+ else if (c->ltab[i].free == c->leb_size)
+ free += c->leb_size;
+ else if (c->ltab[i].free + c->ltab[i].dirty == c->leb_size)
+ free += c->leb_size;
+ }
+ /* Less than twice the size left */
+ if (free <= c->lpt_sz * 2)
+ return 1;
+ return 0;
+}
+
+/**
+ * lpt_tgc_start - start trivial garbage collection of LPT LEBs.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial garbage collection is where a LPT LEB contains only dirty and
+ * free space and so may be reused as soon as the next commit is completed.
+ * This function is called during start commit to mark LPT LEBs for trivial GC.
+ */
+static void lpt_tgc_start(struct ubifs_info *c)
+{
+ int i;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (i + c->lpt_first == c->nhead_lnum)
+ continue;
+ if (c->ltab[i].dirty > 0 &&
+ c->ltab[i].free + c->ltab[i].dirty == c->leb_size) {
+ c->ltab[i].tgc = 1;
+ c->ltab[i].free = c->leb_size;
+ c->ltab[i].dirty = 0;
+ dbg_lp("LEB %d", i + c->lpt_first);
+ }
+ }
+}
+
+/**
+ * lpt_tgc_end - end trivial garbage collection of LPT LEBs.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial garbage collection is where a LPT LEB contains only dirty and
+ * free space and so may be reused as soon as the next commit is completed.
+ * This function is called after the commit is completed (master node has been
+ * written) and un-maps LPT LEBs that were marked for trivial GC.
+ */
+static int lpt_tgc_end(struct ubifs_info *c)
+{
+ int i, err;
+
+ for (i = 0; i < c->lpt_lebs; i++)
+ if (c->ltab[i].tgc) {
+ err = ubifs_leb_unmap(c, i + c->lpt_first);
+ if (err)
+ return err;
+ c->ltab[i].tgc = 0;
+ dbg_lp("LEB %d", i + c->lpt_first);
+ }
+ return 0;
+}
+
+/**
+ * populate_lsave - fill the lsave array with important LEB numbers.
+ * @c: the UBIFS file-system description object
+ *
+ * This function is only called for the "big" model. It records a small number
+ * of LEB numbers of important LEBs. Important LEBs are ones that are (from
+ * most important to least important): empty, freeable, freeable index, dirty
+ * index, dirty or free. Upon mount, we read this list of LEB numbers and bring
+ * their pnodes into memory. That will stop us from having to scan the LPT
+ * straight away. For the "small" model we assume that scanning the LPT is no
+ * big deal.
+ */
+static void populate_lsave(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ int i, cnt = 0;
+
+ ubifs_assert(c, c->big_lpt);
+ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) {
+ c->lpt_drty_flgs |= LSAVE_DIRTY;
+ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
+ }
+
+ if (dbg_populate_lsave(c))
+ return;
+
+ list_for_each_entry(lprops, &c->empty_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ list_for_each_entry(lprops, &c->freeable_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+ c->lsave[cnt++] = lprops->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ for (i = 0; i < heap->cnt; i++) {
+ c->lsave[cnt++] = heap->arr[i]->lnum;
+ if (cnt >= c->lsave_cnt)
+ return;
+ }
+ /* Fill it up completely */
+ while (cnt < c->lsave_cnt)
+ c->lsave[cnt++] = c->main_first;
+}
+
+/**
+ * nnode_lookup - lookup a nnode in the LPT.
+ * @c: UBIFS file-system description object
+ * @i: nnode number
+ *
+ * This function returns a pointer to the nnode on success or a negative
+ * error code on failure.
+ */
+static struct ubifs_nnode *nnode_lookup(struct ubifs_info *c, int i)
+{
+ int err, iip;
+ struct ubifs_nnode *nnode;
+
+ if (!c->nroot) {
+ err = ubifs_read_nnode(c, NULL, 0);
+ if (err)
+ return ERR_PTR(err);
+ }
+ nnode = c->nroot;
+ while (1) {
+ iip = i & (UBIFS_LPT_FANOUT - 1);
+ i >>= UBIFS_LPT_FANOUT_SHIFT;
+ if (!i)
+ break;
+ nnode = ubifs_get_nnode(c, nnode, iip);
+ if (IS_ERR(nnode))
+ return nnode;
+ }
+ return nnode;
+}
+
+/**
+ * make_nnode_dirty - find a nnode and, if found, make it dirty.
+ * @c: UBIFS file-system description object
+ * @node_num: nnode number of nnode to make dirty
+ * @lnum: LEB number where nnode was written
+ * @offs: offset where nnode was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_nnode_dirty(struct ubifs_info *c, int node_num, int lnum,
+ int offs)
+{
+ struct ubifs_nnode *nnode;
+
+ nnode = nnode_lookup(c, node_num);
+ if (IS_ERR(nnode))
+ return PTR_ERR(nnode);
+ if (nnode->parent) {
+ struct ubifs_nbranch *branch;
+
+ branch = &nnode->parent->nbranch[nnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ return 0; /* nnode is obsolete */
+ } else if (c->lpt_lnum != lnum || c->lpt_offs != offs)
+ return 0; /* nnode is obsolete */
+ /* Assumes cnext list is empty i.e. not called during commit */
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ /* Mark parent and ancestors dirty too */
+ nnode = nnode->parent;
+ while (nnode) {
+ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
+ c->dirty_nn_cnt += 1;
+ ubifs_add_nnode_dirt(c, nnode);
+ nnode = nnode->parent;
+ } else
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * make_pnode_dirty - find a pnode and, if found, make it dirty.
+ * @c: UBIFS file-system description object
+ * @node_num: pnode number of pnode to make dirty
+ * @lnum: LEB number where pnode was written
+ * @offs: offset where pnode was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum,
+ int offs)
+{
+ struct ubifs_pnode *pnode;
+ struct ubifs_nbranch *branch;
+
+ pnode = ubifs_pnode_lookup(c, node_num);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ branch = &pnode->parent->nbranch[pnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ return 0;
+ do_make_pnode_dirty(c, pnode);
+ return 0;
+}
+
+/**
+ * make_ltab_dirty - make ltab node dirty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number where ltab was written
+ * @offs: offset where ltab was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_ltab_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->ltab_lnum || offs != c->ltab_offs)
+ return 0; /* This ltab node is obsolete */
+ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) {
+ c->lpt_drty_flgs |= LTAB_DIRTY;
+ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz);
+ }
+ return 0;
+}
+
+/**
+ * make_lsave_dirty - make lsave node dirty.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number where lsave was written
+ * @offs: offset where lsave was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_lsave_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->lsave_lnum || offs != c->lsave_offs)
+ return 0; /* This lsave node is obsolete */
+ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) {
+ c->lpt_drty_flgs |= LSAVE_DIRTY;
+ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz);
+ }
+ return 0;
+}
+
+/**
+ * make_node_dirty - make node dirty.
+ * @c: UBIFS file-system description object
+ * @node_type: LPT node type
+ * @node_num: node number
+ * @lnum: LEB number where node was written
+ * @offs: offset where node was written
+ *
+ * This function is used by LPT garbage collection. LPT garbage collection is
+ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection
+ * simply involves marking all the nodes in the LEB being garbage-collected as
+ * dirty. The dirty nodes are written next commit, after which the LEB is free
+ * to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int make_node_dirty(struct ubifs_info *c, int node_type, int node_num,
+ int lnum, int offs)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return make_nnode_dirty(c, node_num, lnum, offs);
+ case UBIFS_LPT_PNODE:
+ return make_pnode_dirty(c, node_num, lnum, offs);
+ case UBIFS_LPT_LTAB:
+ return make_ltab_dirty(c, lnum, offs);
+ case UBIFS_LPT_LSAVE:
+ return make_lsave_dirty(c, lnum, offs);
+ }
+ return -EINVAL;
+}
+
+/**
+ * get_lpt_node_len - return the length of a node based on its type.
+ * @c: UBIFS file-system description object
+ * @node_type: LPT node type
+ */
+static int get_lpt_node_len(const struct ubifs_info *c, int node_type)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return c->nnode_sz;
+ case UBIFS_LPT_PNODE:
+ return c->pnode_sz;
+ case UBIFS_LPT_LTAB:
+ return c->ltab_sz;
+ case UBIFS_LPT_LSAVE:
+ return c->lsave_sz;
+ }
+ return 0;
+}
+
+/**
+ * get_pad_len - return the length of padding in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @len: length of buffer
+ */
+static int get_pad_len(const struct ubifs_info *c, uint8_t *buf, int len)
+{
+ int offs, pad_len;
+
+ if (c->min_io_size == 1)
+ return 0;
+ offs = c->leb_size - len;
+ pad_len = ALIGN(offs, c->min_io_size) - offs;
+ return pad_len;
+}
+
+/**
+ * get_lpt_node_type - return type (and node number) of a node in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @node_num: node number is returned here
+ */
+static int get_lpt_node_type(const struct ubifs_info *c, uint8_t *buf,
+ int *node_num)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int pos = 0, node_type;
+
+ node_type = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_TYPE_BITS);
+ *node_num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits);
+ return node_type;
+}
+
+/**
+ * is_a_node - determine if a buffer contains a node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer
+ * @len: length of buffer
+ *
+ * This function returns %1 if the buffer contains a node or %0 if it does not.
+ */
+static int is_a_node(const struct ubifs_info *c, uint8_t *buf, int len)
+{
+ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
+ int pos = 0, node_type, node_len;
+ uint16_t crc, calc_crc;
+
+ if (len < UBIFS_LPT_CRC_BYTES + (UBIFS_LPT_TYPE_BITS + 7) / 8)
+ return 0;
+ node_type = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_TYPE_BITS);
+ if (node_type == UBIFS_LPT_NOT_A_NODE)
+ return 0;
+ node_len = get_lpt_node_len(c, node_type);
+ if (!node_len || node_len > len)
+ return 0;
+ pos = 0;
+ addr = buf;
+ crc = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_CRC_BITS);
+ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
+ node_len - UBIFS_LPT_CRC_BYTES);
+ if (crc != calc_crc)
+ return 0;
+ return 1;
+}
+
+/**
+ * lpt_gc_lnum - garbage collect a LPT LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to garbage collect
+ *
+ * LPT garbage collection is used only for the "big" LPT model
+ * (c->big_lpt == 1). Garbage collection simply involves marking all the nodes
+ * in the LEB being garbage-collected as dirty. The dirty nodes are written
+ * next commit, after which the LEB is free to be reused.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int lpt_gc_lnum(struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, node_type, node_num, node_len, offs;
+ void *buf = c->lpt_buf;
+
+ dbg_lp("LEB %d", lnum);
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ return err;
+
+ while (1) {
+ if (!is_a_node(c, buf, len)) {
+ int pad_len;
+
+ pad_len = get_pad_len(c, buf, len);
+ if (pad_len) {
+ buf += pad_len;
+ len -= pad_len;
+ continue;
+ }
+ return 0;
+ }
+ node_type = get_lpt_node_type(c, buf, &node_num);
+ node_len = get_lpt_node_len(c, node_type);
+ offs = c->leb_size - len;
+ ubifs_assert(c, node_len != 0);
+ mutex_lock(&c->lp_mutex);
+ err = make_node_dirty(c, node_type, node_num, lnum, offs);
+ mutex_unlock(&c->lp_mutex);
+ if (err)
+ return err;
+ buf += node_len;
+ len -= node_len;
+ }
+ return 0;
+}
+
+/**
+ * lpt_gc - LPT garbage collection.
+ * @c: UBIFS file-system description object
+ *
+ * Select a LPT LEB for LPT garbage collection and call 'lpt_gc_lnum()'.
+ * Returns %0 on success and a negative error code on failure.
+ */
+static int lpt_gc(struct ubifs_info *c)
+{
+ int i, lnum = -1, dirty = 0;
+
+ mutex_lock(&c->lp_mutex);
+ for (i = 0; i < c->lpt_lebs; i++) {
+ ubifs_assert(c, !c->ltab[i].tgc);
+ if (i + c->lpt_first == c->nhead_lnum ||
+ c->ltab[i].free + c->ltab[i].dirty == c->leb_size)
+ continue;
+ if (c->ltab[i].dirty > dirty) {
+ dirty = c->ltab[i].dirty;
+ lnum = i + c->lpt_first;
+ }
+ }
+ mutex_unlock(&c->lp_mutex);
+ if (lnum == -1)
+ return -ENOSPC;
+ return lpt_gc_lnum(c, lnum);
+}
+
+/**
+ * ubifs_lpt_start_commit - UBIFS commit starts.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called when UBIFS starts the commit operation.
+ * This function "freezes" all currently dirty LEB properties and does not
+ * change them anymore. Further changes are saved and tracked separately
+ * because they are not part of this commit. This function returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+int ubifs_lpt_start_commit(struct ubifs_info *c)
+{
+ int err, cnt;
+
+ dbg_lp("");
+
+ mutex_lock(&c->lp_mutex);
+ err = dbg_chk_lpt_free_spc(c);
+ if (err)
+ goto out;
+ err = dbg_check_ltab(c);
+ if (err)
+ goto out;
+
+ if (c->check_lpt_free) {
+ /*
+ * We ensure there is enough free space in
+ * ubifs_lpt_post_commit() by marking nodes dirty. That
+ * information is lost when we unmount, so we also need
+ * to check free space once after mounting also.
+ */
+ c->check_lpt_free = 0;
+ while (need_write_all(c)) {
+ mutex_unlock(&c->lp_mutex);
+ err = lpt_gc(c);
+ if (err)
+ return err;
+ mutex_lock(&c->lp_mutex);
+ }
+ }
+
+ lpt_tgc_start(c);
+
+ if (!c->dirty_pn_cnt) {
+ dbg_cmt("no cnodes to commit");
+ err = 0;
+ goto out;
+ }
+
+ if (!c->big_lpt && need_write_all(c)) {
+ /* If needed, write everything */
+ err = make_tree_dirty(c);
+ if (err)
+ goto out;
+ lpt_tgc_start(c);
+ }
+
+ if (c->big_lpt)
+ populate_lsave(c);
+
+ cnt = get_cnodes_to_commit(c);
+ ubifs_assert(c, cnt != 0);
+
+ err = layout_cnodes(c);
+ if (err)
+ goto out;
+
+ err = ubifs_lpt_calc_hash(c, c->mst_node->hash_lpt);
+ if (err)
+ goto out;
+
+ /* Copy the LPT's own lprops for end commit to write */
+ memcpy(c->ltab_cmt, c->ltab,
+ sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
+ c->lpt_drty_flgs &= ~(LTAB_DIRTY | LSAVE_DIRTY);
+
+out:
+ mutex_unlock(&c->lp_mutex);
+ return err;
+}
+
+/**
+ * free_obsolete_cnodes - free obsolete cnodes for commit end.
+ * @c: UBIFS file-system description object
+ */
+static void free_obsolete_cnodes(struct ubifs_info *c)
+{
+ struct ubifs_cnode *cnode, *cnext;
+
+ cnext = c->lpt_cnext;
+ if (!cnext)
+ return;
+ do {
+ cnode = cnext;
+ cnext = cnode->cnext;
+ if (test_bit(OBSOLETE_CNODE, &cnode->flags))
+ kfree(cnode);
+ else
+ cnode->cnext = NULL;
+ } while (cnext != c->lpt_cnext);
+ c->lpt_cnext = NULL;
+}
+
+/**
+ * ubifs_lpt_end_commit - finish the commit operation.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called when the commit operation finishes. It
+ * flushes the changes which were "frozen" by 'ubifs_lprops_start_commit()' to
+ * the media. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+int ubifs_lpt_end_commit(struct ubifs_info *c)
+{
+ int err;
+
+ dbg_lp("");
+
+ if (!c->lpt_cnext)
+ return 0;
+
+ err = write_cnodes(c);
+ if (err)
+ return err;
+
+ mutex_lock(&c->lp_mutex);
+ free_obsolete_cnodes(c);
+ mutex_unlock(&c->lp_mutex);
+
+ return 0;
+}
+
+/**
+ * ubifs_lpt_post_commit - post commit LPT trivial GC and LPT GC.
+ * @c: UBIFS file-system description object
+ *
+ * LPT trivial GC is completed after a commit. Also LPT GC is done after a
+ * commit for the "big" LPT model.
+ */
+int ubifs_lpt_post_commit(struct ubifs_info *c)
+{
+ int err;
+
+ mutex_lock(&c->lp_mutex);
+ err = lpt_tgc_end(c);
+ if (err)
+ goto out;
+ if (c->big_lpt)
+ while (need_write_all(c)) {
+ mutex_unlock(&c->lp_mutex);
+ err = lpt_gc(c);
+ if (err)
+ return err;
+ mutex_lock(&c->lp_mutex);
+ }
+out:
+ mutex_unlock(&c->lp_mutex);
+ return err;
+}
+
+/**
+ * first_nnode - find the first nnode in memory.
+ * @c: UBIFS file-system description object
+ * @hght: height of tree where nnode found is returned here
+ *
+ * This function returns a pointer to the nnode found or %NULL if no nnode is
+ * found. This function is a helper to 'ubifs_lpt_free()'.
+ */
+static struct ubifs_nnode *first_nnode(struct ubifs_info *c, int *hght)
+{
+ struct ubifs_nnode *nnode;
+ int h, i, found;
+
+ nnode = c->nroot;
+ *hght = 0;
+ if (!nnode)
+ return NULL;
+ for (h = 1; h < c->lpt_hght; h++) {
+ found = 0;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (nnode->nbranch[i].nnode) {
+ found = 1;
+ nnode = nnode->nbranch[i].nnode;
+ *hght = h;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ return nnode;
+}
+
+/**
+ * next_nnode - find the next nnode in memory.
+ * @c: UBIFS file-system description object
+ * @nnode: nnode from which to start.
+ * @hght: height of tree where nnode is, is passed and returned here
+ *
+ * This function returns a pointer to the nnode found or %NULL if no nnode is
+ * found. This function is a helper to 'ubifs_lpt_free()'.
+ */
+static struct ubifs_nnode *next_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode, int *hght)
+{
+ struct ubifs_nnode *parent;
+ int iip, h, i, found;
+
+ parent = nnode->parent;
+ if (!parent)
+ return NULL;
+ if (nnode->iip == UBIFS_LPT_FANOUT - 1) {
+ *hght -= 1;
+ return parent;
+ }
+ for (iip = nnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) {
+ nnode = parent->nbranch[iip].nnode;
+ if (nnode)
+ break;
+ }
+ if (!nnode) {
+ *hght -= 1;
+ return parent;
+ }
+ for (h = *hght + 1; h < c->lpt_hght; h++) {
+ found = 0;
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ if (nnode->nbranch[i].nnode) {
+ found = 1;
+ nnode = nnode->nbranch[i].nnode;
+ *hght = h;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ return nnode;
+}
+
+/**
+ * ubifs_lpt_free - free resources owned by the LPT.
+ * @c: UBIFS file-system description object
+ * @wr_only: free only resources used for writing
+ */
+void ubifs_lpt_free(struct ubifs_info *c, int wr_only)
+{
+ struct ubifs_nnode *nnode;
+ int i, hght;
+
+ /* Free write-only things first */
+
+ free_obsolete_cnodes(c); /* Leftover from a failed commit */
+
+ vfree(c->ltab_cmt);
+ c->ltab_cmt = NULL;
+ vfree(c->lpt_buf);
+ c->lpt_buf = NULL;
+ kfree(c->lsave);
+ c->lsave = NULL;
+
+ if (wr_only)
+ return;
+
+ /* Now free the rest */
+
+ nnode = first_nnode(c, &hght);
+ while (nnode) {
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++)
+ kfree(nnode->nbranch[i].nnode);
+ nnode = next_nnode(c, nnode, &hght);
+ }
+ for (i = 0; i < LPROPS_HEAP_CNT; i++)
+ kfree(c->lpt_heap[i].arr);
+ kfree(c->dirty_idx.arr);
+ kfree(c->nroot);
+ vfree(c->ltab);
+ kfree(c->lpt_nod_buf);
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+/**
+ * dbg_is_all_ff - determine if a buffer contains only 0xFF bytes.
+ * @buf: buffer
+ * @len: buffer length
+ */
+static int dbg_is_all_ff(uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xff)
+ return 0;
+ return 1;
+}
+
+/**
+ * dbg_is_nnode_dirty - determine if a nnode is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where nnode was written
+ * @offs: offset where nnode was written
+ */
+static int dbg_is_nnode_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ struct ubifs_nnode *nnode;
+ int hght;
+
+ /* Entire tree is in memory so first_nnode / next_nnode are OK */
+ nnode = first_nnode(c, &hght);
+ for (; nnode; nnode = next_nnode(c, nnode, &hght)) {
+ struct ubifs_nbranch *branch;
+
+ cond_resched();
+ if (nnode->parent) {
+ branch = &nnode->parent->nbranch[nnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &nnode->flags))
+ return 1;
+ return 0;
+ } else {
+ if (c->lpt_lnum != lnum || c->lpt_offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &nnode->flags))
+ return 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * dbg_is_pnode_dirty - determine if a pnode is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where pnode was written
+ * @offs: offset where pnode was written
+ */
+static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ int i, cnt;
+
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ struct ubifs_pnode *pnode;
+ struct ubifs_nbranch *branch;
+
+ cond_resched();
+ pnode = ubifs_pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ branch = &pnode->parent->nbranch[pnode->iip];
+ if (branch->lnum != lnum || branch->offs != offs)
+ continue;
+ if (test_bit(DIRTY_CNODE, &pnode->flags))
+ return 1;
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * dbg_is_ltab_dirty - determine if a ltab node is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where ltab node was written
+ * @offs: offset where ltab node was written
+ */
+static int dbg_is_ltab_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->ltab_lnum || offs != c->ltab_offs)
+ return 1;
+ return (c->lpt_drty_flgs & LTAB_DIRTY) != 0;
+}
+
+/**
+ * dbg_is_lsave_dirty - determine if a lsave node is dirty.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where lsave node was written
+ * @offs: offset where lsave node was written
+ */
+static int dbg_is_lsave_dirty(struct ubifs_info *c, int lnum, int offs)
+{
+ if (lnum != c->lsave_lnum || offs != c->lsave_offs)
+ return 1;
+ return (c->lpt_drty_flgs & LSAVE_DIRTY) != 0;
+}
+
+/**
+ * dbg_is_node_dirty - determine if a node is dirty.
+ * @c: the UBIFS file-system description object
+ * @node_type: node type
+ * @lnum: LEB number where node was written
+ * @offs: offset where node was written
+ */
+static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum,
+ int offs)
+{
+ switch (node_type) {
+ case UBIFS_LPT_NNODE:
+ return dbg_is_nnode_dirty(c, lnum, offs);
+ case UBIFS_LPT_PNODE:
+ return dbg_is_pnode_dirty(c, lnum, offs);
+ case UBIFS_LPT_LTAB:
+ return dbg_is_ltab_dirty(c, lnum, offs);
+ case UBIFS_LPT_LSAVE:
+ return dbg_is_lsave_dirty(c, lnum, offs);
+ }
+ return 1;
+}
+
+/**
+ * dbg_check_ltab_lnum - check the ltab for a LPT LEB number.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB number where node was written
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len;
+ int ret;
+ void *buf, *p;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ buf = p = __vmalloc(c->leb_size, GFP_NOFS);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory for ltab checking");
+ return 0;
+ }
+
+ dbg_lp("LEB %d", lnum);
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+
+ while (1) {
+ if (!is_a_node(c, p, len)) {
+ int i, pad_len;
+
+ pad_len = get_pad_len(c, p, len);
+ if (pad_len) {
+ p += pad_len;
+ len -= pad_len;
+ dirty += pad_len;
+ continue;
+ }
+ if (!dbg_is_all_ff(p, len)) {
+ ubifs_err(c, "invalid empty space in LEB %d at %d",
+ lnum, c->leb_size - len);
+ err = -EINVAL;
+ }
+ i = lnum - c->lpt_first;
+ if (len != c->ltab[i].free) {
+ ubifs_err(c, "invalid free space in LEB %d (free %d, expected %d)",
+ lnum, len, c->ltab[i].free);
+ err = -EINVAL;
+ }
+ if (dirty != c->ltab[i].dirty) {
+ ubifs_err(c, "invalid dirty space in LEB %d (dirty %d, expected %d)",
+ lnum, dirty, c->ltab[i].dirty);
+ err = -EINVAL;
+ }
+ goto out;
+ }
+ node_type = get_lpt_node_type(c, p, &node_num);
+ node_len = get_lpt_node_len(c, node_type);
+ ret = dbg_is_node_dirty(c, node_type, lnum, c->leb_size - len);
+ if (ret == 1)
+ dirty += node_len;
+ p += node_len;
+ len -= node_len;
+ }
+
+ err = 0;
+out:
+ vfree(buf);
+ return err;
+}
+
+/**
+ * dbg_check_ltab - check the free and dirty space in the ltab.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_check_ltab(struct ubifs_info *c)
+{
+ int lnum, err, i, cnt;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ /* Bring the entire tree into memory */
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ struct ubifs_pnode *pnode;
+
+ pnode = ubifs_pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ cond_resched();
+ }
+
+ /* Check nodes */
+ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)c->nroot, 0, 0);
+ if (err)
+ return err;
+
+ /* Check each LEB */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ err = dbg_check_ltab_lnum(c, lnum);
+ if (err) {
+ ubifs_err(c, "failed at LEB %d", lnum);
+ return err;
+ }
+ }
+
+ dbg_lp("succeeded");
+ return 0;
+}
+
+/**
+ * dbg_chk_lpt_free_spc - check LPT free space is enough to write entire LPT.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_chk_lpt_free_spc(struct ubifs_info *c)
+{
+ long long free = 0;
+ int i;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (i + c->lpt_first == c->nhead_lnum)
+ free += c->leb_size - c->nhead_offs;
+ else if (c->ltab[i].free == c->leb_size)
+ free += c->leb_size;
+ }
+ if (free < c->lpt_sz) {
+ ubifs_err(c, "LPT space error: free %lld lpt_sz %lld",
+ free, c->lpt_sz);
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * dbg_chk_lpt_sz - check LPT does not write more than LPT size.
+ * @c: the UBIFS file-system description object
+ * @action: what to do
+ * @len: length written
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ * The @action argument may be one of:
+ * o %0 - LPT debugging checking starts, initialize debugging variables;
+ * o %1 - wrote an LPT node, increase LPT size by @len bytes;
+ * o %2 - switched to a different LEB and wasted @len bytes;
+ * o %3 - check that we've written the right number of bytes.
+ * o %4 - wasted @len bytes;
+ */
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len)
+{
+ struct ubifs_debug_info *d = c->dbg;
+ long long chk_lpt_sz, lpt_sz;
+ int err = 0;
+
+ if (!dbg_is_chk_lprops(c))
+ return 0;
+
+ switch (action) {
+ case 0:
+ d->chk_lpt_sz = 0;
+ d->chk_lpt_sz2 = 0;
+ d->chk_lpt_lebs = 0;
+ d->chk_lpt_wastage = 0;
+ if (c->dirty_pn_cnt > c->pnode_cnt) {
+ ubifs_err(c, "dirty pnodes %d exceed max %d",
+ c->dirty_pn_cnt, c->pnode_cnt);
+ err = -EINVAL;
+ }
+ if (c->dirty_nn_cnt > c->nnode_cnt) {
+ ubifs_err(c, "dirty nnodes %d exceed max %d",
+ c->dirty_nn_cnt, c->nnode_cnt);
+ err = -EINVAL;
+ }
+ return err;
+ case 1:
+ d->chk_lpt_sz += len;
+ return 0;
+ case 2:
+ d->chk_lpt_sz += len;
+ d->chk_lpt_wastage += len;
+ d->chk_lpt_lebs += 1;
+ return 0;
+ case 3:
+ chk_lpt_sz = c->leb_size;
+ chk_lpt_sz *= d->chk_lpt_lebs;
+ chk_lpt_sz += len - c->nhead_offs;
+ if (d->chk_lpt_sz != chk_lpt_sz) {
+ ubifs_err(c, "LPT wrote %lld but space used was %lld",
+ d->chk_lpt_sz, chk_lpt_sz);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz > c->lpt_sz) {
+ ubifs_err(c, "LPT wrote %lld but lpt_sz is %lld",
+ d->chk_lpt_sz, c->lpt_sz);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz2 && d->chk_lpt_sz != d->chk_lpt_sz2) {
+ ubifs_err(c, "LPT layout size %lld but wrote %lld",
+ d->chk_lpt_sz, d->chk_lpt_sz2);
+ err = -EINVAL;
+ }
+ if (d->chk_lpt_sz2 && d->new_nhead_offs != len) {
+ ubifs_err(c, "LPT new nhead offs: expected %d was %d",
+ d->new_nhead_offs, len);
+ err = -EINVAL;
+ }
+ lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
+ lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
+ lpt_sz += c->ltab_sz;
+ if (c->big_lpt)
+ lpt_sz += c->lsave_sz;
+ if (d->chk_lpt_sz - d->chk_lpt_wastage > lpt_sz) {
+ ubifs_err(c, "LPT chk_lpt_sz %lld + waste %lld exceeds %lld",
+ d->chk_lpt_sz, d->chk_lpt_wastage, lpt_sz);
+ err = -EINVAL;
+ }
+ if (err) {
+ ubifs_dump_lpt_info(c);
+ ubifs_dump_lpt_lebs(c);
+ dump_stack();
+ }
+ d->chk_lpt_sz2 = d->chk_lpt_sz;
+ d->chk_lpt_sz = 0;
+ d->chk_lpt_wastage = 0;
+ d->chk_lpt_lebs = 0;
+ d->new_nhead_offs = len;
+ return err;
+ case 4:
+ d->chk_lpt_sz += len;
+ d->chk_lpt_wastage += len;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * dump_lpt_leb - dump an LPT LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number to dump
+ *
+ * This function dumps an LEB from LPT area. Nodes in this area are very
+ * different to nodes in the main area (e.g., they do not have common headers,
+ * they do not have 8-byte alignments, etc), so we have a separate function to
+ * dump LPT area LEBs. Note, LPT has to be locked by the caller.
+ */
+static void dump_lpt_leb(const struct ubifs_info *c, int lnum)
+{
+ int err, len = c->leb_size, node_type, node_num, node_len, offs;
+ void *buf, *p;
+
+ pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
+ buf = p = __vmalloc(c->leb_size, GFP_NOFS);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory to dump LPT");
+ return;
+ }
+
+ err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+
+ while (1) {
+ offs = c->leb_size - len;
+ if (!is_a_node(c, p, len)) {
+ int pad_len;
+
+ pad_len = get_pad_len(c, p, len);
+ if (pad_len) {
+ pr_err("LEB %d:%d, pad %d bytes\n",
+ lnum, offs, pad_len);
+ p += pad_len;
+ len -= pad_len;
+ continue;
+ }
+ if (len)
+ pr_err("LEB %d:%d, free %d bytes\n",
+ lnum, offs, len);
+ break;
+ }
+
+ node_type = get_lpt_node_type(c, p, &node_num);
+ switch (node_type) {
+ case UBIFS_LPT_PNODE:
+ {
+ node_len = c->pnode_sz;
+ if (c->big_lpt)
+ pr_err("LEB %d:%d, pnode num %d\n",
+ lnum, offs, node_num);
+ else
+ pr_err("LEB %d:%d, pnode\n", lnum, offs);
+ break;
+ }
+ case UBIFS_LPT_NNODE:
+ {
+ int i;
+ struct ubifs_nnode nnode;
+
+ node_len = c->nnode_sz;
+ if (c->big_lpt)
+ pr_err("LEB %d:%d, nnode num %d, ",
+ lnum, offs, node_num);
+ else
+ pr_err("LEB %d:%d, nnode, ",
+ lnum, offs);
+ err = ubifs_unpack_nnode(c, p, &nnode);
+ if (err) {
+ pr_err("failed to unpack_node, error %d\n",
+ err);
+ break;
+ }
+ for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+ pr_cont("%d:%d", nnode.nbranch[i].lnum,
+ nnode.nbranch[i].offs);
+ if (i != UBIFS_LPT_FANOUT - 1)
+ pr_cont(", ");
+ }
+ pr_cont("\n");
+ break;
+ }
+ case UBIFS_LPT_LTAB:
+ node_len = c->ltab_sz;
+ pr_err("LEB %d:%d, ltab\n", lnum, offs);
+ break;
+ case UBIFS_LPT_LSAVE:
+ node_len = c->lsave_sz;
+ pr_err("LEB %d:%d, lsave len\n", lnum, offs);
+ break;
+ default:
+ ubifs_err(c, "LPT node type %d not recognized", node_type);
+ goto out;
+ }
+
+ p += node_len;
+ len -= node_len;
+ }
+
+ pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum);
+out:
+ vfree(buf);
+ return;
+}
+
+/**
+ * ubifs_dump_lpt_lebs - dump LPT lebs.
+ * @c: UBIFS file-system description object
+ *
+ * This function dumps all LPT LEBs. The caller has to make sure the LPT is
+ * locked.
+ */
+void ubifs_dump_lpt_lebs(const struct ubifs_info *c)
+{
+ int i;
+
+ pr_err("(pid %d) start dumping all LPT LEBs\n", current->pid);
+ for (i = 0; i < c->lpt_lebs; i++)
+ dump_lpt_leb(c, i + c->lpt_first);
+ pr_err("(pid %d) finish dumping all LPT LEBs\n", current->pid);
+}
+
+/**
+ * dbg_populate_lsave - debugging version of 'populate_lsave()'
+ * @c: UBIFS file-system description object
+ *
+ * This is a debugging version for 'populate_lsave()' which populates lsave
+ * with random LEBs instead of useful LEBs, which is good for test coverage.
+ * Returns zero if lsave has not been populated (this debugging feature is
+ * disabled) an non-zero if lsave has been populated.
+ */
+static int dbg_populate_lsave(struct ubifs_info *c)
+{
+ struct ubifs_lprops *lprops;
+ struct ubifs_lpt_heap *heap;
+ int i;
+
+ if (!dbg_is_chk_gen(c))
+ return 0;
+ if (get_random_u32_below(4))
+ return 0;
+
+ for (i = 0; i < c->lsave_cnt; i++)
+ c->lsave[i] = c->main_first;
+
+ list_for_each_entry(lprops, &c->empty_list, list)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
+ list_for_each_entry(lprops, &c->freeable_list, list)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
+ list_for_each_entry(lprops, &c->frdi_idx_list, list)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = lprops->lnum;
+
+ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
+ heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
+ heap = &c->lpt_heap[LPROPS_FREE - 1];
+ for (i = 0; i < heap->cnt; i++)
+ c->lsave[get_random_u32_below(c->lsave_cnt)] = heap->arr[i]->lnum;
+
+ return 1;
+}
diff --git a/ubifs-utils/libubifs/master.c b/ubifs-utils/libubifs/master.c
new file mode 100644
index 00000000..7adc37c1
--- /dev/null
+++ b/ubifs-utils/libubifs/master.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/* This file implements reading and writing the master node */
+
+#include "ubifs.h"
+
+/**
+ * ubifs_compare_master_node - compare two UBIFS master nodes
+ * @c: UBIFS file-system description object
+ * @m1: the first node
+ * @m2: the second node
+ *
+ * This function compares two UBIFS master nodes. Returns 0 if they are equal
+ * and nonzero if not.
+ */
+int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
+{
+ int ret;
+ int behind;
+ int hmac_offs = offsetof(struct ubifs_mst_node, hmac);
+
+ /*
+ * Do not compare the common node header since the sequence number and
+ * hence the CRC are different.
+ */
+ ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ,
+ hmac_offs - UBIFS_CH_SZ);
+ if (ret)
+ return ret;
+
+ /*
+ * Do not compare the embedded HMAC as well which also must be different
+ * due to the different common node header.
+ */
+ behind = hmac_offs + UBIFS_MAX_HMAC_LEN;
+
+ if (UBIFS_MST_NODE_SZ > behind)
+ return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind);
+
+ return 0;
+}
+
+/* mst_node_check_hash - Check hash of a master node
+ * @c: UBIFS file-system description object
+ * @mst: The master node
+ * @expected: The expected hash of the master node
+ *
+ * This checks the hash of a master node against a given expected hash.
+ * Note that we have two master nodes on a UBIFS image which have different
+ * sequence numbers and consequently different CRCs. To be able to match
+ * both master nodes we exclude the common node header containing the sequence
+ * number and CRC from the hash.
+ *
+ * Returns 0 if the hashes are equal, a negative error code otherwise.
+ */
+static int mst_node_check_hash(const struct ubifs_info *c,
+ const struct ubifs_mst_node *mst,
+ const u8 *expected)
+{
+ u8 calc[UBIFS_MAX_HASH_LEN];
+ const void *node = mst;
+
+ crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch),
+ UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch),
+ calc);
+
+ if (ubifs_check_hash(c, expected, calc))
+ return -EPERM;
+
+ return 0;
+}
+
+/**
+ * scan_for_master - search the valid master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function scans the master node LEBs and search for the latest master
+ * node. Returns zero in case of success, %-EUCLEAN if there master area is
+ * corrupted and requires recovery, and a negative error code in case of
+ * failure.
+ */
+static int scan_for_master(struct ubifs_info *c)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int lnum, offs = 0, nodes_cnt, err;
+
+ lnum = UBIFS_MST_LNUM;
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ nodes_cnt = sleb->nodes_cnt;
+ if (nodes_cnt > 0) {
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+ if (snod->type != UBIFS_MST_NODE)
+ goto out_dump;
+ memcpy(c->mst_node, snod->node, snod->len);
+ offs = snod->offs;
+ }
+ ubifs_scan_destroy(sleb);
+
+ lnum += 1;
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ if (sleb->nodes_cnt != nodes_cnt)
+ goto out;
+ if (!sleb->nodes_cnt)
+ goto out;
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
+ if (snod->type != UBIFS_MST_NODE)
+ goto out_dump;
+ if (snod->offs != offs)
+ goto out;
+ if (ubifs_compare_master_node(c, c->mst_node, snod->node))
+ goto out;
+
+ c->mst_offs = offs;
+ ubifs_scan_destroy(sleb);
+
+ if (!ubifs_authenticated(c))
+ return 0;
+
+ if (ubifs_hmac_zero(c, c->mst_node->hmac)) {
+ err = mst_node_check_hash(c, c->mst_node,
+ c->sup_node->hash_mst);
+ if (err)
+ ubifs_err(c, "Failed to verify master node hash");
+ } else {
+ err = ubifs_node_verify_hmac(c, c->mst_node,
+ sizeof(struct ubifs_mst_node),
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ ubifs_err(c, "Failed to verify master node HMAC");
+ }
+
+ if (err)
+ return -EPERM;
+
+ return 0;
+
+out:
+ ubifs_scan_destroy(sleb);
+ return -EUCLEAN;
+
+out_dump:
+ ubifs_err(c, "unexpected node type %d master LEB %d:%d",
+ snod->type, lnum, snod->offs);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * validate_master - validate master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function validates data which was read from master node. Returns zero
+ * if the data is all right and %-EINVAL if not.
+ */
+static int validate_master(const struct ubifs_info *c)
+{
+ long long main_sz;
+ int err;
+
+ if (c->max_sqnum >= SQNUM_WATERMARK) {
+ err = 1;
+ goto out;
+ }
+
+ if (c->cmt_no >= c->max_sqnum) {
+ err = 2;
+ goto out;
+ }
+
+ if (c->highest_inum >= INUM_WATERMARK) {
+ err = 3;
+ goto out;
+ }
+
+ if (c->lhead_lnum < UBIFS_LOG_LNUM ||
+ c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs ||
+ c->lhead_offs < 0 || c->lhead_offs >= c->leb_size ||
+ c->lhead_offs & (c->min_io_size - 1)) {
+ err = 4;
+ goto out;
+ }
+
+ if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first ||
+ c->zroot.offs >= c->leb_size || c->zroot.offs & 7) {
+ err = 5;
+ goto out;
+ }
+
+ if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len ||
+ c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) {
+ err = 6;
+ goto out;
+ }
+
+ if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) {
+ err = 7;
+ goto out;
+ }
+
+ if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first ||
+ c->ihead_offs % c->min_io_size || c->ihead_offs < 0 ||
+ c->ihead_offs > c->leb_size || c->ihead_offs & 7) {
+ err = 8;
+ goto out;
+ }
+
+ main_sz = (long long)c->main_lebs * c->leb_size;
+ if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) {
+ err = 9;
+ goto out;
+ }
+
+ if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last ||
+ c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) {
+ err = 10;
+ goto out;
+ }
+
+ if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last ||
+ c->nhead_offs < 0 || c->nhead_offs % c->min_io_size ||
+ c->nhead_offs > c->leb_size) {
+ err = 11;
+ goto out;
+ }
+
+ if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last ||
+ c->ltab_offs < 0 ||
+ c->ltab_offs + c->ltab_sz > c->leb_size) {
+ err = 12;
+ goto out;
+ }
+
+ if (c->big_lpt && (c->lsave_lnum < c->lpt_first ||
+ c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 ||
+ c->lsave_offs + c->lsave_sz > c->leb_size)) {
+ err = 13;
+ goto out;
+ }
+
+ if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) {
+ err = 14;
+ goto out;
+ }
+
+ if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
+ err = 15;
+ goto out;
+ }
+
+ if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
+ err = 16;
+ goto out;
+ }
+
+ if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
+ c->lst.total_free & 7) {
+ err = 17;
+ goto out;
+ }
+
+ if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
+ err = 18;
+ goto out;
+ }
+
+ if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
+ err = 19;
+ goto out;
+ }
+
+ if (c->lst.total_free + c->lst.total_dirty +
+ c->lst.total_used > main_sz) {
+ err = 20;
+ goto out;
+ }
+
+ if (c->lst.total_dead + c->lst.total_dark +
+ c->lst.total_used + c->bi.old_idx_sz > main_sz) {
+ err = 21;
+ goto out;
+ }
+
+ if (c->lst.total_dead < 0 ||
+ c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
+ c->lst.total_dead & 7) {
+ err = 22;
+ goto out;
+ }
+
+ if (c->lst.total_dark < 0 ||
+ c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
+ c->lst.total_dark & 7) {
+ err = 23;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
+ ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_master - read master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function finds and reads the master node during file-system mount. If
+ * the flash is empty, it creates default master node as well. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+int ubifs_read_master(struct ubifs_info *c)
+{
+ int err, old_leb_cnt;
+
+ c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+ if (!c->mst_node)
+ return -ENOMEM;
+
+ err = scan_for_master(c);
+ if (err) {
+ if (err == -EUCLEAN)
+ err = ubifs_recover_master_node(c);
+ if (err)
+ /*
+ * Note, we do not free 'c->mst_node' here because the
+ * unmount routine will take care of this.
+ */
+ return err;
+ }
+
+ /* Make sure that the recovery flag is clear */
+ c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY);
+
+ c->max_sqnum = le64_to_cpu(c->mst_node->ch.sqnum);
+ c->highest_inum = le64_to_cpu(c->mst_node->highest_inum);
+ c->cmt_no = le64_to_cpu(c->mst_node->cmt_no);
+ c->zroot.lnum = le32_to_cpu(c->mst_node->root_lnum);
+ c->zroot.offs = le32_to_cpu(c->mst_node->root_offs);
+ c->zroot.len = le32_to_cpu(c->mst_node->root_len);
+ c->lhead_lnum = le32_to_cpu(c->mst_node->log_lnum);
+ c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
+ c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
+ c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
+ c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size);
+ c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
+ c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
+ c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
+ c->nhead_offs = le32_to_cpu(c->mst_node->nhead_offs);
+ c->ltab_lnum = le32_to_cpu(c->mst_node->ltab_lnum);
+ c->ltab_offs = le32_to_cpu(c->mst_node->ltab_offs);
+ c->lsave_lnum = le32_to_cpu(c->mst_node->lsave_lnum);
+ c->lsave_offs = le32_to_cpu(c->mst_node->lsave_offs);
+ c->lscan_lnum = le32_to_cpu(c->mst_node->lscan_lnum);
+ c->lst.empty_lebs = le32_to_cpu(c->mst_node->empty_lebs);
+ c->lst.idx_lebs = le32_to_cpu(c->mst_node->idx_lebs);
+ old_leb_cnt = le32_to_cpu(c->mst_node->leb_cnt);
+ c->lst.total_free = le64_to_cpu(c->mst_node->total_free);
+ c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty);
+ c->lst.total_used = le64_to_cpu(c->mst_node->total_used);
+ c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
+ c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
+
+ ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash);
+
+ c->calc_idx_sz = c->bi.old_idx_sz;
+
+ if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
+ c->no_orphs = 1;
+
+ if (old_leb_cnt != c->leb_cnt) {
+ /* The file system has been resized */
+ int growth = c->leb_cnt - old_leb_cnt;
+
+ if (c->leb_cnt < old_leb_cnt ||
+ c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ ubifs_err(c, "bad leb_cnt on master node");
+ ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
+ return -EINVAL;
+ }
+
+ dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs",
+ old_leb_cnt, c->leb_cnt);
+ c->lst.empty_lebs += growth;
+ c->lst.total_free += growth * (long long)c->leb_size;
+ c->lst.total_dark += growth * (long long)c->dark_wm;
+
+ /*
+ * Reflect changes back onto the master node. N.B. the master
+ * node gets written immediately whenever mounting (or
+ * remounting) in read-write mode, so we do not need to write it
+ * here.
+ */
+ c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt);
+ c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs);
+ c->mst_node->total_free = cpu_to_le64(c->lst.total_free);
+ c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark);
+ }
+
+ err = validate_master(c);
+ if (err)
+ return err;
+
+ err = dbg_old_index_check_init(c, &c->zroot);
+
+ return err;
+}
+
+/**
+ * ubifs_write_master - write master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function writes the master node. Returns zero in case of success and a
+ * negative error code in case of failure. The master node is written twice to
+ * enable recovery.
+ */
+int ubifs_write_master(struct ubifs_info *c)
+{
+ int err, lnum, offs, len;
+
+ ubifs_assert(c, !c->ro_media && !c->ro_mount);
+ if (c->ro_error)
+ return -EROFS;
+
+ lnum = UBIFS_MST_LNUM;
+ offs = c->mst_offs + c->mst_node_alsz;
+ len = UBIFS_MST_NODE_SZ;
+
+ if (offs + UBIFS_MST_NODE_SZ > c->leb_size) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ offs = 0;
+ }
+
+ c->mst_offs = offs;
+ c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
+
+ ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx);
+ err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ return err;
+
+ lnum += 1;
+
+ if (offs == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
+ offsetof(struct ubifs_mst_node, hmac));
+
+ return err;
+}
diff --git a/ubifs-utils/libubifs/misc.h b/ubifs-utils/libubifs/misc.h
new file mode 100644
index 00000000..615878e8
--- /dev/null
+++ b/ubifs-utils/libubifs/misc.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file contains miscellaneous helper functions.
+ */
+
+#ifndef __UBIFS_MISC_H__
+#define __UBIFS_MISC_H__
+
+/**
+ * ubifs_zn_dirty - check if znode is dirty.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is dirty and %0 otherwise.
+ */
+static inline int ubifs_zn_dirty(const struct ubifs_znode *znode)
+{
+ return !!test_bit(DIRTY_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_zn_obsolete - check if znode is obsolete.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is obsolete and %0 otherwise.
+ */
+static inline int ubifs_zn_obsolete(const struct ubifs_znode *znode)
+{
+ return !!test_bit(OBSOLETE_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_zn_cow - check if znode has to be copied on write.
+ * @znode: znode to check
+ *
+ * This helper function returns %1 if @znode is has COW flag set and %0
+ * otherwise.
+ */
+static inline int ubifs_zn_cow(const struct ubifs_znode *znode)
+{
+ return !!test_bit(COW_ZNODE, &znode->flags);
+}
+
+/**
+ * ubifs_wake_up_bgt - wake up background thread.
+ * @c: UBIFS file-system description object
+ */
+static inline void ubifs_wake_up_bgt(struct ubifs_info *c)
+{
+ if (c->bgt && !c->need_bgt) {
+ c->need_bgt = 1;
+ wake_up_process(c->bgt);
+ }
+}
+
+/**
+ * ubifs_tnc_find_child - find next child in znode.
+ * @znode: znode to search at
+ * @start: the zbranch index to start at
+ *
+ * This helper function looks for znode child starting at index @start. Returns
+ * the child or %NULL if no children were found.
+ */
+static inline struct ubifs_znode *
+ubifs_tnc_find_child(struct ubifs_znode *znode, int start)
+{
+ while (start < znode->child_cnt) {
+ if (znode->zbranch[start].znode)
+ return znode->zbranch[start].znode;
+ start += 1;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubifs_inode - get UBIFS inode information by VFS 'struct inode' object.
+ * @inode: the VFS 'struct inode' pointer
+ */
+static inline struct ubifs_inode *ubifs_inode(const struct inode *inode)
+{
+ return container_of(inode, struct ubifs_inode, vfs_inode);
+}
+
+/**
+ * ubifs_compr_present - check if compressor was compiled in.
+ * @compr_type: compressor type to check
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %1 of compressor of type @compr_type is present, and
+ * %0 if not.
+ */
+static inline int ubifs_compr_present(struct ubifs_info *c, int compr_type)
+{
+ ubifs_assert(c, compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
+ return !!ubifs_compressors[compr_type]->capi_name;
+}
+
+/**
+ * ubifs_compr_name - get compressor name string by its type.
+ * @compr_type: compressor type
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns compressor type string.
+ */
+static inline const char *ubifs_compr_name(struct ubifs_info *c, int compr_type)
+{
+ ubifs_assert(c, compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
+ return ubifs_compressors[compr_type]->name;
+}
+
+/**
+ * ubifs_wbuf_sync - synchronize write-buffer.
+ * @wbuf: write-buffer to synchronize
+ *
+ * This is the same as 'ubifs_wbuf_sync_nolock()' but it does not assume
+ * that the write-buffer is already locked.
+ */
+static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf)
+{
+ int err;
+
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ err = ubifs_wbuf_sync_nolock(wbuf);
+ mutex_unlock(&wbuf->io_mutex);
+ return err;
+}
+
+/**
+ * ubifs_encode_dev - encode device node IDs.
+ * @dev: UBIFS device node information
+ * @rdev: device IDs to encode
+ *
+ * This is a helper function which encodes major/minor numbers of a device node
+ * into UBIFS device node description. We use standard Linux "new" and "huge"
+ * encodings.
+ */
+static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev)
+{
+ dev->new = cpu_to_le32(new_encode_dev(rdev));
+ return sizeof(dev->new);
+}
+
+/**
+ * ubifs_add_dirt - add dirty space to LEB properties.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to add dirty space for
+ * @dirty: dirty space to add
+ *
+ * This is a helper function which increased amount of dirty LEB space. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static inline int ubifs_add_dirt(struct ubifs_info *c, int lnum, int dirty)
+{
+ return ubifs_update_one_lp(c, lnum, LPROPS_NC, dirty, 0, 0);
+}
+
+/**
+ * ubifs_return_leb - return LEB to lprops.
+ * @c: the UBIFS file-system description object
+ * @lnum: LEB to return
+ *
+ * This helper function cleans the "taken" flag of a logical eraseblock in the
+ * lprops. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static inline int ubifs_return_leb(struct ubifs_info *c, int lnum)
+{
+ return ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_TAKEN, 0);
+}
+
+/**
+ * ubifs_idx_node_sz - return index node size.
+ * @c: the UBIFS file-system description object
+ * @child_cnt: number of children of this index node
+ */
+static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
+{
+ return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len + c->hash_len)
+ * child_cnt;
+}
+
+/**
+ * ubifs_idx_branch - return pointer to an index branch.
+ * @c: the UBIFS file-system description object
+ * @idx: index node
+ * @bnum: branch number
+ */
+static inline
+struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
+ const struct ubifs_idx_node *idx,
+ int bnum)
+{
+ return (struct ubifs_branch *)((void *)idx->branches +
+ (UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
+}
+
+/**
+ * ubifs_idx_key - return pointer to an index key.
+ * @c: the UBIFS file-system description object
+ * @idx: index node
+ */
+static inline void *ubifs_idx_key(const struct ubifs_info *c,
+ const struct ubifs_idx_node *idx)
+{
+ return (void *)((struct ubifs_branch *)idx->branches)->key;
+}
+
+/**
+ * ubifs_tnc_lookup - look up a file-system node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ *
+ * This function look up and reads node with key @key. The caller has to make
+ * sure the @node buffer is large enough to fit the node. Returns zero in case
+ * of success, %-ENOENT if the node was not found, and a negative error code in
+ * case of failure.
+ */
+static inline int ubifs_tnc_lookup(struct ubifs_info *c,
+ const union ubifs_key *key, void *node)
+{
+ return ubifs_tnc_locate(c, key, node, NULL, NULL);
+}
+
+/**
+ * ubifs_get_lprops - get reference to LEB properties.
+ * @c: the UBIFS file-system description object
+ *
+ * This function locks lprops. Lprops have to be unlocked by
+ * 'ubifs_release_lprops()'.
+ */
+static inline void ubifs_get_lprops(struct ubifs_info *c)
+{
+ mutex_lock(&c->lp_mutex);
+}
+
+/**
+ * ubifs_release_lprops - release lprops lock.
+ * @c: the UBIFS file-system description object
+ *
+ * This function has to be called after each 'ubifs_get_lprops()' call to
+ * unlock lprops.
+ */
+static inline void ubifs_release_lprops(struct ubifs_info *c)
+{
+ ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
+ ubifs_assert(c, c->lst.empty_lebs >= 0 &&
+ c->lst.empty_lebs <= c->main_lebs);
+ mutex_unlock(&c->lp_mutex);
+}
+
+/**
+ * ubifs_next_log_lnum - switch to the next log LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: current log LEB
+ *
+ * This helper function returns the log LEB number which goes next after LEB
+ * 'lnum'.
+ */
+static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
+{
+ lnum += 1;
+ if (lnum > c->log_last)
+ lnum = UBIFS_LOG_LNUM;
+
+ return lnum;
+}
+
+static inline int ubifs_xattr_max_cnt(struct ubifs_info *c)
+{
+ int max_xattrs = (c->leb_size / 2) / UBIFS_INO_NODE_SZ;
+
+ ubifs_assert(c, max_xattrs < c->max_orphans);
+ return max_xattrs;
+}
+
+const char *ubifs_assert_action_name(struct ubifs_info *c);
+
+#endif /* __UBIFS_MISC_H__ */
diff --git a/ubifs-utils/libubifs/orphan.c b/ubifs-utils/libubifs/orphan.c
new file mode 100644
index 00000000..fb957d96
--- /dev/null
+++ b/ubifs-utils/libubifs/orphan.c
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Author: Adrian Hunter
+ */
+
+#include "ubifs.h"
+
+/*
+ * An orphan is an inode number whose inode node has been committed to the index
+ * with a link count of zero. That happens when an open file is deleted
+ * (unlinked) and then a commit is run. In the normal course of events the inode
+ * would be deleted when the file is closed. However in the case of an unclean
+ * unmount, orphans need to be accounted for. After an unclean unmount, the
+ * orphans' inodes must be deleted which means either scanning the entire index
+ * looking for them, or keeping a list on flash somewhere. This unit implements
+ * the latter approach.
+ *
+ * The orphan area is a fixed number of LEBs situated between the LPT area and
+ * the main area. The number of orphan area LEBs is specified when the file
+ * system is created. The minimum number is 1. The size of the orphan area
+ * should be so that it can hold the maximum number of orphans that are expected
+ * to ever exist at one time.
+ *
+ * The number of orphans that can fit in a LEB is:
+ *
+ * (c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)
+ *
+ * For example: a 15872 byte LEB can fit 1980 orphans so 1 LEB may be enough.
+ *
+ * Orphans are accumulated in a rb-tree. When an inode's link count drops to
+ * zero, the inode number is added to the rb-tree. It is removed from the tree
+ * when the inode is deleted. Any new orphans that are in the orphan tree when
+ * the commit is run, are written to the orphan area in 1 or more orphan nodes.
+ * If the orphan area is full, it is consolidated to make space. There is
+ * always enough space because validation prevents the user from creating more
+ * than the maximum number of orphans allowed.
+ */
+
+static int dbg_check_orphans(struct ubifs_info *c);
+
+/**
+ * ubifs_add_orphan - add an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Add an orphan. This function is called when an inodes link count drops to
+ * zero.
+ */
+int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *orphan, *o;
+ struct rb_node **p, *parent = NULL;
+
+ orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
+ if (!orphan)
+ return -ENOMEM;
+ orphan->inum = inum;
+ orphan->new = 1;
+
+ spin_lock(&c->orphan_lock);
+ if (c->tot_orphans >= c->max_orphans) {
+ spin_unlock(&c->orphan_lock);
+ kfree(orphan);
+ return -ENFILE;
+ }
+ p = &c->orph_tree.rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = &(*p)->rb_left;
+ else if (inum > o->inum)
+ p = &(*p)->rb_right;
+ else {
+ ubifs_err(c, "orphaned twice");
+ spin_unlock(&c->orphan_lock);
+ kfree(orphan);
+ return -EINVAL;
+ }
+ }
+ c->tot_orphans += 1;
+ c->new_orphans += 1;
+ rb_link_node(&orphan->rb, parent, p);
+ rb_insert_color(&orphan->rb, &c->orph_tree);
+ list_add_tail(&orphan->list, &c->orph_list);
+ list_add_tail(&orphan->new_list, &c->orph_new);
+
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("ino %lu", (unsigned long)inum);
+ return 0;
+}
+
+static struct ubifs_orphan *lookup_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *o;
+ struct rb_node *p;
+
+ p = c->orph_tree.rb_node;
+ while (p) {
+ o = rb_entry(p, struct ubifs_orphan, rb);
+ if (inum < o->inum)
+ p = p->rb_left;
+ else if (inum > o->inum)
+ p = p->rb_right;
+ else {
+ return o;
+ }
+ }
+ return NULL;
+}
+
+static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o)
+{
+ rb_erase(&o->rb, &c->orph_tree);
+ list_del(&o->list);
+ c->tot_orphans -= 1;
+
+ if (o->new) {
+ list_del(&o->new_list);
+ c->new_orphans -= 1;
+ }
+
+ kfree(o);
+}
+
+static void orphan_delete(struct ubifs_info *c, struct ubifs_orphan *orph)
+{
+ if (orph->del) {
+ dbg_gen("deleted twice ino %lu", (unsigned long)orph->inum);
+ return;
+ }
+
+ if (orph->cmt) {
+ orph->del = 1;
+ rb_erase(&orph->rb, &c->orph_tree);
+ orph->dnext = c->orph_dnext;
+ c->orph_dnext = orph;
+ dbg_gen("delete later ino %lu", (unsigned long)orph->inum);
+ return;
+ }
+
+ __orphan_drop(c, orph);
+}
+
+/**
+ * ubifs_delete_orphan - delete an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Delete an orphan. This function is called when an inode is deleted.
+ */
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *orph;
+
+ spin_lock(&c->orphan_lock);
+
+ orph = lookup_orphan(c, inum);
+ if (!orph) {
+ spin_unlock(&c->orphan_lock);
+ ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
+ dump_stack();
+
+ return;
+ }
+
+ orphan_delete(c, orph);
+
+ spin_unlock(&c->orphan_lock);
+}
+
+/**
+ * ubifs_orphan_start_commit - start commit of orphans.
+ * @c: UBIFS file-system description object
+ *
+ * Start commit of orphans.
+ */
+int ubifs_orphan_start_commit(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orphan, **last;
+
+ spin_lock(&c->orphan_lock);
+ last = &c->orph_cnext;
+ list_for_each_entry(orphan, &c->orph_new, new_list) {
+ ubifs_assert(c, orphan->new);
+ ubifs_assert(c, !orphan->cmt);
+ orphan->new = 0;
+ orphan->cmt = 1;
+ *last = orphan;
+ last = &orphan->cnext;
+ }
+ *last = NULL;
+ c->cmt_orphans = c->new_orphans;
+ c->new_orphans = 0;
+ dbg_cmt("%d orphans to commit", c->cmt_orphans);
+ INIT_LIST_HEAD(&c->orph_new);
+ if (c->tot_orphans == 0)
+ c->no_orphs = 1;
+ else
+ c->no_orphs = 0;
+ spin_unlock(&c->orphan_lock);
+ return 0;
+}
+
+/**
+ * avail_orphs - calculate available space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of orphans that can be written in the
+ * available space.
+ */
+static int avail_orphs(struct ubifs_info *c)
+{
+ int avail_lebs, avail, gap;
+
+ avail_lebs = c->orph_lebs - (c->ohead_lnum - c->orph_first) - 1;
+ avail = avail_lebs *
+ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
+ gap = c->leb_size - c->ohead_offs;
+ if (gap >= UBIFS_ORPH_NODE_SZ + sizeof(__le64))
+ avail += (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
+ return avail;
+}
+
+/**
+ * tot_avail_orphs - calculate total space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of orphans that can be written in half
+ * the total space. That leaves half the space for adding new orphans.
+ */
+static int tot_avail_orphs(struct ubifs_info *c)
+{
+ int avail_lebs, avail;
+
+ avail_lebs = c->orph_lebs;
+ avail = avail_lebs *
+ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
+ return avail / 2;
+}
+
+/**
+ * do_write_orph_node - write a node to the orphan head.
+ * @c: UBIFS file-system description object
+ * @len: length of node
+ * @atomic: write atomically
+ *
+ * This function writes a node to the orphan head from the orphan buffer. If
+ * %atomic is not zero, then the write is done atomically. On success, %0 is
+ * returned, otherwise a negative error code is returned.
+ */
+static int do_write_orph_node(struct ubifs_info *c, int len, int atomic)
+{
+ int err = 0;
+
+ if (atomic) {
+ ubifs_assert(c, c->ohead_offs == 0);
+ ubifs_prepare_node(c, c->orph_buf, len, 1);
+ len = ALIGN(len, c->min_io_size);
+ err = ubifs_leb_change(c, c->ohead_lnum, c->orph_buf, len);
+ } else {
+ if (c->ohead_offs == 0) {
+ /* Ensure LEB has been unmapped */
+ err = ubifs_leb_unmap(c, c->ohead_lnum);
+ if (err)
+ return err;
+ }
+ err = ubifs_write_node(c, c->orph_buf, len, c->ohead_lnum,
+ c->ohead_offs);
+ }
+ return err;
+}
+
+/**
+ * write_orph_node - write an orphan node.
+ * @c: UBIFS file-system description object
+ * @atomic: write atomically
+ *
+ * This function builds an orphan node from the cnext list and writes it to the
+ * orphan head. On success, %0 is returned, otherwise a negative error code
+ * is returned.
+ */
+static int write_orph_node(struct ubifs_info *c, int atomic)
+{
+ struct ubifs_orphan *orphan, *cnext;
+ struct ubifs_orph_node *orph;
+ int gap, err, len, cnt, i;
+
+ ubifs_assert(c, c->cmt_orphans > 0);
+ gap = c->leb_size - c->ohead_offs;
+ if (gap < UBIFS_ORPH_NODE_SZ + sizeof(__le64)) {
+ c->ohead_lnum += 1;
+ c->ohead_offs = 0;
+ gap = c->leb_size;
+ if (c->ohead_lnum > c->orph_last) {
+ /*
+ * We limit the number of orphans so that this should
+ * never happen.
+ */
+ ubifs_err(c, "out of space in orphan area");
+ return -EINVAL;
+ }
+ }
+ cnt = (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64);
+ if (cnt > c->cmt_orphans)
+ cnt = c->cmt_orphans;
+ len = UBIFS_ORPH_NODE_SZ + cnt * sizeof(__le64);
+ ubifs_assert(c, c->orph_buf);
+ orph = c->orph_buf;
+ orph->ch.node_type = UBIFS_ORPH_NODE;
+ spin_lock(&c->orphan_lock);
+ cnext = c->orph_cnext;
+ for (i = 0; i < cnt; i++) {
+ orphan = cnext;
+ ubifs_assert(c, orphan->cmt);
+ orph->inos[i] = cpu_to_le64(orphan->inum);
+ orphan->cmt = 0;
+ cnext = orphan->cnext;
+ orphan->cnext = NULL;
+ }
+ c->orph_cnext = cnext;
+ c->cmt_orphans -= cnt;
+ spin_unlock(&c->orphan_lock);
+ if (c->cmt_orphans)
+ orph->cmt_no = cpu_to_le64(c->cmt_no);
+ else
+ /* Mark the last node of the commit */
+ orph->cmt_no = cpu_to_le64((c->cmt_no) | (1ULL << 63));
+ ubifs_assert(c, c->ohead_offs + len <= c->leb_size);
+ ubifs_assert(c, c->ohead_lnum >= c->orph_first);
+ ubifs_assert(c, c->ohead_lnum <= c->orph_last);
+ err = do_write_orph_node(c, len, atomic);
+ c->ohead_offs += ALIGN(len, c->min_io_size);
+ c->ohead_offs = ALIGN(c->ohead_offs, 8);
+ return err;
+}
+
+/**
+ * write_orph_nodes - write orphan nodes until there are no more to commit.
+ * @c: UBIFS file-system description object
+ * @atomic: write atomically
+ *
+ * This function writes orphan nodes for all the orphans to commit. On success,
+ * %0 is returned, otherwise a negative error code is returned.
+ */
+static int write_orph_nodes(struct ubifs_info *c, int atomic)
+{
+ int err;
+
+ while (c->cmt_orphans > 0) {
+ err = write_orph_node(c, atomic);
+ if (err)
+ return err;
+ }
+ if (atomic) {
+ int lnum;
+
+ /* Unmap any unused LEBs after consolidation */
+ for (lnum = c->ohead_lnum + 1; lnum <= c->orph_last; lnum++) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/**
+ * consolidate - consolidate the orphan area.
+ * @c: UBIFS file-system description object
+ *
+ * This function enables consolidation by putting all the orphans into the list
+ * to commit. The list is in the order that the orphans were added, and the
+ * LEBs are written atomically in order, so at no time can orphans be lost by
+ * an unclean unmount.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int consolidate(struct ubifs_info *c)
+{
+ int tot_avail = tot_avail_orphs(c), err = 0;
+
+ spin_lock(&c->orphan_lock);
+ dbg_cmt("there is space for %d orphans and there are %d",
+ tot_avail, c->tot_orphans);
+ if (c->tot_orphans - c->new_orphans <= tot_avail) {
+ struct ubifs_orphan *orphan, **last;
+ int cnt = 0;
+
+ /* Change the cnext list to include all non-new orphans */
+ last = &c->orph_cnext;
+ list_for_each_entry(orphan, &c->orph_list, list) {
+ if (orphan->new)
+ continue;
+ orphan->cmt = 1;
+ *last = orphan;
+ last = &orphan->cnext;
+ cnt += 1;
+ }
+ *last = NULL;
+ ubifs_assert(c, cnt == c->tot_orphans - c->new_orphans);
+ c->cmt_orphans = cnt;
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ } else {
+ /*
+ * We limit the number of orphans so that this should
+ * never happen.
+ */
+ ubifs_err(c, "out of space in orphan area");
+ err = -EINVAL;
+ }
+ spin_unlock(&c->orphan_lock);
+ return err;
+}
+
+/**
+ * commit_orphans - commit orphans.
+ * @c: UBIFS file-system description object
+ *
+ * This function commits orphans to flash. On success, %0 is returned,
+ * otherwise a negative error code is returned.
+ */
+static int commit_orphans(struct ubifs_info *c)
+{
+ int avail, atomic = 0, err;
+
+ ubifs_assert(c, c->cmt_orphans > 0);
+ avail = avail_orphs(c);
+ if (avail < c->cmt_orphans) {
+ /* Not enough space to write new orphans, so consolidate */
+ err = consolidate(c);
+ if (err)
+ return err;
+ atomic = 1;
+ }
+ err = write_orph_nodes(c, atomic);
+ return err;
+}
+
+/**
+ * erase_deleted - erase the orphans marked for deletion.
+ * @c: UBIFS file-system description object
+ *
+ * During commit, the orphans being committed cannot be deleted, so they are
+ * marked for deletion and deleted by this function. Also, the recovery
+ * adds killed orphans to the deletion list, and therefore they are deleted
+ * here too.
+ */
+static void erase_deleted(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orphan, *dnext;
+
+ spin_lock(&c->orphan_lock);
+ dnext = c->orph_dnext;
+ while (dnext) {
+ orphan = dnext;
+ dnext = orphan->dnext;
+ ubifs_assert(c, !orphan->new);
+ ubifs_assert(c, orphan->del);
+ list_del(&orphan->list);
+ c->tot_orphans -= 1;
+ dbg_gen("deleting orphan ino %lu", (unsigned long)orphan->inum);
+ kfree(orphan);
+ }
+ c->orph_dnext = NULL;
+ spin_unlock(&c->orphan_lock);
+}
+
+/**
+ * ubifs_orphan_end_commit - end commit of orphans.
+ * @c: UBIFS file-system description object
+ *
+ * End commit of orphans.
+ */
+int ubifs_orphan_end_commit(struct ubifs_info *c)
+{
+ int err;
+
+ if (c->cmt_orphans != 0) {
+ err = commit_orphans(c);
+ if (err)
+ return err;
+ }
+ erase_deleted(c);
+ err = dbg_check_orphans(c);
+ return err;
+}
+
+/**
+ * ubifs_clear_orphans - erase all LEBs used for orphans.
+ * @c: UBIFS file-system description object
+ *
+ * If recovery is not required, then the orphans from the previous session
+ * are not needed. This function locates the LEBs used to record
+ * orphans, and un-maps them.
+ */
+int ubifs_clear_orphans(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ return 0;
+}
+
+/**
+ * do_kill_orphans - remove orphan inodes from the index.
+ * @c: UBIFS file-system description object
+ * @sleb: scanned LEB
+ * @last_cmt_no: cmt_no of last orphan node read is passed and returned here
+ * @outofdate: whether the LEB is out of date is returned here
+ * @last_flagged: whether the end orphan node is encountered
+ *
+ * This function is a helper to the 'kill_orphans()' function. It goes through
+ * every orphan node in a LEB and for every inode number recorded, removes
+ * all keys for that inode from the TNC.
+ */
+static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ unsigned long long *last_cmt_no, int *outofdate,
+ int *last_flagged)
+{
+ struct ubifs_scan_node *snod;
+ struct ubifs_orph_node *orph;
+ struct ubifs_ino_node *ino = NULL;
+ unsigned long long cmt_no;
+ ino_t inum;
+ int i, n, err, first = 1;
+
+ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ if (snod->type != UBIFS_ORPH_NODE) {
+ ubifs_err(c, "invalid node type %d in orphan area at %d:%d",
+ snod->type, sleb->lnum, snod->offs);
+ ubifs_dump_node(c, snod->node,
+ c->leb_size - snod->offs);
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ orph = snod->node;
+
+ /* Check commit number */
+ cmt_no = le64_to_cpu(orph->cmt_no) & LLONG_MAX;
+ /*
+ * The commit number on the master node may be less, because
+ * of a failed commit. If there are several failed commits in a
+ * row, the commit number written on orphan nodes will continue
+ * to increase (because the commit number is adjusted here) even
+ * though the commit number on the master node stays the same
+ * because the master node has not been re-written.
+ */
+ if (cmt_no > c->cmt_no)
+ c->cmt_no = cmt_no;
+ if (cmt_no < *last_cmt_no && *last_flagged) {
+ /*
+ * The last orphan node had a higher commit number and
+ * was flagged as the last written for that commit
+ * number. That makes this orphan node, out of date.
+ */
+ if (!first) {
+ ubifs_err(c, "out of order commit number %llu in orphan node at %d:%d",
+ cmt_no, sleb->lnum, snod->offs);
+ ubifs_dump_node(c, snod->node,
+ c->leb_size - snod->offs);
+ err = -EINVAL;
+ goto out_free;
+ }
+ dbg_rcvry("out of date LEB %d", sleb->lnum);
+ *outofdate = 1;
+ err = 0;
+ goto out_free;
+ }
+
+ if (first)
+ first = 0;
+
+ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
+ for (i = 0; i < n; i++) {
+ union ubifs_key key;
+
+ inum = le64_to_cpu(orph->inos[i]);
+
+ ino_key_init(c, &key, inum);
+ err = ubifs_tnc_lookup(c, &key, ino);
+ if (err && err != -ENOENT)
+ goto out_free;
+
+ /*
+ * Check whether an inode can really get deleted.
+ * linkat() with O_TMPFILE allows rebirth of an inode.
+ */
+ if (err == 0 && ino->nlink == 0) {
+ dbg_rcvry("deleting orphaned inode %lu",
+ (unsigned long)inum);
+
+ err = ubifs_tnc_remove_ino(c, inum);
+ if (err)
+ goto out_ro;
+ }
+ }
+
+ *last_cmt_no = cmt_no;
+ if (le64_to_cpu(orph->cmt_no) & (1ULL << 63)) {
+ dbg_rcvry("last orph node for commit %llu at %d:%d",
+ cmt_no, sleb->lnum, snod->offs);
+ *last_flagged = 1;
+ } else
+ *last_flagged = 0;
+ }
+
+ err = 0;
+out_free:
+ kfree(ino);
+ return err;
+
+out_ro:
+ ubifs_ro_mode(c, err);
+ kfree(ino);
+ return err;
+}
+
+/**
+ * kill_orphans - remove all orphan inodes from the index.
+ * @c: UBIFS file-system description object
+ *
+ * If recovery is required, then orphan inodes recorded during the previous
+ * session (which ended with an unclean unmount) must be deleted from the index.
+ * This is done by updating the TNC, but since the index is not updated until
+ * the next commit, the LEBs where the orphan information is recorded are not
+ * erased until the next commit.
+ */
+static int kill_orphans(struct ubifs_info *c)
+{
+ unsigned long long last_cmt_no = 0;
+ int lnum, err = 0, outofdate = 0, last_flagged = 0;
+
+ c->ohead_lnum = c->orph_first;
+ c->ohead_offs = 0;
+ /* Check no-orphans flag and skip this if no orphans */
+ if (c->no_orphs) {
+ dbg_rcvry("no orphans");
+ return 0;
+ }
+ /*
+ * Orph nodes always start at c->orph_first and are written to each
+ * successive LEB in turn. Generally unused LEBs will have been unmapped
+ * but may contain out of date orphan nodes if the unmap didn't go
+ * through. In addition, the last orphan node written for each commit is
+ * marked (top bit of orph->cmt_no is set to 1). It is possible that
+ * there are orphan nodes from the next commit (i.e. the commit did not
+ * complete successfully). In that case, no orphans will have been lost
+ * due to the way that orphans are written, and any orphans added will
+ * be valid orphans anyway and so can be deleted.
+ */
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ struct ubifs_scan_leb *sleb;
+
+ dbg_rcvry("LEB %d", lnum);
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) == -EUCLEAN)
+ sleb = ubifs_recover_leb(c, lnum, 0,
+ c->sbuf, -1);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ break;
+ }
+ }
+ err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate,
+ &last_flagged);
+ if (err || outofdate) {
+ ubifs_scan_destroy(sleb);
+ break;
+ }
+ if (sleb->endpt) {
+ c->ohead_lnum = lnum;
+ c->ohead_offs = sleb->endpt;
+ }
+ ubifs_scan_destroy(sleb);
+ }
+ return err;
+}
+
+/**
+ * ubifs_mount_orphans - delete orphan inodes and erase LEBs that recorded them.
+ * @c: UBIFS file-system description object
+ * @unclean: indicates recovery from unclean unmount
+ * @read_only: indicates read only mount
+ *
+ * This function is called when mounting to erase orphans from the previous
+ * session. If UBIFS was not unmounted cleanly, then the inodes recorded as
+ * orphans are deleted.
+ */
+int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only)
+{
+ int err = 0;
+
+ c->max_orphans = tot_avail_orphs(c);
+
+ if (!read_only) {
+ c->orph_buf = vmalloc(c->leb_size);
+ if (!c->orph_buf)
+ return -ENOMEM;
+ }
+
+ if (unclean)
+ err = kill_orphans(c);
+ else if (!read_only)
+ err = ubifs_clear_orphans(c);
+
+ return err;
+}
+
+/*
+ * Everything below is related to debugging.
+ */
+
+struct check_orphan {
+ struct rb_node rb;
+ ino_t inum;
+};
+
+struct check_info {
+ unsigned long last_ino;
+ unsigned long tot_inos;
+ unsigned long missing;
+ unsigned long long leaf_cnt;
+ struct ubifs_ino_node *node;
+ struct rb_root root;
+};
+
+static bool dbg_find_orphan(struct ubifs_info *c, ino_t inum)
+{
+ bool found = false;
+
+ spin_lock(&c->orphan_lock);
+ found = !!lookup_orphan(c, inum);
+ spin_unlock(&c->orphan_lock);
+
+ return found;
+}
+
+static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)
+{
+ struct check_orphan *orphan, *o;
+ struct rb_node **p, *parent = NULL;
+
+ orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS);
+ if (!orphan)
+ return -ENOMEM;
+ orphan->inum = inum;
+
+ p = &root->rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct check_orphan, rb);
+ if (inum < o->inum)
+ p = &(*p)->rb_left;
+ else if (inum > o->inum)
+ p = &(*p)->rb_right;
+ else {
+ kfree(orphan);
+ return 0;
+ }
+ }
+ rb_link_node(&orphan->rb, parent, p);
+ rb_insert_color(&orphan->rb, root);
+ return 0;
+}
+
+static int dbg_find_check_orphan(struct rb_root *root, ino_t inum)
+{
+ struct check_orphan *o;
+ struct rb_node *p;
+
+ p = root->rb_node;
+ while (p) {
+ o = rb_entry(p, struct check_orphan, rb);
+ if (inum < o->inum)
+ p = p->rb_left;
+ else if (inum > o->inum)
+ p = p->rb_right;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static void dbg_free_check_tree(struct rb_root *root)
+{
+ struct check_orphan *o, *n;
+
+ rbtree_postorder_for_each_entry_safe(o, n, root, rb)
+ kfree(o);
+}
+
+static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *priv)
+{
+ struct check_info *ci = priv;
+ ino_t inum;
+ int err;
+
+ inum = key_inum(c, &zbr->key);
+ if (inum != ci->last_ino) {
+ /*
+ * Lowest node type is the inode node or xattr entry(when
+ * selinux/encryption is enabled), so it comes first
+ */
+ if (key_type(c, &zbr->key) != UBIFS_INO_KEY &&
+ key_type(c, &zbr->key) != UBIFS_XENT_KEY)
+ ubifs_err(c, "found orphan node ino %lu, type %d",
+ (unsigned long)inum, key_type(c, &zbr->key));
+ ci->last_ino = inum;
+ ci->tot_inos += 1;
+ err = ubifs_tnc_read_node(c, zbr, ci->node);
+ if (err) {
+ ubifs_err(c, "node read failed, error %d", err);
+ return err;
+ }
+ if (ci->node->nlink == 0)
+ /* Must be recorded as an orphan */
+ if (!dbg_find_check_orphan(&ci->root, inum) &&
+ !dbg_find_orphan(c, inum)) {
+ ubifs_err(c, "missing orphan, ino %lu",
+ (unsigned long)inum);
+ ci->missing += 1;
+ }
+ }
+ ci->leaf_cnt += 1;
+ return 0;
+}
+
+static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb)
+{
+ struct ubifs_scan_node *snod;
+ struct ubifs_orph_node *orph;
+ ino_t inum;
+ int i, n, err;
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+ if (snod->type != UBIFS_ORPH_NODE)
+ continue;
+ orph = snod->node;
+ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
+ for (i = 0; i < n; i++) {
+ inum = le64_to_cpu(orph->inos[i]);
+ err = dbg_ins_check_orphan(&ci->root, inum);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci)
+{
+ int lnum, err = 0;
+ void *buf;
+
+ /* Check no-orphans flag and skip this if no orphans */
+ if (c->no_orphs)
+ return 0;
+
+ buf = __vmalloc(c->leb_size, GFP_NOFS);
+ if (!buf) {
+ ubifs_err(c, "cannot allocate memory to check orphans");
+ return 0;
+ }
+
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ struct ubifs_scan_leb *sleb;
+
+ sleb = ubifs_scan(c, lnum, 0, buf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ break;
+ }
+
+ err = dbg_read_orphans(ci, sleb);
+ ubifs_scan_destroy(sleb);
+ if (err)
+ break;
+ }
+
+ vfree(buf);
+ return err;
+}
+
+static int dbg_check_orphans(struct ubifs_info *c)
+{
+ struct check_info ci;
+ int err;
+
+ if (!dbg_is_chk_orph(c))
+ return 0;
+
+ ci.last_ino = 0;
+ ci.tot_inos = 0;
+ ci.missing = 0;
+ ci.leaf_cnt = 0;
+ ci.root = RB_ROOT;
+ ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ci.node) {
+ ubifs_err(c, "out of memory");
+ return -ENOMEM;
+ }
+
+ err = dbg_scan_orphans(c, &ci);
+ if (err)
+ goto out;
+
+ err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci);
+ if (err) {
+ ubifs_err(c, "cannot scan TNC, error %d", err);
+ goto out;
+ }
+
+ if (ci.missing) {
+ ubifs_err(c, "%lu missing orphan(s)", ci.missing);
+ err = -EINVAL;
+ goto out;
+ }
+
+ dbg_cmt("last inode number is %lu", ci.last_ino);
+ dbg_cmt("total number of inodes is %lu", ci.tot_inos);
+ dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt);
+
+out:
+ dbg_free_check_tree(&ci.root);
+ kfree(ci.node);
+ return err;
+}
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
new file mode 100644
index 00000000..f0d51dd2
--- /dev/null
+++ b/ubifs-utils/libubifs/recovery.c
@@ -0,0 +1,1588 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements functions needed to recover from unclean un-mounts.
+ * When UBIFS is mounted, it checks a flag on the master node to determine if
+ * an un-mount was completed successfully. If not, the process of mounting
+ * incorporates additional checking and fixing of on-flash data structures.
+ * UBIFS always cleans away all remnants of an unclean un-mount, so that
+ * errors do not accumulate. However UBIFS defers recovery if it is mounted
+ * read-only, and the flash is not modified in that case.
+ *
+ * The general UBIFS approach to the recovery is that it recovers from
+ * corruptions which could be caused by power cuts, but it refuses to recover
+ * from corruption caused by other reasons. And UBIFS tries to distinguish
+ * between these 2 reasons of corruptions and silently recover in the former
+ * case and loudly complain in the latter case.
+ *
+ * UBIFS writes only to erased LEBs, so it writes only to the flash space
+ * containing only 0xFFs. UBIFS also always writes strictly from the beginning
+ * of the LEB to the end. And UBIFS assumes that the underlying flash media
+ * writes in @c->max_write_size bytes at a time.
+ *
+ * Hence, if UBIFS finds a corrupted node at offset X, it expects only the min.
+ * I/O unit corresponding to offset X to contain corrupted data, all the
+ * following min. I/O units have to contain empty space (all 0xFFs). If this is
+ * not true, the corruption cannot be the result of a power cut, and UBIFS
+ * refuses to mount.
+ */
+
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include "ubifs.h"
+
+/**
+ * is_empty - determine whether a buffer is empty (contains all 0xff).
+ * @buf: buffer to clean
+ * @len: length of buffer
+ *
+ * This function returns %1 if the buffer is empty (contains all 0xff) otherwise
+ * %0 is returned.
+ */
+static int is_empty(void *buf, int len)
+{
+ uint8_t *p = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (*p++ != 0xff)
+ return 0;
+ return 1;
+}
+
+/**
+ * first_non_ff - find offset of the first non-0xff byte.
+ * @buf: buffer to search in
+ * @len: length of buffer
+ *
+ * This function returns offset of the first non-0xff byte in @buf or %-1 if
+ * the buffer contains only 0xff bytes.
+ */
+static int first_non_ff(void *buf, int len)
+{
+ uint8_t *p = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (*p++ != 0xff)
+ return i;
+ return -1;
+}
+
+/**
+ * get_master_node - get the last valid master node allowing for corruption.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @pbuf: buffer containing the LEB read, is returned here
+ * @mst: master node, if found, is returned here
+ * @cor: corruption, if found, is returned here
+ *
+ * This function allocates a buffer, reads the LEB into it, and finds and
+ * returns the last valid master node allowing for one area of corruption.
+ * The corrupt area, if there is one, must be consistent with the assumption
+ * that it is the result of an unclean unmount while the master node was being
+ * written. Under those circumstances, it is valid to use the previously written
+ * master node.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf,
+ struct ubifs_mst_node **mst, void **cor)
+{
+ const int sz = c->mst_node_alsz;
+ int err, offs, len;
+ void *sbuf, *buf;
+
+ sbuf = vmalloc(c->leb_size);
+ if (!sbuf)
+ return -ENOMEM;
+
+ err = ubifs_leb_read(c, lnum, sbuf, 0, c->leb_size, 0);
+ if (err && err != -EBADMSG)
+ goto out_free;
+
+ /* Find the first position that is definitely not a node */
+ offs = 0;
+ buf = sbuf;
+ len = c->leb_size;
+ while (offs + UBIFS_MST_NODE_SZ <= c->leb_size) {
+ struct ubifs_ch *ch = buf;
+
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC)
+ break;
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ /* See if there was a valid master node before that */
+ if (offs) {
+ int ret;
+
+ offs -= sz;
+ buf -= sz;
+ len += sz;
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret != SCANNED_A_NODE && offs) {
+ /* Could have been corruption so check one place back */
+ offs -= sz;
+ buf -= sz;
+ len += sz;
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret != SCANNED_A_NODE)
+ /*
+ * We accept only one area of corruption because
+ * we are assuming that it was caused while
+ * trying to write a master node.
+ */
+ goto out_err;
+ }
+ if (ret == SCANNED_A_NODE) {
+ struct ubifs_ch *ch = buf;
+
+ if (ch->node_type != UBIFS_MST_NODE)
+ goto out_err;
+ dbg_rcvry("found a master node at %d:%d", lnum, offs);
+ *mst = buf;
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ }
+ /* Check for corruption */
+ if (offs < c->leb_size) {
+ if (!is_empty(buf, min_t(int, len, sz))) {
+ *cor = buf;
+ dbg_rcvry("found corruption at %d:%d", lnum, offs);
+ }
+ offs += sz;
+ buf += sz;
+ len -= sz;
+ }
+ /* Check remaining empty space */
+ if (offs < c->leb_size)
+ if (!is_empty(buf, len))
+ goto out_err;
+ *pbuf = sbuf;
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ vfree(sbuf);
+ *mst = NULL;
+ *cor = NULL;
+ return err;
+}
+
+/**
+ * write_rcvrd_mst_node - write recovered master node.
+ * @c: UBIFS file-system description object
+ * @mst: master node
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int write_rcvrd_mst_node(struct ubifs_info *c,
+ struct ubifs_mst_node *mst)
+{
+ int err = 0, lnum = UBIFS_MST_LNUM, sz = c->mst_node_alsz;
+ __le32 save_flags;
+
+ dbg_rcvry("recovery");
+
+ save_flags = mst->flags;
+ mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY);
+
+ err = ubifs_prepare_node_hmac(c, mst, UBIFS_MST_NODE_SZ,
+ offsetof(struct ubifs_mst_node, hmac), 1);
+ if (err)
+ goto out;
+ err = ubifs_leb_change(c, lnum, mst, sz);
+ if (err)
+ goto out;
+ err = ubifs_leb_change(c, lnum + 1, mst, sz);
+ if (err)
+ goto out;
+out:
+ mst->flags = save_flags;
+ return err;
+}
+
+/**
+ * ubifs_recover_master_node - recover the master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function recovers the master node from corruption that may occur due to
+ * an unclean unmount.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_master_node(struct ubifs_info *c)
+{
+ void *buf1 = NULL, *buf2 = NULL, *cor1 = NULL, *cor2 = NULL;
+ struct ubifs_mst_node *mst1 = NULL, *mst2 = NULL, *mst;
+ const int sz = c->mst_node_alsz;
+ int err, offs1, offs2;
+
+ dbg_rcvry("recovery");
+
+ err = get_master_node(c, UBIFS_MST_LNUM, &buf1, &mst1, &cor1);
+ if (err)
+ goto out_free;
+
+ err = get_master_node(c, UBIFS_MST_LNUM + 1, &buf2, &mst2, &cor2);
+ if (err)
+ goto out_free;
+
+ if (mst1) {
+ offs1 = (void *)mst1 - buf1;
+ if ((le32_to_cpu(mst1->flags) & UBIFS_MST_RCVRY) &&
+ (offs1 == 0 && !cor1)) {
+ /*
+ * mst1 was written by recovery at offset 0 with no
+ * corruption.
+ */
+ dbg_rcvry("recovery recovery");
+ mst = mst1;
+ } else if (mst2) {
+ offs2 = (void *)mst2 - buf2;
+ if (offs1 == offs2) {
+ /* Same offset, so must be the same */
+ if (ubifs_compare_master_node(c, mst1, mst2))
+ goto out_err;
+ mst = mst1;
+ } else if (offs2 + sz == offs1) {
+ /* 1st LEB was written, 2nd was not */
+ if (cor1)
+ goto out_err;
+ mst = mst1;
+ } else if (offs1 == 0 &&
+ c->leb_size - offs2 - sz < sz) {
+ /* 1st LEB was unmapped and written, 2nd not */
+ if (cor1)
+ goto out_err;
+ mst = mst1;
+ } else
+ goto out_err;
+ } else {
+ /*
+ * 2nd LEB was unmapped and about to be written, so
+ * there must be only one master node in the first LEB
+ * and no corruption.
+ */
+ if (offs1 != 0 || cor1)
+ goto out_err;
+ mst = mst1;
+ }
+ } else {
+ if (!mst2)
+ goto out_err;
+ /*
+ * 1st LEB was unmapped and about to be written, so there must
+ * be no room left in 2nd LEB.
+ */
+ offs2 = (void *)mst2 - buf2;
+ if (offs2 + sz + sz <= c->leb_size)
+ goto out_err;
+ mst = mst2;
+ }
+
+ ubifs_msg(c, "recovered master node from LEB %d",
+ (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1));
+
+ memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ);
+
+ if (c->ro_mount) {
+ /* Read-only mode. Keep a copy for switching to rw mode */
+ c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL);
+ if (!c->rcvrd_mst_node) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ);
+
+ /*
+ * We had to recover the master node, which means there was an
+ * unclean reboot. However, it is possible that the master node
+ * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set.
+ * E.g., consider the following chain of events:
+ *
+ * 1. UBIFS was cleanly unmounted, so the master node is clean
+ * 2. UBIFS is being mounted R/W and starts changing the master
+ * node in the first (%UBIFS_MST_LNUM). A power cut happens,
+ * so this LEB ends up with some amount of garbage at the
+ * end.
+ * 3. UBIFS is being mounted R/O. We reach this place and
+ * recover the master node from the second LEB
+ * (%UBIFS_MST_LNUM + 1). But we cannot update the media
+ * because we are being mounted R/O. We have to defer the
+ * operation.
+ * 4. However, this master node (@c->mst_node) is marked as
+ * clean (since the step 1). And if we just return, the
+ * mount code will be confused and won't recover the master
+ * node when it is re-mounter R/W later.
+ *
+ * Thus, to force the recovery by marking the master node as
+ * dirty.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ } else {
+ /* Write the recovered master node */
+ c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1;
+ err = write_rcvrd_mst_node(c, c->mst_node);
+ if (err)
+ goto out_free;
+ }
+
+ vfree(buf2);
+ vfree(buf1);
+
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ ubifs_err(c, "failed to recover master node");
+ if (mst1) {
+ ubifs_err(c, "dumping first master node");
+ ubifs_dump_node(c, mst1, c->leb_size - ((void *)mst1 - buf1));
+ }
+ if (mst2) {
+ ubifs_err(c, "dumping second master node");
+ ubifs_dump_node(c, mst2, c->leb_size - ((void *)mst2 - buf2));
+ }
+ vfree(buf2);
+ vfree(buf1);
+ return err;
+}
+
+/**
+ * ubifs_write_rcvrd_mst_node - write the recovered master node.
+ * @c: UBIFS file-system description object
+ *
+ * This function writes the master node that was recovered during mounting in
+ * read-only mode and must now be written because we are remounting rw.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_write_rcvrd_mst_node(struct ubifs_info *c)
+{
+ int err;
+
+ if (!c->rcvrd_mst_node)
+ return 0;
+ c->rcvrd_mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = write_rcvrd_mst_node(c, c->rcvrd_mst_node);
+ if (err)
+ return err;
+ kfree(c->rcvrd_mst_node);
+ c->rcvrd_mst_node = NULL;
+ return 0;
+}
+
+/**
+ * is_last_write - determine if an offset was in the last write to a LEB.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to check
+ * @offs: offset to check
+ *
+ * This function returns %1 if @offs was in the last write to the LEB whose data
+ * is in @buf, otherwise %0 is returned. The determination is made by checking
+ * for subsequent empty space starting from the next @c->max_write_size
+ * boundary.
+ */
+static int is_last_write(const struct ubifs_info *c, void *buf, int offs)
+{
+ int empty_offs, check_len;
+ uint8_t *p;
+
+ /*
+ * Round up to the next @c->max_write_size boundary i.e. @offs is in
+ * the last wbuf written. After that should be empty space.
+ */
+ empty_offs = ALIGN(offs + 1, c->max_write_size);
+ check_len = c->leb_size - empty_offs;
+ p = buf + empty_offs - offs;
+ return is_empty(p, check_len);
+}
+
+/**
+ * clean_buf - clean the data from an LEB sitting in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to clean
+ * @lnum: LEB number to clean
+ * @offs: offset from which to clean
+ * @len: length of buffer
+ *
+ * This function pads up to the next min_io_size boundary (if there is one) and
+ * sets empty space to all 0xff. @buf, @offs and @len are updated to the next
+ * @c->min_io_size boundary.
+ */
+static void clean_buf(const struct ubifs_info *c, void **buf, int lnum,
+ int *offs, int *len)
+{
+ int empty_offs, pad_len;
+
+ dbg_rcvry("cleaning corruption at %d:%d", lnum, *offs);
+
+ ubifs_assert(c, !(*offs & 7));
+ empty_offs = ALIGN(*offs, c->min_io_size);
+ pad_len = empty_offs - *offs;
+ ubifs_pad(c, *buf, pad_len);
+ *offs += pad_len;
+ *buf += pad_len;
+ *len -= pad_len;
+ memset(*buf, 0xff, c->leb_size - empty_offs);
+}
+
+/**
+ * no_more_nodes - determine if there are no more nodes in a buffer.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to check
+ * @len: length of buffer
+ * @lnum: LEB number of the LEB from which @buf was read
+ * @offs: offset from which @buf was read
+ *
+ * This function ensures that the corrupted node at @offs is the last thing
+ * written to a LEB. This function returns %1 if more data is not found and
+ * %0 if more data is found.
+ */
+static int no_more_nodes(const struct ubifs_info *c, void *buf, int len,
+ int lnum, int offs)
+{
+ struct ubifs_ch *ch = buf;
+ int skip, dlen = le32_to_cpu(ch->len);
+
+ /* Check for empty space after the corrupt node's common header */
+ skip = ALIGN(offs + UBIFS_CH_SZ, c->max_write_size) - offs;
+ if (is_empty(buf + skip, len - skip))
+ return 1;
+ /*
+ * The area after the common header size is not empty, so the common
+ * header must be intact. Check it.
+ */
+ if (ubifs_check_node(c, buf, len, lnum, offs, 1, 0) != -EUCLEAN) {
+ dbg_rcvry("unexpected bad common header at %d:%d", lnum, offs);
+ return 0;
+ }
+ /* Now we know the corrupt node's length we can skip over it */
+ skip = ALIGN(offs + dlen, c->max_write_size) - offs;
+ /* After which there should be empty space */
+ if (is_empty(buf + skip, len - skip))
+ return 1;
+ dbg_rcvry("unexpected data at %d:%d", lnum, offs + skip);
+ return 0;
+}
+
+/**
+ * fix_unclean_leb - fix an unclean LEB.
+ * @c: UBIFS file-system description object
+ * @sleb: scanned LEB information
+ * @start: offset where scan started
+ */
+static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int start)
+{
+ int lnum = sleb->lnum, endpt = start;
+
+ /* Get the end offset of the last node we are keeping */
+ if (!list_empty(&sleb->nodes)) {
+ struct ubifs_scan_node *snod;
+
+ snod = list_entry(sleb->nodes.prev,
+ struct ubifs_scan_node, list);
+ endpt = snod->offs + snod->len;
+ }
+
+ if (c->ro_mount && !c->remounting_rw) {
+ /* Add to recovery list */
+ struct ubifs_unclean_leb *ucleb;
+
+ dbg_rcvry("need to fix LEB %d start %d endpt %d",
+ lnum, start, sleb->endpt);
+ ucleb = kzalloc(sizeof(struct ubifs_unclean_leb), GFP_NOFS);
+ if (!ucleb)
+ return -ENOMEM;
+ ucleb->lnum = lnum;
+ ucleb->endpt = endpt;
+ list_add_tail(&ucleb->list, &c->unclean_leb_list);
+ } else {
+ /* Write the fixed LEB back to flash */
+ int err;
+
+ dbg_rcvry("fixing LEB %d start %d endpt %d",
+ lnum, start, sleb->endpt);
+ if (endpt == 0) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ } else {
+ int len = ALIGN(endpt, c->min_io_size);
+
+ if (start) {
+ err = ubifs_leb_read(c, lnum, sleb->buf, 0,
+ start, 1);
+ if (err)
+ return err;
+ }
+ /* Pad to min_io_size */
+ if (len > endpt) {
+ int pad_len = len - ALIGN(endpt, 8);
+
+ if (pad_len > 0) {
+ void *buf = sleb->buf + len - pad_len;
+
+ ubifs_pad(c, buf, pad_len);
+ }
+ }
+ err = ubifs_leb_change(c, lnum, sleb->buf, len);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/**
+ * drop_last_group - drop the last group of nodes.
+ * @sleb: scanned LEB information
+ * @offs: offset of dropped nodes is returned here
+ *
+ * This is a helper function for 'ubifs_recover_leb()' which drops the last
+ * group of nodes of the scanned LEB.
+ */
+static void drop_last_group(struct ubifs_scan_leb *sleb, int *offs)
+{
+ while (!list_empty(&sleb->nodes)) {
+ struct ubifs_scan_node *snod;
+ struct ubifs_ch *ch;
+
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+ ch = snod->node;
+ if (ch->group_type != UBIFS_IN_NODE_GROUP)
+ break;
+
+ dbg_rcvry("dropping grouped node at %d:%d",
+ sleb->lnum, snod->offs);
+ *offs = snod->offs;
+ list_del(&snod->list);
+ kfree(snod);
+ sleb->nodes_cnt -= 1;
+ }
+}
+
+/**
+ * drop_last_node - drop the last node.
+ * @sleb: scanned LEB information
+ * @offs: offset of dropped nodes is returned here
+ *
+ * This is a helper function for 'ubifs_recover_leb()' which drops the last
+ * node of the scanned LEB.
+ */
+static void drop_last_node(struct ubifs_scan_leb *sleb, int *offs)
+{
+ struct ubifs_scan_node *snod;
+
+ if (!list_empty(&sleb->nodes)) {
+ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
+ list);
+
+ dbg_rcvry("dropping last node at %d:%d",
+ sleb->lnum, snod->offs);
+ *offs = snod->offs;
+ list_del(&snod->list);
+ kfree(snod);
+ sleb->nodes_cnt -= 1;
+ }
+}
+
+/**
+ * ubifs_recover_leb - scan and recover a LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @offs: offset
+ * @sbuf: LEB-sized buffer to use
+ * @jhead: journal head number this LEB belongs to (%-1 if the LEB does not
+ * belong to any journal head)
+ *
+ * This function does a scan of a LEB, but caters for errors that might have
+ * been caused by the unclean unmount from which we are attempting to recover.
+ * Returns the scanned information on success and a negative error code on
+ * failure.
+ */
+struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int jhead)
+{
+ int ret = 0, err, len = c->leb_size - offs, start = offs, min_io_unit;
+ int grouped = jhead == -1 ? 0 : c->jheads[jhead].grouped;
+ struct ubifs_scan_leb *sleb;
+ void *buf = sbuf + offs;
+
+ dbg_rcvry("%d:%d, jhead %d, grouped %d", lnum, offs, jhead, grouped);
+
+ sleb = ubifs_start_scan(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return sleb;
+
+ ubifs_assert(c, len >= 8);
+ while (len >= 8) {
+ dbg_scan("look at LEB %d:%d (%d bytes left)",
+ lnum, offs, len);
+
+ cond_resched();
+
+ /*
+ * Scan quietly until there is an error from which we cannot
+ * recover
+ */
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
+ if (ret == SCANNED_A_NODE) {
+ /* A valid node, and not a padding node */
+ struct ubifs_ch *ch = buf;
+ int node_len;
+
+ err = ubifs_add_snod(c, sleb, buf, offs);
+ if (err)
+ goto error;
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ } else if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ } else if (ret == SCANNED_EMPTY_SPACE ||
+ ret == SCANNED_GARBAGE ||
+ ret == SCANNED_A_BAD_PAD_NODE ||
+ ret == SCANNED_A_CORRUPT_NODE) {
+ dbg_rcvry("found corruption (%d) at %d:%d",
+ ret, lnum, offs);
+ break;
+ } else {
+ ubifs_err(c, "unexpected return value %d", ret);
+ err = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) {
+ if (!is_last_write(c, buf, offs))
+ goto corrupted_rescan;
+ } else if (ret == SCANNED_A_CORRUPT_NODE) {
+ if (!no_more_nodes(c, buf, len, lnum, offs))
+ goto corrupted_rescan;
+ } else if (!is_empty(buf, len)) {
+ if (!is_last_write(c, buf, offs)) {
+ int corruption = first_non_ff(buf, len);
+
+ /*
+ * See header comment for this file for more
+ * explanations about the reasons we have this check.
+ */
+ ubifs_err(c, "corrupt empty space LEB %d:%d, corruption starts at %d",
+ lnum, offs, corruption);
+ /* Make sure we dump interesting non-0xFF data */
+ offs += corruption;
+ buf += corruption;
+ goto corrupted;
+ }
+ }
+
+ min_io_unit = round_down(offs, c->min_io_size);
+ if (grouped)
+ /*
+ * If nodes are grouped, always drop the incomplete group at
+ * the end.
+ */
+ drop_last_group(sleb, &offs);
+
+ if (jhead == GCHD) {
+ /*
+ * If this LEB belongs to the GC head then while we are in the
+ * middle of the same min. I/O unit keep dropping nodes. So
+ * basically, what we want is to make sure that the last min.
+ * I/O unit where we saw the corruption is dropped completely
+ * with all the uncorrupted nodes which may possibly sit there.
+ *
+ * In other words, let's name the min. I/O unit where the
+ * corruption starts B, and the previous min. I/O unit A. The
+ * below code tries to deal with a situation when half of B
+ * contains valid nodes or the end of a valid node, and the
+ * second half of B contains corrupted data or garbage. This
+ * means that UBIFS had been writing to B just before the power
+ * cut happened. I do not know how realistic is this scenario
+ * that half of the min. I/O unit had been written successfully
+ * and the other half not, but this is possible in our 'failure
+ * mode emulation' infrastructure at least.
+ *
+ * So what is the problem, why we need to drop those nodes? Why
+ * can't we just clean-up the second half of B by putting a
+ * padding node there? We can, and this works fine with one
+ * exception which was reproduced with power cut emulation
+ * testing and happens extremely rarely.
+ *
+ * Imagine the file-system is full, we run GC which starts
+ * moving valid nodes from LEB X to LEB Y (obviously, LEB Y is
+ * the current GC head LEB). The @c->gc_lnum is -1, which means
+ * that GC will retain LEB X and will try to continue. Imagine
+ * that LEB X is currently the dirtiest LEB, and the amount of
+ * used space in LEB Y is exactly the same as amount of free
+ * space in LEB X.
+ *
+ * And a power cut happens when nodes are moved from LEB X to
+ * LEB Y. We are here trying to recover LEB Y which is the GC
+ * head LEB. We find the min. I/O unit B as described above.
+ * Then we clean-up LEB Y by padding min. I/O unit. And later
+ * 'ubifs_rcvry_gc_commit()' function fails, because it cannot
+ * find a dirty LEB which could be GC'd into LEB Y! Even LEB X
+ * does not match because the amount of valid nodes there does
+ * not fit the free space in LEB Y any more! And this is
+ * because of the padding node which we added to LEB Y. The
+ * user-visible effect of this which I once observed and
+ * analysed is that we cannot mount the file-system with
+ * -ENOSPC error.
+ *
+ * So obviously, to make sure that situation does not happen we
+ * should free min. I/O unit B in LEB Y completely and the last
+ * used min. I/O unit in LEB Y should be A. This is basically
+ * what the below code tries to do.
+ */
+ while (offs > min_io_unit)
+ drop_last_node(sleb, &offs);
+ }
+
+ buf = sbuf + offs;
+ len = c->leb_size - offs;
+
+ clean_buf(c, &buf, lnum, &offs, &len);
+ ubifs_end_scan(c, sleb, lnum, offs);
+
+ err = fix_unclean_leb(c, sleb, start);
+ if (err)
+ goto error;
+
+ return sleb;
+
+corrupted_rescan:
+ /* Re-scan the corrupted data with verbose messages */
+ ubifs_err(c, "corruption %d", ret);
+ ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
+corrupted:
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ err = -EUCLEAN;
+error:
+ ubifs_err(c, "LEB %d scanning failed", lnum);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+}
+
+/**
+ * get_cs_sqnum - get commit start sequence number.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of commit start node
+ * @offs: offset of commit start node
+ * @cs_sqnum: commit start sequence number is returned here
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs,
+ unsigned long long *cs_sqnum)
+{
+ struct ubifs_cs_node *cs_node = NULL;
+ int err, ret;
+
+ dbg_rcvry("at %d:%d", lnum, offs);
+ cs_node = kmalloc(UBIFS_CS_NODE_SZ, GFP_KERNEL);
+ if (!cs_node)
+ return -ENOMEM;
+ if (c->leb_size - offs < UBIFS_CS_NODE_SZ)
+ goto out_err;
+ err = ubifs_leb_read(c, lnum, (void *)cs_node, offs,
+ UBIFS_CS_NODE_SZ, 0);
+ if (err && err != -EBADMSG)
+ goto out_free;
+ ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0);
+ if (ret != SCANNED_A_NODE) {
+ ubifs_err(c, "Not a valid node");
+ goto out_err;
+ }
+ if (cs_node->ch.node_type != UBIFS_CS_NODE) {
+ ubifs_err(c, "Not a CS node, type is %d", cs_node->ch.node_type);
+ goto out_err;
+ }
+ if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) {
+ ubifs_err(c, "CS node cmt_no %llu != current cmt_no %llu",
+ (unsigned long long)le64_to_cpu(cs_node->cmt_no),
+ c->cmt_no);
+ goto out_err;
+ }
+ *cs_sqnum = le64_to_cpu(cs_node->ch.sqnum);
+ dbg_rcvry("commit start sqnum %llu", *cs_sqnum);
+ kfree(cs_node);
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out_free:
+ ubifs_err(c, "failed to get CS sqnum");
+ kfree(cs_node);
+ return err;
+}
+
+/**
+ * ubifs_recover_log_leb - scan and recover a log LEB.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @offs: offset
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function does a scan of a LEB, but caters for errors that might have
+ * been caused by unclean reboots from which we are attempting to recover
+ * (assume that only the last log LEB can be corrupted by an unclean reboot).
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf)
+{
+ struct ubifs_scan_leb *sleb;
+ int next_lnum;
+
+ dbg_rcvry("LEB %d", lnum);
+ next_lnum = lnum + 1;
+ if (next_lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ next_lnum = UBIFS_LOG_LNUM;
+ if (next_lnum != c->ltail_lnum) {
+ /*
+ * We can only recover at the end of the log, so check that the
+ * next log LEB is empty or out of date.
+ */
+ sleb = ubifs_scan(c, next_lnum, 0, sbuf, 0);
+ if (IS_ERR(sleb))
+ return sleb;
+ if (sleb->nodes_cnt) {
+ struct ubifs_scan_node *snod;
+ unsigned long long cs_sqnum = c->cs_sqnum;
+
+ snod = list_entry(sleb->nodes.next,
+ struct ubifs_scan_node, list);
+ if (cs_sqnum == 0) {
+ int err;
+
+ err = get_cs_sqnum(c, lnum, offs, &cs_sqnum);
+ if (err) {
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+ }
+ }
+ if (snod->sqnum > cs_sqnum) {
+ ubifs_err(c, "unrecoverable log corruption in LEB %d",
+ lnum);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(-EUCLEAN);
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ }
+ return ubifs_recover_leb(c, lnum, offs, sbuf, -1);
+}
+
+/**
+ * recover_head - recover a head.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of head to recover
+ * @offs: offset of head to recover
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function ensures that there is no data on the flash at a head location.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int recover_head(struct ubifs_info *c, int lnum, int offs, void *sbuf)
+{
+ int len = c->max_write_size, err;
+
+ if (offs + len > c->leb_size)
+ len = c->leb_size - offs;
+
+ if (!len)
+ return 0;
+
+ /* Read at the head location and check it is empty flash */
+ err = ubifs_leb_read(c, lnum, sbuf, offs, len, 1);
+ if (err || !is_empty(sbuf, len)) {
+ dbg_rcvry("cleaning head at %d:%d", lnum, offs);
+ if (offs == 0)
+ return ubifs_leb_unmap(c, lnum);
+ err = ubifs_leb_read(c, lnum, sbuf, 0, offs, 1);
+ if (err)
+ return err;
+ return ubifs_leb_change(c, lnum, sbuf, offs);
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_recover_inl_heads - recover index and LPT heads.
+ * @c: UBIFS file-system description object
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function ensures that there is no data on the flash at the index and
+ * LPT head locations.
+ *
+ * This deals with the recovery of a half-completed journal commit. UBIFS is
+ * careful never to overwrite the last version of the index or the LPT. Because
+ * the index and LPT are wandering trees, data from a half-completed commit will
+ * not be referenced anywhere in UBIFS. The data will be either in LEBs that are
+ * assumed to be empty and will be unmapped anyway before use, or in the index
+ * and LPT heads.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf)
+{
+ int err;
+
+ ubifs_assert(c, !c->ro_mount || c->remounting_rw);
+
+ dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs);
+ err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf);
+ if (err)
+ return err;
+
+ dbg_rcvry("checking LPT head at %d:%d", c->nhead_lnum, c->nhead_offs);
+
+ return recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf);
+}
+
+/**
+ * clean_an_unclean_leb - read and write a LEB to remove corruption.
+ * @c: UBIFS file-system description object
+ * @ucleb: unclean LEB information
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function reads a LEB up to a point pre-determined by the mount recovery,
+ * checks the nodes, and writes the result back to the flash, thereby cleaning
+ * off any following corruption, or non-fatal ECC errors.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int clean_an_unclean_leb(struct ubifs_info *c,
+ struct ubifs_unclean_leb *ucleb, void *sbuf)
+{
+ int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1;
+ void *buf = sbuf;
+
+ dbg_rcvry("LEB %d len %d", lnum, len);
+
+ if (len == 0) {
+ /* Nothing to read, just unmap it */
+ return ubifs_leb_unmap(c, lnum);
+ }
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ while (len >= 8) {
+ int ret;
+
+ cond_resched();
+
+ /* Scan quietly until there is an error */
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
+
+ if (ret == SCANNED_A_NODE) {
+ /* A valid node, and not a padding node */
+ struct ubifs_ch *ch = buf;
+ int node_len;
+
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ continue;
+ }
+
+ if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ continue;
+ }
+
+ if (ret == SCANNED_EMPTY_SPACE) {
+ ubifs_err(c, "unexpected empty space at %d:%d",
+ lnum, offs);
+ return -EUCLEAN;
+ }
+
+ if (quiet) {
+ /* Redo the last scan but noisily */
+ quiet = 0;
+ continue;
+ }
+
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ return -EUCLEAN;
+ }
+
+ /* Pad to min_io_size */
+ len = ALIGN(ucleb->endpt, c->min_io_size);
+ if (len > ucleb->endpt) {
+ int pad_len = len - ALIGN(ucleb->endpt, 8);
+
+ if (pad_len > 0) {
+ buf = c->sbuf + len - pad_len;
+ ubifs_pad(c, buf, pad_len);
+ }
+ }
+
+ /* Write back the LEB atomically */
+ err = ubifs_leb_change(c, lnum, sbuf, len);
+ if (err)
+ return err;
+
+ dbg_rcvry("cleaned LEB %d", lnum);
+
+ return 0;
+}
+
+/**
+ * ubifs_clean_lebs - clean LEBs recovered during read-only mount.
+ * @c: UBIFS file-system description object
+ * @sbuf: LEB-sized buffer to use
+ *
+ * This function cleans a LEB identified during recovery that needs to be
+ * written but was not because UBIFS was mounted read-only. This happens when
+ * remounting to read-write mode.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf)
+{
+ dbg_rcvry("recovery");
+ while (!list_empty(&c->unclean_leb_list)) {
+ struct ubifs_unclean_leb *ucleb;
+ int err;
+
+ ucleb = list_entry(c->unclean_leb_list.next,
+ struct ubifs_unclean_leb, list);
+ err = clean_an_unclean_leb(c, ucleb, sbuf);
+ if (err)
+ return err;
+ list_del(&ucleb->list);
+ kfree(ucleb);
+ }
+ return 0;
+}
+
+/**
+ * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty
+ * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static int grab_empty_leb(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ /*
+ * Note, it is very important to first search for an empty LEB and then
+ * run the commit, not vice-versa. The reason is that there might be
+ * only one empty LEB at the moment, the one which has been the
+ * @c->gc_lnum just before the power cut happened. During the regular
+ * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no
+ * one but GC can grab it. But at this moment this single empty LEB is
+ * not marked as taken, so if we run commit - what happens? Right, the
+ * commit will grab it and write the index there. Remember that the
+ * index always expands as long as there is free space, and it only
+ * starts consolidating when we run out of space.
+ *
+ * IOW, if we run commit now, we might not be able to find a free LEB
+ * after this.
+ */
+ lnum = ubifs_find_free_leb_for_idx(c);
+ if (lnum < 0) {
+ ubifs_err(c, "could not find an empty LEB");
+ ubifs_dump_lprops(c);
+ ubifs_dump_budg(c, &c->bi);
+ return lnum;
+ }
+
+ /* Reset the index flag */
+ err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_INDEX, 0);
+ if (err)
+ return err;
+
+ c->gc_lnum = lnum;
+ dbg_rcvry("found empty LEB %d, run commit", lnum);
+
+ return ubifs_run_commit(c);
+}
+
+/**
+ * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
+ * @c: UBIFS file-system description object
+ *
+ * Out-of-place garbage collection requires always one empty LEB with which to
+ * start garbage collection. The LEB number is recorded in c->gc_lnum and is
+ * written to the master node on unmounting. In the case of an unclean unmount
+ * the value of gc_lnum recorded in the master node is out of date and cannot
+ * be used. Instead, recovery must allocate an empty LEB for this purpose.
+ * However, there may not be enough empty space, in which case it must be
+ * possible to GC the dirtiest LEB into the GC head LEB.
+ *
+ * This function also runs the commit which causes the TNC updates from
+ * size-recovery and orphans to be written to the flash. That is important to
+ * ensure correct replay order for subsequent mounts.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_rcvry_gc_commit(struct ubifs_info *c)
+{
+ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+ struct ubifs_lprops lp;
+ int err;
+
+ dbg_rcvry("GC head LEB %d, offs %d", wbuf->lnum, wbuf->offs);
+
+ c->gc_lnum = -1;
+ if (wbuf->lnum == -1 || wbuf->offs == c->leb_size)
+ return grab_empty_leb(c);
+
+ err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2);
+ if (err) {
+ if (err != -ENOSPC)
+ return err;
+
+ dbg_rcvry("could not find a dirty LEB");
+ return grab_empty_leb(c);
+ }
+
+ ubifs_assert(c, !(lp.flags & LPROPS_INDEX));
+ ubifs_assert(c, lp.free + lp.dirty >= wbuf->offs);
+
+ /*
+ * We run the commit before garbage collection otherwise subsequent
+ * mounts will see the GC and orphan deletion in a different order.
+ */
+ dbg_rcvry("committing");
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+
+ dbg_rcvry("GC'ing LEB %d", lp.lnum);
+ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
+ err = ubifs_garbage_collect_leb(c, &lp);
+ if (err >= 0) {
+ int err2 = ubifs_wbuf_sync_nolock(wbuf);
+
+ if (err2)
+ err = err2;
+ }
+ mutex_unlock(&wbuf->io_mutex);
+ if (err < 0) {
+ ubifs_err(c, "GC failed, error %d", err);
+ if (err == -EAGAIN)
+ err = -EINVAL;
+ return err;
+ }
+
+ ubifs_assert(c, err == LEB_RETAINED);
+ if (err != LEB_RETAINED)
+ return -EINVAL;
+
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ return err;
+
+ dbg_rcvry("allocated LEB %d for GC", lp.lnum);
+ return 0;
+}
+
+/**
+ * struct size_entry - inode size information for recovery.
+ * @rb: link in the RB-tree of sizes
+ * @inum: inode number
+ * @i_size: size on inode
+ * @d_size: maximum size based on data nodes
+ * @exists: indicates whether the inode exists
+ * @inode: inode if pinned in memory awaiting rw mode to fix it
+ */
+struct size_entry {
+ struct rb_node rb;
+ ino_t inum;
+ loff_t i_size;
+ loff_t d_size;
+ int exists;
+ struct inode *inode;
+};
+
+/**
+ * add_ino - add an entry to the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ * @i_size: size on inode
+ * @d_size: maximum size based on data nodes
+ * @exists: indicates whether the inode exists
+ */
+static int add_ino(struct ubifs_info *c, ino_t inum, loff_t i_size,
+ loff_t d_size, int exists)
+{
+ struct rb_node **p = &c->size_tree.rb_node, *parent = NULL;
+ struct size_entry *e;
+
+ while (*p) {
+ parent = *p;
+ e = rb_entry(parent, struct size_entry, rb);
+ if (inum < e->inum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ e = kzalloc(sizeof(struct size_entry), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ e->inum = inum;
+ e->i_size = i_size;
+ e->d_size = d_size;
+ e->exists = exists;
+
+ rb_link_node(&e->rb, parent, p);
+ rb_insert_color(&e->rb, &c->size_tree);
+
+ return 0;
+}
+
+/**
+ * find_ino - find an entry on the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ */
+static struct size_entry *find_ino(struct ubifs_info *c, ino_t inum)
+{
+ struct rb_node *p = c->size_tree.rb_node;
+ struct size_entry *e;
+
+ while (p) {
+ e = rb_entry(p, struct size_entry, rb);
+ if (inum < e->inum)
+ p = p->rb_left;
+ else if (inum > e->inum)
+ p = p->rb_right;
+ else
+ return e;
+ }
+ return NULL;
+}
+
+/**
+ * remove_ino - remove an entry from the size tree.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ */
+static void remove_ino(struct ubifs_info *c, ino_t inum)
+{
+ struct size_entry *e = find_ino(c, inum);
+
+ if (!e)
+ return;
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+}
+
+/**
+ * ubifs_destroy_size_tree - free resources related to the size tree.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_destroy_size_tree(struct ubifs_info *c)
+{
+ struct size_entry *e, *n;
+
+ rbtree_postorder_for_each_entry_safe(e, n, &c->size_tree, rb) {
+ iput(e->inode);
+ kfree(e);
+ }
+
+ c->size_tree = RB_ROOT;
+}
+
+/**
+ * ubifs_recover_size_accum - accumulate inode sizes for recovery.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @deletion: node is for a deletion
+ * @new_size: inode size
+ *
+ * This function has two purposes:
+ * 1) to ensure there are no data nodes that fall outside the inode size
+ * 2) to ensure there are no data nodes for inodes that do not exist
+ * To accomplish those purposes, a rb-tree is constructed containing an entry
+ * for each inode number in the journal that has not been deleted, and recording
+ * the size from the inode node, the maximum size of any data node (also altered
+ * by truncations) and a flag indicating a inode number for which no inode node
+ * was present in the journal.
+ *
+ * Note that there is still the possibility that there are data nodes that have
+ * been committed that are beyond the inode size, however the only way to find
+ * them would be to scan the entire index. Alternatively, some provision could
+ * be made to record the size of inodes at the start of commit, which would seem
+ * very cumbersome for a scenario that is quite unlikely and the only negative
+ * consequence of which is wasted space.
+ *
+ * This functions returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
+ int deletion, loff_t new_size)
+{
+ ino_t inum = key_inum(c, key);
+ struct size_entry *e;
+ int err;
+
+ switch (key_type(c, key)) {
+ case UBIFS_INO_KEY:
+ if (deletion)
+ remove_ino(c, inum);
+ else {
+ e = find_ino(c, inum);
+ if (e) {
+ e->i_size = new_size;
+ e->exists = 1;
+ } else {
+ err = add_ino(c, inum, new_size, 0, 1);
+ if (err)
+ return err;
+ }
+ }
+ break;
+ case UBIFS_DATA_KEY:
+ e = find_ino(c, inum);
+ if (e) {
+ if (new_size > e->d_size)
+ e->d_size = new_size;
+ } else {
+ err = add_ino(c, inum, 0, new_size, 0);
+ if (err)
+ return err;
+ }
+ break;
+ case UBIFS_TRUN_KEY:
+ e = find_ino(c, inum);
+ if (e)
+ e->d_size = new_size;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * fix_size_in_place - fix inode size in place on flash.
+ * @c: UBIFS file-system description object
+ * @e: inode size information for recovery
+ */
+static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
+{
+ struct ubifs_ino_node *ino = c->sbuf;
+ unsigned char *p;
+ union ubifs_key key;
+ int err, lnum, offs, len;
+ loff_t i_size;
+ uint32_t crc;
+
+ /* Locate the inode node LEB number and offset */
+ ino_key_init(c, &key, e->inum);
+ err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs);
+ if (err)
+ goto out;
+ /*
+ * If the size recorded on the inode node is greater than the size that
+ * was calculated from nodes in the journal then don't change the inode.
+ */
+ i_size = le64_to_cpu(ino->size);
+ if (i_size >= e->d_size)
+ return 0;
+ /* Read the LEB */
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 1);
+ if (err)
+ goto out;
+ /* Change the size field and recalculate the CRC */
+ ino = c->sbuf + offs;
+ ino->size = cpu_to_le64(e->d_size);
+ len = le32_to_cpu(ino->ch.len);
+ crc = crc32(UBIFS_CRC32_INIT, (void *)ino + 8, len - 8);
+ ino->ch.crc = cpu_to_le32(crc);
+ /* Work out where data in the LEB ends and free space begins */
+ p = c->sbuf;
+ len = c->leb_size - 1;
+ while (p[len] == 0xff)
+ len -= 1;
+ len = ALIGN(len + 1, c->min_io_size);
+ /* Atomically write the fixed LEB back again */
+ err = ubifs_leb_change(c, lnum, c->sbuf, len);
+ if (err)
+ goto out;
+ dbg_rcvry("inode %lu at %d:%d size %lld -> %lld",
+ (unsigned long)e->inum, lnum, offs, i_size, e->d_size);
+ return 0;
+
+out:
+ ubifs_warn(c, "inode %lu failed to fix size %lld -> %lld error %d",
+ (unsigned long)e->inum, e->i_size, e->d_size, err);
+ return err;
+}
+
+/**
+ * inode_fix_size - fix inode size
+ * @c: UBIFS file-system description object
+ * @e: inode size information for recovery
+ */
+static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
+{
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ int err;
+
+ if (c->ro_mount)
+ ubifs_assert(c, !e->inode);
+
+ if (e->inode) {
+ /* Remounting rw, pick up inode we stored earlier */
+ inode = e->inode;
+ } else {
+ inode = ubifs_iget(c->vfs_sb, e->inum);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ if (inode->i_size >= e->d_size) {
+ /*
+ * The original inode in the index already has a size
+ * big enough, nothing to do
+ */
+ iput(inode);
+ return 0;
+ }
+
+ dbg_rcvry("ino %lu size %lld -> %lld",
+ (unsigned long)e->inum,
+ inode->i_size, e->d_size);
+
+ ui = ubifs_inode(inode);
+
+ inode->i_size = e->d_size;
+ ui->ui_size = e->d_size;
+ ui->synced_i_size = e->d_size;
+
+ e->inode = inode;
+ }
+
+ /*
+ * In readonly mode just keep the inode pinned in memory until we go
+ * readwrite. In readwrite mode write the inode to the journal with the
+ * fixed size.
+ */
+ if (c->ro_mount)
+ return 0;
+
+ err = ubifs_jnl_write_inode(c, inode);
+
+ iput(inode);
+
+ if (err)
+ return err;
+
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+
+ return 0;
+}
+
+/**
+ * ubifs_recover_size - recover inode size.
+ * @c: UBIFS file-system description object
+ * @in_place: If true, do a in-place size fixup
+ *
+ * This function attempts to fix inode size discrepancies identified by the
+ * 'ubifs_recover_size_accum()' function.
+ *
+ * This functions returns %0 on success and a negative error code on failure.
+ */
+int ubifs_recover_size(struct ubifs_info *c, bool in_place)
+{
+ struct rb_node *this = rb_first(&c->size_tree);
+
+ while (this) {
+ struct size_entry *e;
+ int err;
+
+ e = rb_entry(this, struct size_entry, rb);
+
+ this = rb_next(this);
+
+ if (!e->exists) {
+ union ubifs_key key;
+
+ ino_key_init(c, &key, e->inum);
+ err = ubifs_tnc_lookup(c, &key, c->sbuf);
+ if (err && err != -ENOENT)
+ return err;
+ if (err == -ENOENT) {
+ /* Remove data nodes that have no inode */
+ dbg_rcvry("removing ino %lu",
+ (unsigned long)e->inum);
+ err = ubifs_tnc_remove_ino(c, e->inum);
+ if (err)
+ return err;
+ } else {
+ struct ubifs_ino_node *ino = c->sbuf;
+
+ e->exists = 1;
+ e->i_size = le64_to_cpu(ino->size);
+ }
+ }
+
+ if (e->exists && e->i_size < e->d_size) {
+ ubifs_assert(c, !(c->ro_mount && in_place));
+
+ /*
+ * We found data that is outside the found inode size,
+ * fixup the inode size
+ */
+
+ if (in_place) {
+ err = fix_size_in_place(c, e);
+ if (err)
+ return err;
+ iput(e->inode);
+ } else {
+ err = inode_fix_size(c, e);
+ if (err)
+ return err;
+ continue;
+ }
+ }
+
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
new file mode 100644
index 00000000..c59d47fe
--- /dev/null
+++ b/ubifs-utils/libubifs/replay.c
@@ -0,0 +1,1250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains journal replay code. It runs when the file-system is being
+ * mounted and requires no locking.
+ *
+ * The larger is the journal, the longer it takes to scan it, so the longer it
+ * takes to mount UBIFS. This is why the journal has limited size which may be
+ * changed depending on the system requirements. But a larger journal gives
+ * faster I/O speed because it writes the index less frequently. So this is a
+ * trade-off. Also, the journal is indexed by the in-memory index (TNC), so the
+ * larger is the journal, the more memory its index may consume.
+ */
+
+#include "ubifs.h"
+#include <linux/list_sort.h>
+#include <crypto/hash.h>
+
+/**
+ * struct replay_entry - replay list entry.
+ * @lnum: logical eraseblock number of the node
+ * @offs: node offset
+ * @len: node length
+ * @deletion: non-zero if this entry corresponds to a node deletion
+ * @sqnum: node sequence number
+ * @list: links the replay list
+ * @key: node key
+ * @nm: directory entry name
+ * @old_size: truncation old size
+ * @new_size: truncation new size
+ *
+ * The replay process first scans all buds and builds the replay list, then
+ * sorts the replay list in nodes sequence number order, and then inserts all
+ * the replay entries to the TNC.
+ */
+struct replay_entry {
+ int lnum;
+ int offs;
+ int len;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ unsigned int deletion:1;
+ unsigned long long sqnum;
+ struct list_head list;
+ union ubifs_key key;
+ union {
+ struct fscrypt_name nm;
+ struct {
+ loff_t old_size;
+ loff_t new_size;
+ };
+ };
+};
+
+/**
+ * struct bud_entry - entry in the list of buds to replay.
+ * @list: next bud in the list
+ * @bud: bud description object
+ * @sqnum: reference node sequence number
+ * @free: free bytes in the bud
+ * @dirty: dirty bytes in the bud
+ */
+struct bud_entry {
+ struct list_head list;
+ struct ubifs_bud *bud;
+ unsigned long long sqnum;
+ int free;
+ int dirty;
+};
+
+/**
+ * set_bud_lprops - set free and dirty space used by a bud.
+ * @c: UBIFS file-system description object
+ * @b: bud entry which describes the bud
+ *
+ * This function makes sure the LEB properties of bud @b are set correctly
+ * after the replay. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
+{
+ const struct ubifs_lprops *lp;
+ int err = 0, dirty;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ dirty = lp->dirty;
+ if (b->bud->start == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
+ /*
+ * The LEB was added to the journal with a starting offset of
+ * zero which means the LEB must have been empty. The LEB
+ * property values should be @lp->free == @c->leb_size and
+ * @lp->dirty == 0, but that is not the case. The reason is that
+ * the LEB had been garbage collected before it became the bud,
+ * and there was no commit in between. The garbage collector
+ * resets the free and dirty space without recording it
+ * anywhere except lprops, so if there was no commit then
+ * lprops does not have that information.
+ *
+ * We do not need to adjust free space because the scan has told
+ * us the exact value which is recorded in the replay entry as
+ * @b->free.
+ *
+ * However we do need to subtract from the dirty space the
+ * amount of space that the garbage collector reclaimed, which
+ * is the whole LEB minus the amount of space that was free.
+ */
+ dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
+ lp->free, lp->dirty);
+ dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum,
+ lp->free, lp->dirty);
+ dirty -= c->leb_size - lp->free;
+ /*
+ * If the replay order was perfect the dirty space would now be
+ * zero. The order is not perfect because the journal heads
+ * race with each other. This is not a problem but is does mean
+ * that the dirty space may temporarily exceed c->leb_size
+ * during the replay.
+ */
+ if (dirty != 0)
+ dbg_mnt("LEB %d lp: %d free %d dirty replay: %d free %d dirty",
+ b->bud->lnum, lp->free, lp->dirty, b->free,
+ b->dirty);
+ }
+ lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ /* Make sure the journal head points to the latest bud */
+ err = ubifs_wbuf_seek_nolock(&c->jheads[b->bud->jhead].wbuf,
+ b->bud->lnum, c->leb_size - b->free);
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * set_buds_lprops - set free and dirty space for all replayed buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function sets LEB properties for all replayed buds. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+static int set_buds_lprops(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+ int err;
+
+ list_for_each_entry(b, &c->replay_buds, list) {
+ err = set_bud_lprops(c, b);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * trun_remove_range - apply a replay entry for a truncation to the TNC.
+ * @c: UBIFS file-system description object
+ * @r: replay entry of truncation
+ */
+static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)
+{
+ unsigned min_blk, max_blk;
+ union ubifs_key min_key, max_key;
+ ino_t ino;
+
+ min_blk = r->new_size / UBIFS_BLOCK_SIZE;
+ if (r->new_size & (UBIFS_BLOCK_SIZE - 1))
+ min_blk += 1;
+
+ max_blk = r->old_size / UBIFS_BLOCK_SIZE;
+ if ((r->old_size & (UBIFS_BLOCK_SIZE - 1)) == 0)
+ max_blk -= 1;
+
+ ino = key_inum(c, &r->key);
+
+ data_key_init(c, &min_key, ino, min_blk);
+ data_key_init(c, &max_key, ino, max_blk);
+
+ return ubifs_tnc_remove_range(c, &min_key, &max_key);
+}
+
+/**
+ * inode_still_linked - check whether inode in question will be re-linked.
+ * @c: UBIFS file-system description object
+ * @rino: replay entry to test
+ *
+ * O_TMPFILE files can be re-linked, this means link count goes from 0 to 1.
+ * This case needs special care, otherwise all references to the inode will
+ * be removed upon the first replay entry of an inode with link count 0
+ * is found.
+ */
+static bool inode_still_linked(struct ubifs_info *c, struct replay_entry *rino)
+{
+ struct replay_entry *r;
+
+ ubifs_assert(c, rino->deletion);
+ ubifs_assert(c, key_type(c, &rino->key) == UBIFS_INO_KEY);
+
+ /*
+ * Find the most recent entry for the inode behind @rino and check
+ * whether it is a deletion.
+ */
+ list_for_each_entry_reverse(r, &c->replay_list, list) {
+ ubifs_assert(c, r->sqnum >= rino->sqnum);
+ if (key_inum(c, &r->key) == key_inum(c, &rino->key) &&
+ key_type(c, &r->key) == UBIFS_INO_KEY)
+ return r->deletion == 0;
+
+ }
+
+ ubifs_assert(c, 0);
+ return false;
+}
+
+/**
+ * apply_replay_entry - apply a replay entry to the TNC.
+ * @c: UBIFS file-system description object
+ * @r: replay entry to apply
+ *
+ * Apply a replay entry to the TNC.
+ */
+static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
+{
+ int err;
+
+ dbg_mntk(&r->key, "LEB %d:%d len %d deletion %d sqnum %llu key ",
+ r->lnum, r->offs, r->len, r->deletion, r->sqnum);
+
+ if (is_hash_key(c, &r->key)) {
+ if (r->deletion)
+ err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
+ else
+ err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
+ r->len, r->hash, &r->nm);
+ } else {
+ if (r->deletion)
+ switch (key_type(c, &r->key)) {
+ case UBIFS_INO_KEY:
+ {
+ ino_t inum = key_inum(c, &r->key);
+
+ if (inode_still_linked(c, r)) {
+ err = 0;
+ break;
+ }
+
+ err = ubifs_tnc_remove_ino(c, inum);
+ break;
+ }
+ case UBIFS_TRUN_KEY:
+ err = trun_remove_range(c, r);
+ break;
+ default:
+ err = ubifs_tnc_remove(c, &r->key);
+ break;
+ }
+ else
+ err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
+ r->len, r->hash);
+ if (err)
+ return err;
+
+ if (c->need_recovery)
+ err = ubifs_recover_size_accum(c, &r->key, r->deletion,
+ r->new_size);
+ }
+
+ return err;
+}
+
+/**
+ * replay_entries_cmp - compare 2 replay entries.
+ * @priv: UBIFS file-system description object
+ * @a: first replay entry
+ * @b: second replay entry
+ *
+ * This is a comparios function for 'list_sort()' which compares 2 replay
+ * entries @a and @b by comparing their sequence number. Returns %1 if @a has
+ * greater sequence number and %-1 otherwise.
+ */
+static int replay_entries_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ struct ubifs_info *c = priv;
+ struct replay_entry *ra, *rb;
+
+ cond_resched();
+ if (a == b)
+ return 0;
+
+ ra = list_entry(a, struct replay_entry, list);
+ rb = list_entry(b, struct replay_entry, list);
+ ubifs_assert(c, ra->sqnum != rb->sqnum);
+ if (ra->sqnum > rb->sqnum)
+ return 1;
+ return -1;
+}
+
+/**
+ * apply_replay_list - apply the replay list to the TNC.
+ * @c: UBIFS file-system description object
+ *
+ * Apply all entries in the replay list to the TNC. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+static int apply_replay_list(struct ubifs_info *c)
+{
+ struct replay_entry *r;
+ int err;
+
+ list_sort(c, &c->replay_list, &replay_entries_cmp);
+
+ list_for_each_entry(r, &c->replay_list, list) {
+ cond_resched();
+
+ err = apply_replay_entry(c, r);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * destroy_replay_list - destroy the replay.
+ * @c: UBIFS file-system description object
+ *
+ * Destroy the replay list.
+ */
+static void destroy_replay_list(struct ubifs_info *c)
+{
+ struct replay_entry *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &c->replay_list, list) {
+ if (is_hash_key(c, &r->key))
+ kfree(fname_name(&r->nm));
+ list_del(&r->list);
+ kfree(r);
+ }
+}
+
+/**
+ * insert_node - insert a node to the replay list
+ * @c: UBIFS file-system description object
+ * @lnum: node logical eraseblock number
+ * @offs: node offset
+ * @len: node length
+ * @key: node key
+ * @sqnum: sequence number
+ * @deletion: non-zero if this is a deletion
+ * @used: number of bytes in use in a LEB
+ * @old_size: truncation old size
+ * @new_size: truncation new size
+ *
+ * This function inserts a scanned non-direntry node to the replay list. The
+ * replay list contains @struct replay_entry elements, and we sort this list in
+ * sequence number order before applying it. The replay list is applied at the
+ * very end of the replay process. Since the list is sorted in sequence number
+ * order, the older modifications are applied first. This function returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
+ const u8 *hash, union ubifs_key *key,
+ unsigned long long sqnum, int deletion, int *used,
+ loff_t old_size, loff_t new_size)
+{
+ struct replay_entry *r;
+
+ dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
+
+ if (key_inum(c, key) >= c->highest_inum)
+ c->highest_inum = key_inum(c, key);
+
+ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ if (!deletion)
+ *used += ALIGN(len, 8);
+ r->lnum = lnum;
+ r->offs = offs;
+ r->len = len;
+ ubifs_copy_hash(c, hash, r->hash);
+ r->deletion = !!deletion;
+ r->sqnum = sqnum;
+ key_copy(c, key, &r->key);
+ r->old_size = old_size;
+ r->new_size = new_size;
+
+ list_add_tail(&r->list, &c->replay_list);
+ return 0;
+}
+
+/**
+ * insert_dent - insert a directory entry node into the replay list.
+ * @c: UBIFS file-system description object
+ * @lnum: node logical eraseblock number
+ * @offs: node offset
+ * @len: node length
+ * @key: node key
+ * @name: directory entry name
+ * @nlen: directory entry name length
+ * @sqnum: sequence number
+ * @deletion: non-zero if this is a deletion
+ * @used: number of bytes in use in a LEB
+ *
+ * This function inserts a scanned directory entry node or an extended
+ * attribute entry to the replay list. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
+ const u8 *hash, union ubifs_key *key,
+ const char *name, int nlen, unsigned long long sqnum,
+ int deletion, int *used)
+{
+ struct replay_entry *r;
+ char *nbuf;
+
+ dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs);
+ if (key_inum(c, key) >= c->highest_inum)
+ c->highest_inum = key_inum(c, key);
+
+ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ nbuf = kmalloc(nlen + 1, GFP_KERNEL);
+ if (!nbuf) {
+ kfree(r);
+ return -ENOMEM;
+ }
+
+ if (!deletion)
+ *used += ALIGN(len, 8);
+ r->lnum = lnum;
+ r->offs = offs;
+ r->len = len;
+ ubifs_copy_hash(c, hash, r->hash);
+ r->deletion = !!deletion;
+ r->sqnum = sqnum;
+ key_copy(c, key, &r->key);
+ fname_len(&r->nm) = nlen;
+ memcpy(nbuf, name, nlen);
+ nbuf[nlen] = '\0';
+ fname_name(&r->nm) = nbuf;
+
+ list_add_tail(&r->list, &c->replay_list);
+ return 0;
+}
+
+/**
+ * ubifs_validate_entry - validate directory or extended attribute entry node.
+ * @c: UBIFS file-system description object
+ * @dent: the node to validate
+ *
+ * This function validates directory or extended attribute entry node @dent.
+ * Returns zero if the node is all right and a %-EINVAL if not.
+ */
+int ubifs_validate_entry(struct ubifs_info *c,
+ const struct ubifs_dent_node *dent)
+{
+ int key_type = key_type_flash(c, dent->key);
+ int nlen = le16_to_cpu(dent->nlen);
+
+ if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 ||
+ dent->type >= UBIFS_ITYPES_CNT ||
+ nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
+ (key_type == UBIFS_XENT_KEY && strnlen(dent->name, nlen) != nlen) ||
+ le64_to_cpu(dent->inum) > MAX_INUM) {
+ ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ?
+ "directory entry" : "extended attribute entry");
+ return -EINVAL;
+ }
+
+ if (key_type != UBIFS_DENT_KEY && key_type != UBIFS_XENT_KEY) {
+ ubifs_err(c, "bad key type %d", key_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * is_last_bud - check if the bud is the last in the journal head.
+ * @c: UBIFS file-system description object
+ * @bud: bud description object
+ *
+ * This function checks if bud @bud is the last bud in its journal head. This
+ * information is then used by 'replay_bud()' to decide whether the bud can
+ * have corruptions or not. Indeed, only last buds can be corrupted by power
+ * cuts. Returns %1 if this is the last bud, and %0 if not.
+ */
+static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
+{
+ struct ubifs_jhead *jh = &c->jheads[bud->jhead];
+ struct ubifs_bud *next;
+ uint32_t data;
+ int err;
+
+ if (list_is_last(&bud->list, &jh->buds_list))
+ return 1;
+
+ /*
+ * The following is a quirk to make sure we work correctly with UBIFS
+ * images used with older UBIFS.
+ *
+ * Normally, the last bud will be the last in the journal head's list
+ * of bud. However, there is one exception if the UBIFS image belongs
+ * to older UBIFS. This is fairly unlikely: one would need to use old
+ * UBIFS, then have a power cut exactly at the right point, and then
+ * try to mount this image with new UBIFS.
+ *
+ * The exception is: it is possible to have 2 buds A and B, A goes
+ * before B, and B is the last, bud B is contains no data, and bud A is
+ * corrupted at the end. The reason is that in older versions when the
+ * journal code switched the next bud (from A to B), it first added a
+ * log reference node for the new bud (B), and only after this it
+ * synchronized the write-buffer of current bud (A). But later this was
+ * changed and UBIFS started to always synchronize the write-buffer of
+ * the bud (A) before writing the log reference for the new bud (B).
+ *
+ * But because older UBIFS always synchronized A's write-buffer before
+ * writing to B, we can recognize this exceptional situation but
+ * checking the contents of bud B - if it is empty, then A can be
+ * treated as the last and we can recover it.
+ *
+ * TODO: remove this piece of code in a couple of years (today it is
+ * 16.05.2011).
+ */
+ next = list_entry(bud->list.next, struct ubifs_bud, list);
+ if (!list_is_last(&next->list, &jh->buds_list))
+ return 0;
+
+ err = ubifs_leb_read(c, next->lnum, (char *)&data, next->start, 4, 1);
+ if (err)
+ return 0;
+
+ return data == 0xFFFFFFFF;
+}
+
+/* authenticate_sleb_hash is split out for stack usage */
+static int noinline_for_stack
+authenticate_sleb_hash(struct ubifs_info *c,
+ struct shash_desc *log_hash, u8 *hash)
+{
+ SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
+
+ hash_desc->tfm = c->hash_tfm;
+
+ ubifs_shash_copy_state(c, log_hash, hash_desc);
+ return crypto_shash_final(hash_desc, hash);
+}
+
+/**
+ * authenticate_sleb - authenticate one scan LEB
+ * @c: UBIFS file-system description object
+ * @sleb: the scan LEB to authenticate
+ * @log_hash:
+ * @is_last: if true, this is the last LEB
+ *
+ * This function iterates over the buds of a single LEB authenticating all buds
+ * with the authentication nodes on this LEB. Authentication nodes are written
+ * after some buds and contain a HMAC covering the authentication node itself
+ * and the buds between the last authentication node and the current
+ * authentication node. It can happen that the last buds cannot be authenticated
+ * because a powercut happened when some nodes were written but not the
+ * corresponding authentication node. This function returns the number of nodes
+ * that could be authenticated or a negative error code.
+ */
+static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ struct shash_desc *log_hash, int is_last)
+{
+ int n_not_auth = 0;
+ struct ubifs_scan_node *snod;
+ int n_nodes = 0;
+ int err;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ u8 hmac[UBIFS_HMAC_ARR_SZ];
+
+ if (!ubifs_authenticated(c))
+ return sleb->nodes_cnt;
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+
+ n_nodes++;
+
+ if (snod->type == UBIFS_AUTH_NODE) {
+ struct ubifs_auth_node *auth = snod->node;
+
+ err = authenticate_sleb_hash(c, log_hash, hash);
+ if (err)
+ goto out;
+
+ err = crypto_shash_tfm_digest(c->hmac_tfm, hash,
+ c->hash_len, hmac);
+ if (err)
+ goto out;
+
+ err = ubifs_check_hmac(c, auth->hmac, hmac);
+ if (err) {
+ err = -EPERM;
+ goto out;
+ }
+ n_not_auth = 0;
+ } else {
+ err = crypto_shash_update(log_hash, snod->node,
+ snod->len);
+ if (err)
+ goto out;
+ n_not_auth++;
+ }
+ }
+
+ /*
+ * A powercut can happen when some nodes were written, but not yet
+ * the corresponding authentication node. This may only happen on
+ * the last bud though.
+ */
+ if (n_not_auth) {
+ if (is_last) {
+ dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them",
+ n_not_auth, sleb->lnum);
+ err = 0;
+ } else {
+ dbg_mnt("%d unauthenticated nodes found on non-last LEB %d",
+ n_not_auth, sleb->lnum);
+ err = -EPERM;
+ }
+ } else {
+ err = 0;
+ }
+out:
+ return err ? err : n_nodes - n_not_auth;
+}
+
+/**
+ * replay_bud - replay a bud logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @b: bud entry which describes the bud
+ *
+ * This function replays bud @bud, recovers it if needed, and adds all nodes
+ * from this bud to the replay list. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
+{
+ int is_last = is_last_bud(c, b->bud);
+ int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
+ int n_nodes, n = 0;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+
+ dbg_mnt("replay bud LEB %d, head %d, offs %d, is_last %d",
+ lnum, b->bud->jhead, offs, is_last);
+
+ if (c->need_recovery && is_last)
+ /*
+ * Recover only last LEBs in the journal heads, because power
+ * cuts may cause corruptions only in these LEBs, because only
+ * these LEBs could possibly be written to at the power cut
+ * time.
+ */
+ sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, b->bud->jhead);
+ else
+ sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+
+ n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last);
+ if (n_nodes < 0) {
+ err = n_nodes;
+ goto out;
+ }
+
+ ubifs_shash_copy_state(c, b->bud->log_hash,
+ c->jheads[b->bud->jhead].log_hash);
+
+ /*
+ * The bud does not have to start from offset zero - the beginning of
+ * the 'lnum' LEB may contain previously committed data. One of the
+ * things we have to do in replay is to correctly update lprops with
+ * newer information about this LEB.
+ *
+ * At this point lprops thinks that this LEB has 'c->leb_size - offs'
+ * bytes of free space because it only contain information about
+ * committed data.
+ *
+ * But we know that real amount of free space is 'c->leb_size -
+ * sleb->endpt', and the space in the 'lnum' LEB between 'offs' and
+ * 'sleb->endpt' is used by bud data. We have to correctly calculate
+ * how much of these data are dirty and update lprops with this
+ * information.
+ *
+ * The dirt in that LEB region is comprised of padding nodes, deletion
+ * nodes, truncation nodes and nodes which are obsoleted by subsequent
+ * nodes in this LEB. So instead of calculating clean space, we
+ * calculate used space ('used' variable).
+ */
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ int deletion = 0;
+
+ cond_resched();
+
+ if (snod->sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "file system's life ended");
+ goto out_dump;
+ }
+
+ ubifs_node_calc_hash(c, snod->node, hash);
+
+ if (snod->sqnum > c->max_sqnum)
+ c->max_sqnum = snod->sqnum;
+
+ switch (snod->type) {
+ case UBIFS_INO_NODE:
+ {
+ struct ubifs_ino_node *ino = snod->node;
+ loff_t new_size = le64_to_cpu(ino->size);
+
+ if (le32_to_cpu(ino->nlink) == 0)
+ deletion = 1;
+ err = insert_node(c, lnum, snod->offs, snod->len, hash,
+ &snod->key, snod->sqnum, deletion,
+ &used, 0, new_size);
+ break;
+ }
+ case UBIFS_DATA_NODE:
+ {
+ struct ubifs_data_node *dn = snod->node;
+ loff_t new_size = le32_to_cpu(dn->size) +
+ key_block(c, &snod->key) *
+ UBIFS_BLOCK_SIZE;
+
+ err = insert_node(c, lnum, snod->offs, snod->len, hash,
+ &snod->key, snod->sqnum, deletion,
+ &used, 0, new_size);
+ break;
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ struct ubifs_dent_node *dent = snod->node;
+
+ err = ubifs_validate_entry(c, dent);
+ if (err)
+ goto out_dump;
+
+ err = insert_dent(c, lnum, snod->offs, snod->len, hash,
+ &snod->key, dent->name,
+ le16_to_cpu(dent->nlen), snod->sqnum,
+ !le64_to_cpu(dent->inum), &used);
+ break;
+ }
+ case UBIFS_TRUN_NODE:
+ {
+ struct ubifs_trun_node *trun = snod->node;
+ loff_t old_size = le64_to_cpu(trun->old_size);
+ loff_t new_size = le64_to_cpu(trun->new_size);
+ union ubifs_key key;
+
+ /* Validate truncation node */
+ if (old_size < 0 || old_size > c->max_inode_sz ||
+ new_size < 0 || new_size > c->max_inode_sz ||
+ old_size <= new_size) {
+ ubifs_err(c, "bad truncation node");
+ goto out_dump;
+ }
+
+ /*
+ * Create a fake truncation key just to use the same
+ * functions which expect nodes to have keys.
+ */
+ trun_key_init(c, &key, le32_to_cpu(trun->inum));
+ err = insert_node(c, lnum, snod->offs, snod->len, hash,
+ &key, snod->sqnum, 1, &used,
+ old_size, new_size);
+ break;
+ }
+ case UBIFS_AUTH_NODE:
+ break;
+ default:
+ ubifs_err(c, "unexpected node type %d in bud LEB %d:%d",
+ snod->type, lnum, snod->offs);
+ err = -EINVAL;
+ goto out_dump;
+ }
+ if (err)
+ goto out;
+
+ n++;
+ if (n == n_nodes)
+ break;
+ }
+
+ ubifs_assert(c, ubifs_search_bud(c, lnum));
+ ubifs_assert(c, sleb->endpt - offs >= used);
+ ubifs_assert(c, sleb->endpt % c->min_io_size == 0);
+
+ b->dirty = sleb->endpt - offs - used;
+ b->free = c->leb_size - sleb->endpt;
+ dbg_mnt("bud LEB %d replied: dirty %d, free %d",
+ lnum, b->dirty, b->free);
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_dump:
+ ubifs_err(c, "bad node is at LEB %d:%d", lnum, snod->offs);
+ ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * replay_buds - replay all buds.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int replay_buds(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+ int err;
+ unsigned long long prev_sqnum = 0;
+
+ list_for_each_entry(b, &c->replay_buds, list) {
+ err = replay_bud(c, b);
+ if (err)
+ return err;
+
+ ubifs_assert(c, b->sqnum > prev_sqnum);
+ prev_sqnum = b->sqnum;
+ }
+
+ return 0;
+}
+
+/**
+ * destroy_bud_list - destroy the list of buds to replay.
+ * @c: UBIFS file-system description object
+ */
+static void destroy_bud_list(struct ubifs_info *c)
+{
+ struct bud_entry *b;
+
+ while (!list_empty(&c->replay_buds)) {
+ b = list_entry(c->replay_buds.next, struct bud_entry, list);
+ list_del(&b->list);
+ kfree(b);
+ }
+}
+
+/**
+ * add_replay_bud - add a bud to the list of buds to replay.
+ * @c: UBIFS file-system description object
+ * @lnum: bud logical eraseblock number to replay
+ * @offs: bud start offset
+ * @jhead: journal head to which this bud belongs
+ * @sqnum: reference node sequence number
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
+ unsigned long long sqnum)
+{
+ struct ubifs_bud *bud;
+ struct bud_entry *b;
+ int err;
+
+ dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead);
+
+ bud = kmalloc(sizeof(struct ubifs_bud), GFP_KERNEL);
+ if (!bud)
+ return -ENOMEM;
+
+ b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL);
+ if (!b) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ bud->lnum = lnum;
+ bud->start = offs;
+ bud->jhead = jhead;
+ bud->log_hash = ubifs_hash_get_desc(c);
+ if (IS_ERR(bud->log_hash)) {
+ err = PTR_ERR(bud->log_hash);
+ goto out;
+ }
+
+ ubifs_shash_copy_state(c, c->log_hash, bud->log_hash);
+
+ ubifs_add_bud(c, bud);
+
+ b->bud = bud;
+ b->sqnum = sqnum;
+ list_add_tail(&b->list, &c->replay_buds);
+
+ return 0;
+out:
+ kfree(bud);
+ kfree(b);
+
+ return err;
+}
+
+/**
+ * validate_ref - validate a reference node.
+ * @c: UBIFS file-system description object
+ * @ref: the reference node to validate
+ *
+ * This function returns %1 if a bud reference already exists for the LEB. %0 is
+ * returned if the reference node is new, otherwise %-EINVAL is returned if
+ * validation failed.
+ */
+static int validate_ref(struct ubifs_info *c, const struct ubifs_ref_node *ref)
+{
+ struct ubifs_bud *bud;
+ int lnum = le32_to_cpu(ref->lnum);
+ unsigned int offs = le32_to_cpu(ref->offs);
+ unsigned int jhead = le32_to_cpu(ref->jhead);
+
+ /*
+ * ref->offs may point to the end of LEB when the journal head points
+ * to the end of LEB and we write reference node for it during commit.
+ * So this is why we require 'offs > c->leb_size'.
+ */
+ if (jhead >= c->jhead_cnt || lnum >= c->leb_cnt ||
+ lnum < c->main_first || offs > c->leb_size ||
+ offs & (c->min_io_size - 1))
+ return -EINVAL;
+
+ /* Make sure we have not already looked at this bud */
+ bud = ubifs_search_bud(c, lnum);
+ if (bud) {
+ if (bud->jhead == jhead && bud->start <= offs)
+ return 1;
+ ubifs_err(c, "bud at LEB %d:%d was already referred", lnum, offs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * replay_log_leb - replay a log logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lnum: log logical eraseblock to replay
+ * @offs: offset to start replaying from
+ * @sbuf: scan buffer
+ *
+ * This function replays a log LEB and returns zero in case of success, %1 if
+ * this is the last LEB in the log, and a negative error code in case of
+ * failure.
+ */
+static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
+{
+ int err;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ const struct ubifs_cs_node *node;
+
+ dbg_mnt("replay log LEB %d:%d", lnum, offs);
+ sleb = ubifs_scan(c, lnum, offs, sbuf, c->need_recovery);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery)
+ return PTR_ERR(sleb);
+ /*
+ * Note, the below function will recover this log LEB only if
+ * it is the last, because unclean reboots can possibly corrupt
+ * only the tail of the log.
+ */
+ sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ }
+
+ if (sleb->nodes_cnt == 0) {
+ err = 1;
+ goto out;
+ }
+
+ node = sleb->buf;
+ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+ if (c->cs_sqnum == 0) {
+ /*
+ * This is the first log LEB we are looking at, make sure that
+ * the first node is a commit start node. Also record its
+ * sequence number so that UBIFS can determine where the log
+ * ends, because all nodes which were have higher sequence
+ * numbers.
+ */
+ if (snod->type != UBIFS_CS_NODE) {
+ ubifs_err(c, "first log node at LEB %d:%d is not CS node",
+ lnum, offs);
+ goto out_dump;
+ }
+ if (le64_to_cpu(node->cmt_no) != c->cmt_no) {
+ ubifs_err(c, "first CS node at LEB %d:%d has wrong commit number %llu expected %llu",
+ lnum, offs,
+ (unsigned long long)le64_to_cpu(node->cmt_no),
+ c->cmt_no);
+ goto out_dump;
+ }
+
+ c->cs_sqnum = le64_to_cpu(node->ch.sqnum);
+ dbg_mnt("commit start sqnum %llu", c->cs_sqnum);
+
+ err = ubifs_shash_init(c, c->log_hash);
+ if (err)
+ goto out;
+
+ err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ);
+ if (err < 0)
+ goto out;
+ }
+
+ if (snod->sqnum < c->cs_sqnum) {
+ /*
+ * This means that we reached end of log and now
+ * look to the older log data, which was already
+ * committed but the eraseblock was not erased (UBIFS
+ * only un-maps it). So this basically means we have to
+ * exit with "end of log" code.
+ */
+ err = 1;
+ goto out;
+ }
+
+ /* Make sure the first node sits at offset zero of the LEB */
+ if (snod->offs != 0) {
+ ubifs_err(c, "first node is not at zero offset");
+ goto out_dump;
+ }
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ cond_resched();
+
+ if (snod->sqnum >= SQNUM_WATERMARK) {
+ ubifs_err(c, "file system's life ended");
+ goto out_dump;
+ }
+
+ if (snod->sqnum < c->cs_sqnum) {
+ ubifs_err(c, "bad sqnum %llu, commit sqnum %llu",
+ snod->sqnum, c->cs_sqnum);
+ goto out_dump;
+ }
+
+ if (snod->sqnum > c->max_sqnum)
+ c->max_sqnum = snod->sqnum;
+
+ switch (snod->type) {
+ case UBIFS_REF_NODE: {
+ const struct ubifs_ref_node *ref = snod->node;
+
+ err = validate_ref(c, ref);
+ if (err == 1)
+ break; /* Already have this bud */
+ if (err)
+ goto out_dump;
+
+ err = ubifs_shash_update(c, c->log_hash, ref,
+ UBIFS_REF_NODE_SZ);
+ if (err)
+ goto out;
+
+ err = add_replay_bud(c, le32_to_cpu(ref->lnum),
+ le32_to_cpu(ref->offs),
+ le32_to_cpu(ref->jhead),
+ snod->sqnum);
+ if (err)
+ goto out;
+
+ break;
+ }
+ case UBIFS_CS_NODE:
+ /* Make sure it sits at the beginning of LEB */
+ if (snod->offs != 0) {
+ ubifs_err(c, "unexpected node in log");
+ goto out_dump;
+ }
+ break;
+ default:
+ ubifs_err(c, "unexpected node in log");
+ goto out_dump;
+ }
+ }
+
+ if (sleb->endpt || c->lhead_offs >= c->leb_size) {
+ c->lhead_lnum = lnum;
+ c->lhead_offs = sleb->endpt;
+ }
+
+ err = !sleb->endpt;
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+
+out_dump:
+ ubifs_err(c, "log error detected while replaying the log at LEB %d:%d",
+ lnum, offs + snod->offs);
+ ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
+ ubifs_scan_destroy(sleb);
+ return -EINVAL;
+}
+
+/**
+ * take_ihead - update the status of the index head in lprops to 'taken'.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the amount of free space in the index head LEB or a
+ * negative error code.
+ */
+static int take_ihead(struct ubifs_info *c)
+{
+ const struct ubifs_lprops *lp;
+ int err, free;
+
+ ubifs_get_lprops(c);
+
+ lp = ubifs_lpt_lookup_dirty(c, c->ihead_lnum);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ free = lp->free;
+
+ lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+ lp->flags | LPROPS_TAKEN, 0);
+ if (IS_ERR(lp)) {
+ err = PTR_ERR(lp);
+ goto out;
+ }
+
+ err = free;
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_replay_journal - replay journal.
+ * @c: UBIFS file-system description object
+ *
+ * This function scans the journal, replays and cleans it up. It makes sure all
+ * memory data structures related to uncommitted journal are built (dirty TNC
+ * tree, tree of buds, modified lprops, etc).
+ */
+int ubifs_replay_journal(struct ubifs_info *c)
+{
+ int err, lnum, free;
+
+ BUILD_BUG_ON(UBIFS_TRUN_KEY > 5);
+
+ /* Update the status of the index head in lprops to 'taken' */
+ free = take_ihead(c);
+ if (free < 0)
+ return free; /* Error code */
+
+ if (c->ihead_offs != c->leb_size - free) {
+ ubifs_err(c, "bad index head LEB %d:%d", c->ihead_lnum,
+ c->ihead_offs);
+ return -EINVAL;
+ }
+
+ dbg_mnt("start replaying the journal");
+ c->replaying = 1;
+ lnum = c->ltail_lnum = c->lhead_lnum;
+
+ do {
+ err = replay_log_leb(c, lnum, 0, c->sbuf);
+ if (err == 1) {
+ if (lnum != c->lhead_lnum)
+ /* We hit the end of the log */
+ break;
+
+ /*
+ * The head of the log must always start with the
+ * "commit start" node on a properly formatted UBIFS.
+ * But we found no nodes at all, which means that
+ * something went wrong and we cannot proceed mounting
+ * the file-system.
+ */
+ ubifs_err(c, "no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted",
+ lnum, 0);
+ err = -EINVAL;
+ }
+ if (err)
+ goto out;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ } while (lnum != c->ltail_lnum);
+
+ err = replay_buds(c);
+ if (err)
+ goto out;
+
+ err = apply_replay_list(c);
+ if (err)
+ goto out;
+
+ err = set_buds_lprops(c);
+ if (err)
+ goto out;
+
+ /*
+ * UBIFS budgeting calculations use @c->bi.uncommitted_idx variable
+ * to roughly estimate index growth. Things like @c->bi.min_idx_lebs
+ * depend on it. This means we have to initialize it to make sure
+ * budgeting works properly.
+ */
+ c->bi.uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt);
+ c->bi.uncommitted_idx *= c->max_idx_node_sz;
+
+ ubifs_assert(c, c->bud_bytes <= c->max_bud_bytes || c->need_recovery);
+ dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, highest_inum %lu",
+ c->lhead_lnum, c->lhead_offs, c->max_sqnum,
+ (unsigned long)c->highest_inum);
+out:
+ destroy_replay_list(c);
+ destroy_bud_list(c);
+ c->replaying = 0;
+ return err;
+}
diff --git a/ubifs-utils/libubifs/sb.c b/ubifs-utils/libubifs/sb.c
new file mode 100644
index 00000000..e7693b94
--- /dev/null
+++ b/ubifs-utils/libubifs/sb.c
@@ -0,0 +1,956 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements UBIFS superblock. The superblock is stored at the first
+ * LEB of the volume and is never changed by UBIFS. Only user-space tools may
+ * change it. The superblock node mostly contains geometry information.
+ */
+
+#include "ubifs.h"
+#include <linux/slab.h>
+#include <linux/math64.h>
+#include <linux/uuid.h>
+
+/*
+ * Default journal size in logical eraseblocks as a percent of total
+ * flash size.
+ */
+#define DEFAULT_JNL_PERCENT 5
+
+/* Default maximum journal size in bytes */
+#define DEFAULT_MAX_JNL (32*1024*1024)
+
+/* Default indexing tree fanout */
+#define DEFAULT_FANOUT 8
+
+/* Default number of data journal heads */
+#define DEFAULT_JHEADS_CNT 1
+
+/* Default positions of different LEBs in the main area */
+#define DEFAULT_IDX_LEB 0
+#define DEFAULT_DATA_LEB 1
+#define DEFAULT_GC_LEB 2
+
+/* Default number of LEB numbers in LPT's save table */
+#define DEFAULT_LSAVE_CNT 256
+
+/* Default reserved pool size as a percent of maximum free space */
+#define DEFAULT_RP_PERCENT 5
+
+/* The default maximum size of reserved pool in bytes */
+#define DEFAULT_MAX_RP_SIZE (5*1024*1024)
+
+/* Default time granularity in nanoseconds */
+#define DEFAULT_TIME_GRAN 1000000000
+
+static int get_default_compressor(struct ubifs_info *c)
+{
+ if (ubifs_compr_present(c, UBIFS_COMPR_ZSTD))
+ return UBIFS_COMPR_ZSTD;
+
+ if (ubifs_compr_present(c, UBIFS_COMPR_LZO))
+ return UBIFS_COMPR_LZO;
+
+ if (ubifs_compr_present(c, UBIFS_COMPR_ZLIB))
+ return UBIFS_COMPR_ZLIB;
+
+ return UBIFS_COMPR_NONE;
+}
+
+/**
+ * create_default_filesystem - format empty UBI volume.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates default empty file-system. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+static int create_default_filesystem(struct ubifs_info *c)
+{
+ struct ubifs_sb_node *sup;
+ struct ubifs_mst_node *mst;
+ struct ubifs_idx_node *idx;
+ struct ubifs_branch *br;
+ struct ubifs_ino_node *ino;
+ struct ubifs_cs_node *cs;
+ union ubifs_key key;
+ int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
+ int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
+ int min_leb_cnt = UBIFS_MIN_LEB_CNT;
+ int idx_node_size;
+ long long tmp64, main_bytes;
+ __le64 tmp_le64;
+ struct timespec64 ts;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ u8 hash_lpt[UBIFS_HASH_ARR_SZ];
+
+ /* Some functions called from here depend on the @c->key_len filed */
+ c->key_len = UBIFS_SK_LEN;
+
+ /*
+ * First of all, we have to calculate default file-system geometry -
+ * log size, journal size, etc.
+ */
+ if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT)
+ /* We can first multiply then divide and have no overflow */
+ jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100;
+ else
+ jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT;
+
+ if (jnl_lebs < UBIFS_MIN_JNL_LEBS)
+ jnl_lebs = UBIFS_MIN_JNL_LEBS;
+ if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL)
+ jnl_lebs = DEFAULT_MAX_JNL / c->leb_size;
+
+ /*
+ * The log should be large enough to fit reference nodes for all bud
+ * LEBs. Because buds do not have to start from the beginning of LEBs
+ * (half of the LEB may contain committed data), the log should
+ * generally be larger, make it twice as large.
+ */
+ tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1;
+ log_lebs = tmp / c->leb_size;
+ /* Plus one LEB reserved for commit */
+ log_lebs += 1;
+ if (c->leb_cnt - min_leb_cnt > 8) {
+ /* And some extra space to allow writes while committing */
+ log_lebs += 1;
+ min_leb_cnt += 1;
+ }
+
+ max_buds = jnl_lebs - log_lebs;
+ if (max_buds < UBIFS_MIN_BUD_LEBS)
+ max_buds = UBIFS_MIN_BUD_LEBS;
+
+ /*
+ * Orphan nodes are stored in a separate area. One node can store a lot
+ * of orphan inode numbers, but when new orphan comes we just add a new
+ * orphan node. At some point the nodes are consolidated into one
+ * orphan node.
+ */
+ orph_lebs = UBIFS_MIN_ORPH_LEBS;
+ if (c->leb_cnt - min_leb_cnt > 1)
+ /*
+ * For debugging purposes it is better to have at least 2
+ * orphan LEBs, because the orphan subsystem would need to do
+ * consolidations and would be stressed more.
+ */
+ orph_lebs += 1;
+
+ main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs;
+ main_lebs -= orph_lebs;
+
+ lpt_first = UBIFS_LOG_LNUM + log_lebs;
+ c->lsave_cnt = DEFAULT_LSAVE_CNT;
+ c->max_leb_cnt = c->leb_cnt;
+ err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
+ &big_lpt, hash_lpt);
+ if (err)
+ return err;
+
+ dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first,
+ lpt_first + lpt_lebs - 1);
+
+ main_first = c->leb_cnt - main_lebs;
+
+ sup = kzalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_KERNEL);
+ mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+ idx_node_size = ubifs_idx_node_sz(c, 1);
+ idx = kzalloc(ALIGN(idx_node_size, c->min_io_size), GFP_KERNEL);
+ ino = kzalloc(ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size), GFP_KERNEL);
+ cs = kzalloc(ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size), GFP_KERNEL);
+
+ if (!sup || !mst || !idx || !ino || !cs) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Create default superblock */
+
+ tmp64 = (long long)max_buds * c->leb_size;
+ if (big_lpt)
+ sup_flags |= UBIFS_FLG_BIGLPT;
+ if (ubifs_default_version > 4)
+ sup_flags |= UBIFS_FLG_DOUBLE_HASH;
+
+ if (ubifs_authenticated(c)) {
+ sup_flags |= UBIFS_FLG_AUTHENTICATION;
+ sup->hash_algo = cpu_to_le16(c->auth_hash_algo);
+ err = ubifs_hmac_wkm(c, sup->hmac_wkm);
+ if (err)
+ goto out;
+ } else {
+ sup->hash_algo = cpu_to_le16(0xffff);
+ }
+
+ sup->ch.node_type = UBIFS_SB_NODE;
+ sup->key_hash = UBIFS_KEY_HASH_R5;
+ sup->flags = cpu_to_le32(sup_flags);
+ sup->min_io_size = cpu_to_le32(c->min_io_size);
+ sup->leb_size = cpu_to_le32(c->leb_size);
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+ sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt);
+ sup->max_bud_bytes = cpu_to_le64(tmp64);
+ sup->log_lebs = cpu_to_le32(log_lebs);
+ sup->lpt_lebs = cpu_to_le32(lpt_lebs);
+ sup->orph_lebs = cpu_to_le32(orph_lebs);
+ sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT);
+ sup->fanout = cpu_to_le32(DEFAULT_FANOUT);
+ sup->lsave_cnt = cpu_to_le32(c->lsave_cnt);
+ sup->fmt_version = cpu_to_le32(ubifs_default_version);
+ sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN);
+ if (c->mount_opts.override_compr)
+ sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
+ else
+ sup->default_compr = cpu_to_le16(get_default_compressor(c));
+
+ generate_random_uuid(sup->uuid);
+
+ main_bytes = (long long)main_lebs * c->leb_size;
+ tmp64 = div_u64(main_bytes * DEFAULT_RP_PERCENT, 100);
+ if (tmp64 > DEFAULT_MAX_RP_SIZE)
+ tmp64 = DEFAULT_MAX_RP_SIZE;
+ sup->rp_size = cpu_to_le64(tmp64);
+ sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
+
+ dbg_gen("default superblock created at LEB 0:0");
+
+ /* Create default master node */
+
+ mst->ch.node_type = UBIFS_MST_NODE;
+ mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
+ mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO);
+ mst->cmt_no = 0;
+ mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
+ mst->root_offs = 0;
+ tmp = ubifs_idx_node_sz(c, 1);
+ mst->root_len = cpu_to_le32(tmp);
+ mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB);
+ mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB);
+ mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size));
+ mst->index_size = cpu_to_le64(ALIGN(tmp, 8));
+ mst->lpt_lnum = cpu_to_le32(c->lpt_lnum);
+ mst->lpt_offs = cpu_to_le32(c->lpt_offs);
+ mst->nhead_lnum = cpu_to_le32(c->nhead_lnum);
+ mst->nhead_offs = cpu_to_le32(c->nhead_offs);
+ mst->ltab_lnum = cpu_to_le32(c->ltab_lnum);
+ mst->ltab_offs = cpu_to_le32(c->ltab_offs);
+ mst->lsave_lnum = cpu_to_le32(c->lsave_lnum);
+ mst->lsave_offs = cpu_to_le32(c->lsave_offs);
+ mst->lscan_lnum = cpu_to_le32(main_first);
+ mst->empty_lebs = cpu_to_le32(main_lebs - 2);
+ mst->idx_lebs = cpu_to_le32(1);
+ mst->leb_cnt = cpu_to_le32(c->leb_cnt);
+ ubifs_copy_hash(c, hash_lpt, mst->hash_lpt);
+
+ /* Calculate lprops statistics */
+ tmp64 = main_bytes;
+ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
+ tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
+ mst->total_free = cpu_to_le64(tmp64);
+
+ tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size);
+ ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) -
+ UBIFS_INO_NODE_SZ;
+ tmp64 += ino_waste;
+ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8);
+ mst->total_dirty = cpu_to_le64(tmp64);
+
+ /* The indexing LEB does not contribute to dark space */
+ tmp64 = ((long long)(c->main_lebs - 1) * c->dark_wm);
+ mst->total_dark = cpu_to_le64(tmp64);
+
+ mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
+
+ dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
+
+ /* Create the root indexing node */
+
+ c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
+ c->key_hash = key_r5_hash;
+
+ idx->ch.node_type = UBIFS_IDX_NODE;
+ idx->child_cnt = cpu_to_le16(1);
+ ino_key_init(c, &key, UBIFS_ROOT_INO);
+ br = ubifs_idx_branch(c, idx, 0);
+ key_write_idx(c, &key, &br->key);
+ br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
+ br->len = cpu_to_le32(UBIFS_INO_NODE_SZ);
+
+ dbg_gen("default root indexing node created LEB %d:0",
+ main_first + DEFAULT_IDX_LEB);
+
+ /* Create default root inode */
+
+ ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
+ ino->ch.node_type = UBIFS_INO_NODE;
+ ino->creat_sqnum = cpu_to_le64(++c->max_sqnum);
+ ino->nlink = cpu_to_le32(2);
+
+ ktime_get_coarse_real_ts64(&ts);
+ tmp_le64 = cpu_to_le64(ts.tv_sec);
+ ino->atime_sec = tmp_le64;
+ ino->ctime_sec = tmp_le64;
+ ino->mtime_sec = tmp_le64;
+ ino->atime_nsec = 0;
+ ino->ctime_nsec = 0;
+ ino->mtime_nsec = 0;
+ ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
+ ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ);
+
+ /* Set compression enabled by default */
+ ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
+
+ dbg_gen("root inode created at LEB %d:0",
+ main_first + DEFAULT_DATA_LEB);
+
+ /*
+ * The first node in the log has to be the commit start node. This is
+ * always the case during normal file-system operation. Write a fake
+ * commit start node to the log.
+ */
+
+ cs->ch.node_type = UBIFS_CS_NODE;
+
+ err = ubifs_write_node_hmac(c, sup, UBIFS_SB_NODE_SZ, 0, 0,
+ offsetof(struct ubifs_sb_node, hmac));
+ if (err)
+ goto out;
+
+ err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
+ main_first + DEFAULT_DATA_LEB, 0);
+ if (err)
+ goto out;
+
+ ubifs_node_calc_hash(c, ino, hash);
+ ubifs_copy_hash(c, hash, ubifs_branch_hash(c, br));
+
+ err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
+ if (err)
+ goto out;
+
+ ubifs_node_calc_hash(c, idx, hash);
+ ubifs_copy_hash(c, hash, mst->hash_root_idx);
+
+ err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0,
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ goto out;
+
+ err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
+ 0, offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ goto out;
+
+ err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
+ if (err)
+ goto out;
+
+ ubifs_msg(c, "default file-system created");
+
+ err = 0;
+out:
+ kfree(sup);
+ kfree(mst);
+ kfree(idx);
+ kfree(ino);
+ kfree(cs);
+
+ return err;
+}
+
+/**
+ * validate_sb - validate superblock node.
+ * @c: UBIFS file-system description object
+ * @sup: superblock node
+ *
+ * This function validates superblock node @sup. Since most of data was read
+ * from the superblock and stored in @c, the function validates fields in @c
+ * instead. Returns zero in case of success and %-EINVAL in case of validation
+ * failure.
+ */
+static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
+{
+ long long max_bytes;
+ int err = 1, min_leb_cnt;
+
+ if (!c->key_hash) {
+ err = 2;
+ goto failed;
+ }
+
+ if (sup->key_fmt != UBIFS_SIMPLE_KEY_FMT) {
+ err = 3;
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->min_io_size) != c->min_io_size) {
+ ubifs_err(c, "min. I/O unit mismatch: %d in superblock, %d real",
+ le32_to_cpu(sup->min_io_size), c->min_io_size);
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->leb_size) != c->leb_size) {
+ ubifs_err(c, "LEB size mismatch: %d in superblock, %d real",
+ le32_to_cpu(sup->leb_size), c->leb_size);
+ goto failed;
+ }
+
+ if (c->log_lebs < UBIFS_MIN_LOG_LEBS ||
+ c->lpt_lebs < UBIFS_MIN_LPT_LEBS ||
+ c->orph_lebs < UBIFS_MIN_ORPH_LEBS ||
+ c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
+ err = 4;
+ goto failed;
+ }
+
+ /*
+ * Calculate minimum allowed amount of main area LEBs. This is very
+ * similar to %UBIFS_MIN_LEB_CNT, but we take into account real what we
+ * have just read from the superblock.
+ */
+ min_leb_cnt = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs;
+ min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;
+
+ if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) {
+ ubifs_err(c, "bad LEB count: %d in superblock, %d on UBI volume, %d minimum required",
+ c->leb_cnt, c->vi.size, min_leb_cnt);
+ goto failed;
+ }
+
+ if (c->max_leb_cnt < c->leb_cnt) {
+ ubifs_err(c, "max. LEB count %d less than LEB count %d",
+ c->max_leb_cnt, c->leb_cnt);
+ goto failed;
+ }
+
+ if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
+ ubifs_err(c, "too few main LEBs count %d, must be at least %d",
+ c->main_lebs, UBIFS_MIN_MAIN_LEBS);
+ goto failed;
+ }
+
+ max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS;
+ if (c->max_bud_bytes < max_bytes) {
+ ubifs_err(c, "too small journal (%lld bytes), must be at least %lld bytes",
+ c->max_bud_bytes, max_bytes);
+ goto failed;
+ }
+
+ max_bytes = (long long)c->leb_size * c->main_lebs;
+ if (c->max_bud_bytes > max_bytes) {
+ ubifs_err(c, "too large journal size (%lld bytes), only %lld bytes available in the main area",
+ c->max_bud_bytes, max_bytes);
+ goto failed;
+ }
+
+ if (c->jhead_cnt < NONDATA_JHEADS_CNT + 1 ||
+ c->jhead_cnt > NONDATA_JHEADS_CNT + UBIFS_MAX_JHEADS) {
+ err = 9;
+ goto failed;
+ }
+
+ if (c->fanout < UBIFS_MIN_FANOUT ||
+ ubifs_idx_node_sz(c, c->fanout) > c->leb_size) {
+ err = 10;
+ goto failed;
+ }
+
+ if (c->lsave_cnt < 0 || (c->lsave_cnt > DEFAULT_LSAVE_CNT &&
+ c->lsave_cnt > c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS -
+ c->log_lebs - c->lpt_lebs - c->orph_lebs)) {
+ err = 11;
+ goto failed;
+ }
+
+ if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs +
+ c->orph_lebs + c->main_lebs != c->leb_cnt) {
+ err = 12;
+ goto failed;
+ }
+
+ if (c->default_compr >= UBIFS_COMPR_TYPES_CNT) {
+ err = 13;
+ goto failed;
+ }
+
+ if (c->rp_size < 0 || max_bytes < c->rp_size) {
+ err = 14;
+ goto failed;
+ }
+
+ if (le32_to_cpu(sup->time_gran) > 1000000000 ||
+ le32_to_cpu(sup->time_gran) < 1) {
+ err = 15;
+ goto failed;
+ }
+
+ if (!c->double_hash && c->fmt_version >= 5) {
+ err = 16;
+ goto failed;
+ }
+
+ if (c->encrypted && c->fmt_version < 5) {
+ err = 17;
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ ubifs_err(c, "bad superblock, error %d", err);
+ ubifs_dump_node(c, sup, ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size));
+ return -EINVAL;
+}
+
+/**
+ * ubifs_read_sb_node - read superblock node.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns a pointer to the superblock node or a negative error
+ * code. Note, the user of this function is responsible of kfree()'ing the
+ * returned superblock buffer.
+ */
+static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
+{
+ struct ubifs_sb_node *sup;
+ int err;
+
+ sup = kmalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_NOFS);
+ if (!sup)
+ return ERR_PTR(-ENOMEM);
+
+ err = ubifs_read_node(c, sup, UBIFS_SB_NODE, UBIFS_SB_NODE_SZ,
+ UBIFS_SB_LNUM, 0);
+ if (err) {
+ kfree(sup);
+ return ERR_PTR(err);
+ }
+
+ return sup;
+}
+
+static int authenticate_sb_node(struct ubifs_info *c,
+ const struct ubifs_sb_node *sup)
+{
+ unsigned int sup_flags = le32_to_cpu(sup->flags);
+ u8 hmac_wkm[UBIFS_HMAC_ARR_SZ];
+ int authenticated = !!(sup_flags & UBIFS_FLG_AUTHENTICATION);
+ int hash_algo;
+ int err;
+
+ if (c->authenticated && !authenticated) {
+ ubifs_err(c, "authenticated FS forced, but found FS without authentication");
+ return -EINVAL;
+ }
+
+ if (!c->authenticated && authenticated) {
+ ubifs_err(c, "authenticated FS found, but no key given");
+ return -EINVAL;
+ }
+
+ ubifs_msg(c, "Mounting in %sauthenticated mode",
+ c->authenticated ? "" : "un");
+
+ if (!c->authenticated)
+ return 0;
+
+ if (!IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION))
+ return -EOPNOTSUPP;
+
+ hash_algo = le16_to_cpu(sup->hash_algo);
+ if (hash_algo >= HASH_ALGO__LAST) {
+ ubifs_err(c, "superblock uses unknown hash algo %d",
+ hash_algo);
+ return -EINVAL;
+ }
+
+ if (strcmp(hash_algo_name[hash_algo], c->auth_hash_name)) {
+ ubifs_err(c, "This filesystem uses %s for hashing,"
+ " but %s is specified", hash_algo_name[hash_algo],
+ c->auth_hash_name);
+ return -EINVAL;
+ }
+
+ /*
+ * The super block node can either be authenticated by a HMAC or
+ * by a signature in a ubifs_sig_node directly following the
+ * super block node to support offline image creation.
+ */
+ if (ubifs_hmac_zero(c, sup->hmac)) {
+ err = ubifs_sb_verify_signature(c, sup);
+ } else {
+ err = ubifs_hmac_wkm(c, hmac_wkm);
+ if (err)
+ return err;
+ if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
+ ubifs_err(c, "provided key does not fit");
+ return -ENOKEY;
+ }
+ err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
+ offsetof(struct ubifs_sb_node,
+ hmac));
+ }
+
+ if (err)
+ ubifs_err(c, "Failed to authenticate superblock: %d", err);
+
+ return err;
+}
+
+/**
+ * ubifs_write_sb_node - write superblock node.
+ * @c: UBIFS file-system description object
+ * @sup: superblock node read with 'ubifs_read_sb_node()'
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
+{
+ int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
+ int err;
+
+ err = ubifs_prepare_node_hmac(c, sup, UBIFS_SB_NODE_SZ,
+ offsetof(struct ubifs_sb_node, hmac), 1);
+ if (err)
+ return err;
+
+ return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
+}
+
+/**
+ * ubifs_read_superblock - read superblock.
+ * @c: UBIFS file-system description object
+ *
+ * This function finds, reads and checks the superblock. If an empty UBI volume
+ * is being mounted, this function creates default superblock. Returns zero in
+ * case of success, and a negative error code in case of failure.
+ */
+int ubifs_read_superblock(struct ubifs_info *c)
+{
+ int err, sup_flags;
+ struct ubifs_sb_node *sup;
+
+ if (c->empty) {
+ err = create_default_filesystem(c);
+ if (err)
+ return err;
+ }
+
+ sup = ubifs_read_sb_node(c);
+ if (IS_ERR(sup))
+ return PTR_ERR(sup);
+
+ c->sup_node = sup;
+
+ c->fmt_version = le32_to_cpu(sup->fmt_version);
+ c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
+
+ /*
+ * The software supports all previous versions but not future versions,
+ * due to the unavailability of time-travelling equipment.
+ */
+ if (c->fmt_version > UBIFS_FORMAT_VERSION) {
+ ubifs_assert(c, !c->ro_media || c->ro_mount);
+ if (!c->ro_mount ||
+ c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) {
+ ubifs_err(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION,
+ UBIFS_RO_COMPAT_VERSION);
+ if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) {
+ ubifs_msg(c, "only R/O mounting is possible");
+ err = -EROFS;
+ } else
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * The FS is mounted R/O, and the media format is
+ * R/O-compatible with the UBIFS implementation, so we can
+ * mount.
+ */
+ c->rw_incompat = 1;
+ }
+
+ if (c->fmt_version < 3) {
+ ubifs_err(c, "on-flash format version %d is not supported",
+ c->fmt_version);
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (sup->key_hash) {
+ case UBIFS_KEY_HASH_R5:
+ c->key_hash = key_r5_hash;
+ c->key_hash_type = UBIFS_KEY_HASH_R5;
+ break;
+
+ case UBIFS_KEY_HASH_TEST:
+ c->key_hash = key_test_hash;
+ c->key_hash_type = UBIFS_KEY_HASH_TEST;
+ break;
+ }
+
+ c->key_fmt = sup->key_fmt;
+
+ switch (c->key_fmt) {
+ case UBIFS_SIMPLE_KEY_FMT:
+ c->key_len = UBIFS_SK_LEN;
+ break;
+ default:
+ ubifs_err(c, "unsupported key format");
+ err = -EINVAL;
+ goto out;
+ }
+
+ c->leb_cnt = le32_to_cpu(sup->leb_cnt);
+ c->max_leb_cnt = le32_to_cpu(sup->max_leb_cnt);
+ c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes);
+ c->log_lebs = le32_to_cpu(sup->log_lebs);
+ c->lpt_lebs = le32_to_cpu(sup->lpt_lebs);
+ c->orph_lebs = le32_to_cpu(sup->orph_lebs);
+ c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT;
+ c->fanout = le32_to_cpu(sup->fanout);
+ c->lsave_cnt = le32_to_cpu(sup->lsave_cnt);
+ c->rp_size = le64_to_cpu(sup->rp_size);
+ c->rp_uid = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid));
+ c->rp_gid = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid));
+ sup_flags = le32_to_cpu(sup->flags);
+ if (!c->mount_opts.override_compr)
+ c->default_compr = le16_to_cpu(sup->default_compr);
+
+ c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
+ memcpy(&c->uuid, &sup->uuid, 16);
+ c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
+ c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
+ c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
+ c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
+
+ err = authenticate_sb_node(c, sup);
+ if (err)
+ goto out;
+
+ if ((sup_flags & ~UBIFS_FLG_MASK) != 0) {
+ ubifs_err(c, "Unknown feature flags found: %#x",
+ sup_flags & ~UBIFS_FLG_MASK);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!IS_ENABLED(CONFIG_FS_ENCRYPTION) && c->encrypted) {
+ ubifs_err(c, "file system contains encrypted files but UBIFS"
+ " was built without crypto support.");
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Automatically increase file system size to the maximum size */
+ if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
+ int old_leb_cnt = c->leb_cnt;
+
+ c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+
+ c->superblock_need_write = 1;
+
+ dbg_mnt("Auto resizing from %d LEBs to %d LEBs",
+ old_leb_cnt, c->leb_cnt);
+ }
+
+ c->log_bytes = (long long)c->log_lebs * c->leb_size;
+ c->log_last = UBIFS_LOG_LNUM + c->log_lebs - 1;
+ c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs;
+ c->lpt_last = c->lpt_first + c->lpt_lebs - 1;
+ c->orph_first = c->lpt_last + 1;
+ c->orph_last = c->orph_first + c->orph_lebs - 1;
+ c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
+ c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
+ c->main_first = c->leb_cnt - c->main_lebs;
+
+ err = validate_sb(c, sup);
+out:
+ return err;
+}
+
+/**
+ * fixup_leb - fixup/unmap an LEB containing free space.
+ * @c: UBIFS file-system description object
+ * @lnum: the LEB number to fix up
+ * @len: number of used bytes in LEB (starting at offset 0)
+ *
+ * This function reads the contents of the given LEB number @lnum, then fixes
+ * it up, so that empty min. I/O units in the end of LEB are actually erased on
+ * flash (rather than being just all-0xff real data). If the LEB is completely
+ * empty, it is simply unmapped.
+ */
+static int fixup_leb(struct ubifs_info *c, int lnum, int len)
+{
+ int err;
+
+ ubifs_assert(c, len >= 0);
+ ubifs_assert(c, len % c->min_io_size == 0);
+ ubifs_assert(c, len < c->leb_size);
+
+ if (len == 0) {
+ dbg_mnt("unmap empty LEB %d", lnum);
+ return ubifs_leb_unmap(c, lnum);
+ }
+
+ dbg_mnt("fixup LEB %d, data len %d", lnum, len);
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 1);
+ if (err)
+ return err;
+
+ return ubifs_leb_change(c, lnum, c->sbuf, len);
+}
+
+/**
+ * fixup_free_space - find & remap all LEBs containing free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function walks through all LEBs in the filesystem and fiexes up those
+ * containing free/empty space.
+ */
+static int fixup_free_space(struct ubifs_info *c)
+{
+ int lnum, err = 0;
+ struct ubifs_lprops *lprops;
+
+ ubifs_get_lprops(c);
+
+ /* Fixup LEBs in the master area */
+ for (lnum = UBIFS_MST_LNUM; lnum < UBIFS_LOG_LNUM; lnum++) {
+ err = fixup_leb(c, lnum, c->mst_offs + c->mst_node_alsz);
+ if (err)
+ goto out;
+ }
+
+ /* Unmap unused log LEBs */
+ lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
+ while (lnum != c->ltail_lnum) {
+ err = fixup_leb(c, lnum, 0);
+ if (err)
+ goto out;
+ lnum = ubifs_next_log_lnum(c, lnum);
+ }
+
+ /*
+ * Fixup the log head which contains the only a CS node at the
+ * beginning.
+ */
+ err = fixup_leb(c, c->lhead_lnum,
+ ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size));
+ if (err)
+ goto out;
+
+ /* Fixup LEBs in the LPT area */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ int free = c->ltab[lnum - c->lpt_first].free;
+
+ if (free > 0) {
+ err = fixup_leb(c, lnum, c->leb_size - free);
+ if (err)
+ goto out;
+ }
+ }
+
+ /* Unmap LEBs in the orphans area */
+ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
+ err = fixup_leb(c, lnum, 0);
+ if (err)
+ goto out;
+ }
+
+ /* Fixup LEBs in the main area */
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ lprops = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lprops)) {
+ err = PTR_ERR(lprops);
+ goto out;
+ }
+
+ if (lprops->free > 0) {
+ err = fixup_leb(c, lnum, c->leb_size - lprops->free);
+ if (err)
+ goto out;
+ }
+ }
+
+out:
+ ubifs_release_lprops(c);
+ return err;
+}
+
+/**
+ * ubifs_fixup_free_space - find & fix all LEBs with free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function fixes up LEBs containing free space on first mount, if the
+ * appropriate flag was set when the FS was created. Each LEB with one or more
+ * empty min. I/O unit (i.e. free-space-count > 0) is re-written, to make sure
+ * the free space is actually erased. E.g., this is necessary for some NAND
+ * chips, since the free space may have been programmed like real "0xff" data
+ * (generating a non-0xff ECC), causing future writes to the not-really-erased
+ * NAND pages to behave badly. After the space is fixed up, the superblock flag
+ * is cleared, so that this is skipped for all future mounts.
+ */
+int ubifs_fixup_free_space(struct ubifs_info *c)
+{
+ int err;
+ struct ubifs_sb_node *sup = c->sup_node;
+
+ ubifs_assert(c, c->space_fixup);
+ ubifs_assert(c, !c->ro_mount);
+
+ ubifs_msg(c, "start fixing up free space");
+
+ err = fixup_free_space(c);
+ if (err)
+ return err;
+
+ /* Free-space fixup is no longer required */
+ c->space_fixup = 0;
+ sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
+
+ c->superblock_need_write = 1;
+
+ ubifs_msg(c, "free space fixup complete");
+ return err;
+}
+
+int ubifs_enable_encryption(struct ubifs_info *c)
+{
+ int err;
+ struct ubifs_sb_node *sup = c->sup_node;
+
+ if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
+ return -EOPNOTSUPP;
+
+ if (c->encrypted)
+ return 0;
+
+ if (c->ro_mount || c->ro_media)
+ return -EROFS;
+
+ if (c->fmt_version < 5) {
+ ubifs_err(c, "on-flash format version 5 is needed for encryption");
+ return -EINVAL;
+ }
+
+ sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
+
+ err = ubifs_write_sb_node(c, sup);
+ if (!err)
+ c->encrypted = 1;
+
+ return err;
+}
diff --git a/ubifs-utils/libubifs/scan.c b/ubifs-utils/libubifs/scan.c
new file mode 100644
index 00000000..84a9157d
--- /dev/null
+++ b/ubifs-utils/libubifs/scan.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements the scan which is a general-purpose function for
+ * determining what nodes are in an eraseblock. The scan is used to replay the
+ * journal, to do garbage collection. for the TNC in-the-gaps method, and by
+ * debugging functions.
+ */
+
+#include "ubifs.h"
+
+/**
+ * scan_padding_bytes - scan for padding bytes.
+ * @buf: buffer to scan
+ * @len: length of buffer
+ *
+ * This function returns the number of padding bytes on success and
+ * %SCANNED_GARBAGE on failure.
+ */
+static int scan_padding_bytes(void *buf, int len)
+{
+ int pad_len = 0, max_pad_len = min_t(int, UBIFS_PAD_NODE_SZ, len);
+ uint8_t *p = buf;
+
+ dbg_scan("not a node");
+
+ while (pad_len < max_pad_len && *p++ == UBIFS_PADDING_BYTE)
+ pad_len += 1;
+
+ if (!pad_len || (pad_len & 7))
+ return SCANNED_GARBAGE;
+
+ dbg_scan("%d padding bytes", pad_len);
+
+ return pad_len;
+}
+
+/**
+ * ubifs_scan_a_node - scan for a node or padding.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to scan
+ * @len: length of buffer
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ * @quiet: print no messages
+ *
+ * This function returns a scanning code to indicate what was scanned.
+ */
+int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int quiet)
+{
+ struct ubifs_ch *ch = buf;
+ uint32_t magic;
+
+ magic = le32_to_cpu(ch->magic);
+
+ if (magic == 0xFFFFFFFF) {
+ dbg_scan("hit empty space at LEB %d:%d", lnum, offs);
+ return SCANNED_EMPTY_SPACE;
+ }
+
+ if (magic != UBIFS_NODE_MAGIC)
+ return scan_padding_bytes(buf, len);
+
+ if (len < UBIFS_CH_SZ)
+ return SCANNED_GARBAGE;
+
+ dbg_scan("scanning %s at LEB %d:%d",
+ dbg_ntype(ch->node_type), lnum, offs);
+
+ if (ubifs_check_node(c, buf, len, lnum, offs, quiet, 1))
+ return SCANNED_A_CORRUPT_NODE;
+
+ if (ch->node_type == UBIFS_PAD_NODE) {
+ struct ubifs_pad_node *pad = buf;
+ int pad_len = le32_to_cpu(pad->pad_len);
+ int node_len = le32_to_cpu(ch->len);
+
+ /* Validate the padding node */
+ if (pad_len < 0 ||
+ offs + node_len + pad_len > c->leb_size) {
+ if (!quiet) {
+ ubifs_err(c, "bad pad node at LEB %d:%d",
+ lnum, offs);
+ ubifs_dump_node(c, pad, len);
+ }
+ return SCANNED_A_BAD_PAD_NODE;
+ }
+
+ /* Make the node pads to 8-byte boundary */
+ if ((node_len + pad_len) & 7) {
+ if (!quiet)
+ ubifs_err(c, "bad padding length %d - %d",
+ offs, offs + node_len + pad_len);
+ return SCANNED_A_BAD_PAD_NODE;
+ }
+
+ dbg_scan("%d bytes padded at LEB %d:%d, offset now %d", pad_len,
+ lnum, offs, ALIGN(offs + node_len + pad_len, 8));
+
+ return node_len + pad_len;
+ }
+
+ return SCANNED_A_NODE;
+}
+
+/**
+ * ubifs_start_scan - create LEB scanning information at start of scan.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ * @sbuf: scan buffer (must be c->leb_size)
+ *
+ * This function returns the scanned information on success and a negative error
+ * code on failure.
+ */
+struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf)
+{
+ struct ubifs_scan_leb *sleb;
+ int err;
+
+ dbg_scan("scan LEB %d:%d", lnum, offs);
+
+ sleb = kzalloc(sizeof(struct ubifs_scan_leb), GFP_NOFS);
+ if (!sleb)
+ return ERR_PTR(-ENOMEM);
+
+ sleb->lnum = lnum;
+ INIT_LIST_HEAD(&sleb->nodes);
+ sleb->buf = sbuf;
+
+ err = ubifs_leb_read(c, lnum, sbuf + offs, offs, c->leb_size - offs, 0);
+ if (err && err != -EBADMSG) {
+ ubifs_err(c, "cannot read %d bytes from LEB %d:%d, error %d",
+ c->leb_size - offs, lnum, offs, err);
+ kfree(sleb);
+ return ERR_PTR(err);
+ }
+
+ /*
+ * Note, we ignore integrity errors (EBASMSG) because all the nodes are
+ * protected by CRC checksums.
+ */
+ return sleb;
+}
+
+/**
+ * ubifs_end_scan - update LEB scanning information at end of scan.
+ * @c: UBIFS file-system description object
+ * @sleb: scanning information
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ */
+void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int lnum, int offs)
+{
+ dbg_scan("stop scanning LEB %d at offset %d", lnum, offs);
+ ubifs_assert(c, offs % c->min_io_size == 0);
+
+ sleb->endpt = ALIGN(offs, c->min_io_size);
+}
+
+/**
+ * ubifs_add_snod - add a scanned node to LEB scanning information.
+ * @c: UBIFS file-system description object
+ * @sleb: scanning information
+ * @buf: buffer containing node
+ * @offs: offset of node on flash
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ void *buf, int offs)
+{
+ struct ubifs_ch *ch = buf;
+ struct ubifs_ino_node *ino = buf;
+ struct ubifs_scan_node *snod;
+
+ snod = kmalloc(sizeof(struct ubifs_scan_node), GFP_NOFS);
+ if (!snod)
+ return -ENOMEM;
+
+ snod->sqnum = le64_to_cpu(ch->sqnum);
+ snod->type = ch->node_type;
+ snod->offs = offs;
+ snod->len = le32_to_cpu(ch->len);
+ snod->node = buf;
+
+ switch (ch->node_type) {
+ case UBIFS_INO_NODE:
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ case UBIFS_DATA_NODE:
+ /*
+ * The key is in the same place in all keyed
+ * nodes.
+ */
+ key_read(c, &ino->key, &snod->key);
+ break;
+ default:
+ invalid_key_init(c, &snod->key);
+ break;
+ }
+ list_add_tail(&snod->list, &sleb->nodes);
+ sleb->nodes_cnt += 1;
+ return 0;
+}
+
+/**
+ * ubifs_scanned_corruption - print information after UBIFS scanned corruption.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of corruption
+ * @offs: offset of corruption
+ * @buf: buffer containing corruption
+ */
+void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
+ void *buf)
+{
+ int len;
+
+ ubifs_err(c, "corruption at LEB %d:%d", lnum, offs);
+ len = c->leb_size - offs;
+ if (len > 8192)
+ len = 8192;
+ ubifs_err(c, "first %d bytes from LEB %d:%d", len, lnum, offs);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
+}
+
+/**
+ * ubifs_scan - scan a logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @offs: offset to start at (usually zero)
+ * @sbuf: scan buffer (must be of @c->leb_size bytes in size)
+ * @quiet: print no messages
+ *
+ * This function scans LEB number @lnum and returns complete information about
+ * its contents. Returns the scanned information in case of success and,
+ * %-EUCLEAN if the LEB neads recovery, and other negative error codes in case
+ * of failure.
+ *
+ * If @quiet is non-zero, this function does not print large and scary
+ * error messages and flash dumps in case of errors.
+ */
+struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int quiet)
+{
+ void *buf = sbuf + offs;
+ int err, len = c->leb_size - offs;
+ struct ubifs_scan_leb *sleb;
+
+ sleb = ubifs_start_scan(c, lnum, offs, sbuf);
+ if (IS_ERR(sleb))
+ return sleb;
+
+ while (len >= 8) {
+ struct ubifs_ch *ch = buf;
+ int node_len, ret;
+
+ dbg_scan("look at LEB %d:%d (%d bytes left)",
+ lnum, offs, len);
+
+ cond_resched();
+
+ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
+ if (ret > 0) {
+ /* Padding bytes or a valid padding node */
+ offs += ret;
+ buf += ret;
+ len -= ret;
+ continue;
+ }
+
+ if (ret == SCANNED_EMPTY_SPACE)
+ /* Empty space is checked later */
+ break;
+
+ switch (ret) {
+ case SCANNED_GARBAGE:
+ ubifs_err(c, "garbage");
+ goto corrupted;
+ case SCANNED_A_NODE:
+ break;
+ case SCANNED_A_CORRUPT_NODE:
+ case SCANNED_A_BAD_PAD_NODE:
+ ubifs_err(c, "bad node");
+ goto corrupted;
+ default:
+ ubifs_err(c, "unknown");
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = ubifs_add_snod(c, sleb, buf, offs);
+ if (err)
+ goto error;
+
+ node_len = ALIGN(le32_to_cpu(ch->len), 8);
+ offs += node_len;
+ buf += node_len;
+ len -= node_len;
+ }
+
+ if (offs % c->min_io_size) {
+ if (!quiet)
+ ubifs_err(c, "empty space starts at non-aligned offset %d",
+ offs);
+ goto corrupted;
+ }
+
+ ubifs_end_scan(c, sleb, lnum, offs);
+
+ for (; len > 4; offs += 4, buf = buf + 4, len -= 4)
+ if (*(uint32_t *)buf != 0xffffffff)
+ break;
+ for (; len; offs++, buf++, len--)
+ if (*(uint8_t *)buf != 0xff) {
+ if (!quiet)
+ ubifs_err(c, "corrupt empty space at LEB %d:%d",
+ lnum, offs);
+ goto corrupted;
+ }
+
+ return sleb;
+
+corrupted:
+ if (!quiet) {
+ ubifs_scanned_corruption(c, lnum, offs, buf);
+ ubifs_err(c, "LEB %d scanning failed", lnum);
+ }
+ err = -EUCLEAN;
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+
+error:
+ ubifs_err(c, "LEB %d scanning failed, error %d", lnum, err);
+ ubifs_scan_destroy(sleb);
+ return ERR_PTR(err);
+}
+
+/**
+ * ubifs_scan_destroy - destroy LEB scanning information.
+ * @sleb: scanning information to free
+ */
+void ubifs_scan_destroy(struct ubifs_scan_leb *sleb)
+{
+ struct ubifs_scan_node *node;
+ struct list_head *head;
+
+ head = &sleb->nodes;
+ while (!list_empty(head)) {
+ node = list_entry(head->next, struct ubifs_scan_node, list);
+ list_del(&node->list);
+ kfree(node);
+ }
+ kfree(sleb);
+}
diff --git a/ubifs-utils/libubifs/super.c b/ubifs-utils/libubifs/super.c
new file mode 100644
index 00000000..09e270d6
--- /dev/null
+++ b/ubifs-utils/libubifs/super.c
@@ -0,0 +1,2505 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+/*
+ * This file implements UBIFS initialization and VFS superblock operations. Some
+ * initialization stuff which is rather large and complex is placed at
+ * corresponding subsystems, but most of it is here.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/kthread.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/math64.h>
+#include <linux/writeback.h>
+#include "ubifs.h"
+
+static int ubifs_default_version_set(const char *val, const struct kernel_param *kp)
+{
+ int n = 0, ret;
+
+ ret = kstrtoint(val, 10, &n);
+ if (ret != 0 || n < 4 || n > UBIFS_FORMAT_VERSION)
+ return -EINVAL;
+ return param_set_int(val, kp);
+}
+
+static const struct kernel_param_ops ubifs_default_version_ops = {
+ .set = ubifs_default_version_set,
+ .get = param_get_int,
+};
+
+int ubifs_default_version = UBIFS_FORMAT_VERSION;
+module_param_cb(default_version, &ubifs_default_version_ops, &ubifs_default_version, 0600);
+
+/*
+ * Maximum amount of memory we may 'kmalloc()' without worrying that we are
+ * allocating too much.
+ */
+#define UBIFS_KMALLOC_OK (128*1024)
+
+/* Slab cache for UBIFS inodes */
+static struct kmem_cache *ubifs_inode_slab;
+
+/* UBIFS TNC shrinker description */
+static struct shrinker *ubifs_shrinker_info;
+
+/**
+ * validate_inode - validate inode.
+ * @c: UBIFS file-system description object
+ * @inode: the inode to validate
+ *
+ * This is a helper function for 'ubifs_iget()' which validates various fields
+ * of a newly built inode to make sure they contain sane values and prevent
+ * possible vulnerabilities. Returns zero if the inode is all right and
+ * a non-zero error code if not.
+ */
+static int validate_inode(struct ubifs_info *c, const struct inode *inode)
+{
+ int err;
+ const struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (inode->i_size > c->max_inode_sz) {
+ ubifs_err(c, "inode is too large (%lld)",
+ (long long)inode->i_size);
+ return 1;
+ }
+
+ if (ui->compr_type >= UBIFS_COMPR_TYPES_CNT) {
+ ubifs_err(c, "unknown compression type %d", ui->compr_type);
+ return 2;
+ }
+
+ if (ui->xattr_names + ui->xattr_cnt > XATTR_LIST_MAX)
+ return 3;
+
+ if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA)
+ return 4;
+
+ if (ui->xattr && !S_ISREG(inode->i_mode))
+ return 5;
+
+ if (!ubifs_compr_present(c, ui->compr_type)) {
+ ubifs_warn(c, "inode %lu uses '%s' compression, but it was not compiled in",
+ inode->i_ino, ubifs_compr_name(c, ui->compr_type));
+ }
+
+ err = dbg_check_dir(c, inode);
+ return err;
+}
+
+struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
+{
+ int err;
+ union ubifs_key key;
+ struct ubifs_ino_node *ino;
+ struct ubifs_info *c = sb->s_fs_info;
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
+ dbg_gen("inode %lu", inum);
+
+ inode = iget_locked(sb, inum);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+ ui = ubifs_inode(inode);
+
+ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ino) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ino_key_init(c, &key, inode->i_ino);
+
+ err = ubifs_tnc_lookup(c, &key, ino);
+ if (err)
+ goto out_ino;
+
+ inode->i_flags |= S_NOCMTIME;
+
+ if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
+ inode->i_flags |= S_NOATIME;
+
+ set_nlink(inode, le32_to_cpu(ino->nlink));
+ i_uid_write(inode, le32_to_cpu(ino->uid));
+ i_gid_write(inode, le32_to_cpu(ino->gid));
+ inode_set_atime(inode, (int64_t)le64_to_cpu(ino->atime_sec),
+ le32_to_cpu(ino->atime_nsec));
+ inode_set_mtime(inode, (int64_t)le64_to_cpu(ino->mtime_sec),
+ le32_to_cpu(ino->mtime_nsec));
+ inode_set_ctime(inode, (int64_t)le64_to_cpu(ino->ctime_sec),
+ le32_to_cpu(ino->ctime_nsec));
+ inode->i_mode = le32_to_cpu(ino->mode);
+ inode->i_size = le64_to_cpu(ino->size);
+
+ ui->data_len = le32_to_cpu(ino->data_len);
+ ui->flags = le32_to_cpu(ino->flags);
+ ui->compr_type = le16_to_cpu(ino->compr_type);
+ ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum);
+ ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ ui->xattr_size = le32_to_cpu(ino->xattr_size);
+ ui->xattr_names = le32_to_cpu(ino->xattr_names);
+ ui->synced_i_size = ui->ui_size = inode->i_size;
+
+ ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0;
+
+ err = validate_inode(c, inode);
+ if (err)
+ goto out_invalid;
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_mapping->a_ops = &ubifs_file_address_operations;
+ inode->i_op = &ubifs_file_inode_operations;
+ inode->i_fop = &ubifs_file_operations;
+ if (ui->xattr) {
+ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ ((char *)ui->data)[ui->data_len] = '\0';
+ } else if (ui->data_len != 0) {
+ err = 10;
+ goto out_invalid;
+ }
+ break;
+ case S_IFDIR:
+ inode->i_op = &ubifs_dir_inode_operations;
+ inode->i_fop = &ubifs_dir_operations;
+ if (ui->data_len != 0) {
+ err = 11;
+ goto out_invalid;
+ }
+ break;
+ case S_IFLNK:
+ inode->i_op = &ubifs_symlink_inode_operations;
+ if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
+ err = 12;
+ goto out_invalid;
+ }
+ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ ((char *)ui->data)[ui->data_len] = '\0';
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ {
+ dev_t rdev;
+ union ubifs_dev_desc *dev;
+
+ ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+ if (!ui->data) {
+ err = -ENOMEM;
+ goto out_ino;
+ }
+
+ dev = (union ubifs_dev_desc *)ino->data;
+ if (ui->data_len == sizeof(dev->new))
+ rdev = new_decode_dev(le32_to_cpu(dev->new));
+ else if (ui->data_len == sizeof(dev->huge))
+ rdev = huge_decode_dev(le64_to_cpu(dev->huge));
+ else {
+ err = 13;
+ goto out_invalid;
+ }
+ memcpy(ui->data, ino->data, ui->data_len);
+ inode->i_op = &ubifs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, rdev);
+ break;
+ }
+ case S_IFSOCK:
+ case S_IFIFO:
+ inode->i_op = &ubifs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, 0);
+ if (ui->data_len != 0) {
+ err = 14;
+ goto out_invalid;
+ }
+ break;
+ default:
+ err = 15;
+ goto out_invalid;
+ }
+
+ kfree(ino);
+ ubifs_set_inode_flags(inode);
+ unlock_new_inode(inode);
+ return inode;
+
+out_invalid:
+ ubifs_err(c, "inode %lu validation failed, error %d", inode->i_ino, err);
+ ubifs_dump_node(c, ino, UBIFS_MAX_INO_NODE_SZ);
+ ubifs_dump_inode(c, inode);
+ err = -EINVAL;
+out_ino:
+ kfree(ino);
+out:
+ ubifs_err(c, "failed to read inode %lu, error %d", inode->i_ino, err);
+ iget_failed(inode);
+ return ERR_PTR(err);
+}
+
+static struct inode *ubifs_alloc_inode(struct super_block *sb)
+{
+ struct ubifs_inode *ui;
+
+ ui = alloc_inode_sb(sb, ubifs_inode_slab, GFP_NOFS);
+ if (!ui)
+ return NULL;
+
+ memset((void *)ui + sizeof(struct inode), 0,
+ sizeof(struct ubifs_inode) - sizeof(struct inode));
+ mutex_init(&ui->ui_mutex);
+ init_rwsem(&ui->xattr_sem);
+ spin_lock_init(&ui->ui_lock);
+ return &ui->vfs_inode;
+};
+
+static void ubifs_free_inode(struct inode *inode)
+{
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ kfree(ui->data);
+ fscrypt_free_inode(inode);
+
+ kmem_cache_free(ubifs_inode_slab, ui);
+}
+
+/*
+ * Note, Linux write-back code calls this without 'i_mutex'.
+ */
+static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ int err = 0;
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ubifs_assert(c, !ui->xattr);
+ if (is_bad_inode(inode))
+ return 0;
+
+ mutex_lock(&ui->ui_mutex);
+ /*
+ * Due to races between write-back forced by budgeting
+ * (see 'sync_some_inodes()') and background write-back, the inode may
+ * have already been synchronized, do not do this again. This might
+ * also happen if it was synchronized in an VFS operation, e.g.
+ * 'ubifs_link()'.
+ */
+ if (!ui->dirty) {
+ mutex_unlock(&ui->ui_mutex);
+ return 0;
+ }
+
+ /*
+ * As an optimization, do not write orphan inodes to the media just
+ * because this is not needed.
+ */
+ dbg_gen("inode %lu, mode %#x, nlink %u",
+ inode->i_ino, (int)inode->i_mode, inode->i_nlink);
+ if (inode->i_nlink) {
+ err = ubifs_jnl_write_inode(c, inode);
+ if (err)
+ ubifs_err(c, "can't write inode %lu, error %d",
+ inode->i_ino, err);
+ else
+ err = dbg_check_inode_size(c, inode, ui->ui_size);
+ }
+
+ ui->dirty = 0;
+ mutex_unlock(&ui->ui_mutex);
+ ubifs_release_dirty_inode_budget(c, ui);
+ return err;
+}
+
+static int ubifs_drop_inode(struct inode *inode)
+{
+ int drop = generic_drop_inode(inode);
+
+ if (!drop)
+ drop = fscrypt_drop_inode(inode);
+
+ return drop;
+}
+
+static void ubifs_evict_inode(struct inode *inode)
+{
+ int err;
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ if (ui->xattr)
+ /*
+ * Extended attribute inode deletions are fully handled in
+ * 'ubifs_removexattr()'. These inodes are special and have
+ * limited usage, so there is nothing to do here.
+ */
+ goto out;
+
+ dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode);
+ ubifs_assert(c, !atomic_read(&inode->i_count));
+
+ truncate_inode_pages_final(&inode->i_data);
+
+ if (inode->i_nlink)
+ goto done;
+
+ if (is_bad_inode(inode))
+ goto out;
+
+ ui->ui_size = inode->i_size = 0;
+ err = ubifs_jnl_delete_inode(c, inode);
+ if (err)
+ /*
+ * Worst case we have a lost orphan inode wasting space, so a
+ * simple error message is OK here.
+ */
+ ubifs_err(c, "can't delete inode %lu, error %d",
+ inode->i_ino, err);
+
+out:
+ if (ui->dirty)
+ ubifs_release_dirty_inode_budget(c, ui);
+ else {
+ /* We've deleted something - clean the "no space" flags */
+ c->bi.nospace = c->bi.nospace_rp = 0;
+ smp_wmb();
+ }
+done:
+ clear_inode(inode);
+ fscrypt_put_encryption_info(inode);
+}
+
+static void ubifs_dirty_inode(struct inode *inode, int flags)
+{
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ ubifs_assert(c, mutex_is_locked(&ui->ui_mutex));
+ if (!ui->dirty) {
+ ui->dirty = 1;
+ dbg_gen("inode %lu", inode->i_ino);
+ }
+}
+
+static int ubifs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct ubifs_info *c = dentry->d_sb->s_fs_info;
+ unsigned long long free;
+ __le32 *uuid = (__le32 *)c->uuid;
+
+ free = ubifs_get_free_space(c);
+ dbg_gen("free space %lld bytes (%lld blocks)",
+ free, free >> UBIFS_BLOCK_SHIFT);
+
+ buf->f_type = UBIFS_SUPER_MAGIC;
+ buf->f_bsize = UBIFS_BLOCK_SIZE;
+ buf->f_blocks = c->block_cnt;
+ buf->f_bfree = free >> UBIFS_BLOCK_SHIFT;
+ if (free > c->report_rp_size)
+ buf->f_bavail = (free - c->report_rp_size) >> UBIFS_BLOCK_SHIFT;
+ else
+ buf->f_bavail = 0;
+ buf->f_files = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = UBIFS_MAX_NLEN;
+ buf->f_fsid.val[0] = le32_to_cpu(uuid[0]) ^ le32_to_cpu(uuid[2]);
+ buf->f_fsid.val[1] = le32_to_cpu(uuid[1]) ^ le32_to_cpu(uuid[3]);
+ ubifs_assert(c, buf->f_bfree <= c->block_cnt);
+ return 0;
+}
+
+static int ubifs_show_options(struct seq_file *s, struct dentry *root)
+{
+ struct ubifs_info *c = root->d_sb->s_fs_info;
+
+ if (c->mount_opts.unmount_mode == 2)
+ seq_puts(s, ",fast_unmount");
+ else if (c->mount_opts.unmount_mode == 1)
+ seq_puts(s, ",norm_unmount");
+
+ if (c->mount_opts.bulk_read == 2)
+ seq_puts(s, ",bulk_read");
+ else if (c->mount_opts.bulk_read == 1)
+ seq_puts(s, ",no_bulk_read");
+
+ if (c->mount_opts.chk_data_crc == 2)
+ seq_puts(s, ",chk_data_crc");
+ else if (c->mount_opts.chk_data_crc == 1)
+ seq_puts(s, ",no_chk_data_crc");
+
+ if (c->mount_opts.override_compr) {
+ seq_printf(s, ",compr=%s",
+ ubifs_compr_name(c, c->mount_opts.compr_type));
+ }
+
+ seq_printf(s, ",assert=%s", ubifs_assert_action_name(c));
+ seq_printf(s, ",ubi=%d,vol=%d", c->vi.ubi_num, c->vi.vol_id);
+
+ return 0;
+}
+
+static int ubifs_sync_fs(struct super_block *sb, int wait)
+{
+ int i, err;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ /*
+ * Zero @wait is just an advisory thing to help the file system shove
+ * lots of data into the queues, and there will be the second
+ * '->sync_fs()' call, with non-zero @wait.
+ */
+ if (!wait)
+ return 0;
+
+ /*
+ * Synchronize write buffers, because 'ubifs_run_commit()' does not
+ * do this if it waits for an already running commit.
+ */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Strictly speaking, it is not necessary to commit the journal here,
+ * synchronizing write-buffers would be enough. But committing makes
+ * UBIFS free space predictions much more accurate, so we want to let
+ * the user be able to get more accurate results of 'statfs()' after
+ * they synchronize the file system.
+ */
+ err = ubifs_run_commit(c);
+ if (err)
+ return err;
+
+ return ubi_sync(c->vi.ubi_num);
+}
+
+/**
+ * init_constants_early - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This function initialize UBIFS constants which do not need the superblock to
+ * be read. It also checks that the UBI volume satisfies basic UBIFS
+ * requirements. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int init_constants_early(struct ubifs_info *c)
+{
+ if (c->vi.corrupted) {
+ ubifs_warn(c, "UBI volume is corrupted - read-only mode");
+ c->ro_media = 1;
+ }
+
+ if (c->di.ro_mode) {
+ ubifs_msg(c, "read-only UBI device");
+ c->ro_media = 1;
+ }
+
+ if (c->vi.vol_type == UBI_STATIC_VOLUME) {
+ ubifs_msg(c, "static UBI volume - read-only mode");
+ c->ro_media = 1;
+ }
+
+ c->leb_cnt = c->vi.size;
+ c->leb_size = c->vi.usable_leb_size;
+ c->leb_start = c->di.leb_start;
+ c->half_leb_size = c->leb_size / 2;
+ c->min_io_size = c->di.min_io_size;
+ c->min_io_shift = fls(c->min_io_size) - 1;
+ c->max_write_size = c->di.max_write_size;
+ c->max_write_shift = fls(c->max_write_size) - 1;
+
+ if (c->leb_size < UBIFS_MIN_LEB_SZ) {
+ ubifs_errc(c, "too small LEBs (%d bytes), min. is %d bytes",
+ c->leb_size, UBIFS_MIN_LEB_SZ);
+ return -EINVAL;
+ }
+
+ if (c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ ubifs_errc(c, "too few LEBs (%d), min. is %d",
+ c->leb_cnt, UBIFS_MIN_LEB_CNT);
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(c->min_io_size)) {
+ ubifs_errc(c, "bad min. I/O size %d", c->min_io_size);
+ return -EINVAL;
+ }
+
+ /*
+ * Maximum write size has to be greater or equivalent to min. I/O
+ * size, and be multiple of min. I/O size.
+ */
+ if (c->max_write_size < c->min_io_size ||
+ c->max_write_size % c->min_io_size ||
+ !is_power_of_2(c->max_write_size)) {
+ ubifs_errc(c, "bad write buffer size %d for %d min. I/O unit",
+ c->max_write_size, c->min_io_size);
+ return -EINVAL;
+ }
+
+ /*
+ * UBIFS aligns all node to 8-byte boundary, so to make function in
+ * io.c simpler, assume minimum I/O unit size to be 8 bytes if it is
+ * less than 8.
+ */
+ if (c->min_io_size < 8) {
+ c->min_io_size = 8;
+ c->min_io_shift = 3;
+ if (c->max_write_size < c->min_io_size) {
+ c->max_write_size = c->min_io_size;
+ c->max_write_shift = c->min_io_shift;
+ }
+ }
+
+ c->ref_node_alsz = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size);
+ c->mst_node_alsz = ALIGN(UBIFS_MST_NODE_SZ, c->min_io_size);
+
+ /*
+ * Initialize node length ranges which are mostly needed for node
+ * length validation.
+ */
+ c->ranges[UBIFS_PAD_NODE].len = UBIFS_PAD_NODE_SZ;
+ c->ranges[UBIFS_SB_NODE].len = UBIFS_SB_NODE_SZ;
+ c->ranges[UBIFS_MST_NODE].len = UBIFS_MST_NODE_SZ;
+ c->ranges[UBIFS_REF_NODE].len = UBIFS_REF_NODE_SZ;
+ c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ;
+ c->ranges[UBIFS_CS_NODE].len = UBIFS_CS_NODE_SZ;
+ c->ranges[UBIFS_AUTH_NODE].min_len = UBIFS_AUTH_NODE_SZ;
+ c->ranges[UBIFS_AUTH_NODE].max_len = UBIFS_AUTH_NODE_SZ +
+ UBIFS_MAX_HMAC_LEN;
+ c->ranges[UBIFS_SIG_NODE].min_len = UBIFS_SIG_NODE_SZ;
+ c->ranges[UBIFS_SIG_NODE].max_len = c->leb_size - UBIFS_SB_NODE_SZ;
+
+ c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ;
+ c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ;
+ c->ranges[UBIFS_ORPH_NODE].min_len =
+ UBIFS_ORPH_NODE_SZ + sizeof(__le64);
+ c->ranges[UBIFS_ORPH_NODE].max_len = c->leb_size;
+ c->ranges[UBIFS_DENT_NODE].min_len = UBIFS_DENT_NODE_SZ;
+ c->ranges[UBIFS_DENT_NODE].max_len = UBIFS_MAX_DENT_NODE_SZ;
+ c->ranges[UBIFS_XENT_NODE].min_len = UBIFS_XENT_NODE_SZ;
+ c->ranges[UBIFS_XENT_NODE].max_len = UBIFS_MAX_XENT_NODE_SZ;
+ c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_SZ;
+ c->ranges[UBIFS_DATA_NODE].max_len = UBIFS_MAX_DATA_NODE_SZ;
+ /*
+ * Minimum indexing node size is amended later when superblock is
+ * read and the key length is known.
+ */
+ c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ + UBIFS_BRANCH_SZ;
+ /*
+ * Maximum indexing node size is amended later when superblock is
+ * read and the fanout is known.
+ */
+ c->ranges[UBIFS_IDX_NODE].max_len = INT_MAX;
+
+ /*
+ * Initialize dead and dark LEB space watermarks. See gc.c for comments
+ * about these values.
+ */
+ c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size);
+ c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size);
+
+ /*
+ * Calculate how many bytes would be wasted at the end of LEB if it was
+ * fully filled with data nodes of maximum size. This is used in
+ * calculations when reporting free space.
+ */
+ c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ;
+
+ /* Buffer size for bulk-reads */
+ c->max_bu_buf_len = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ;
+ if (c->max_bu_buf_len > c->leb_size)
+ c->max_bu_buf_len = c->leb_size;
+
+ /* Log is ready, preserve one LEB for commits. */
+ c->min_log_bytes = c->leb_size;
+
+ return 0;
+}
+
+/**
+ * bud_wbuf_callback - bud LEB write-buffer synchronization call-back.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB the write-buffer was synchronized to
+ * @free: how many free bytes left in this LEB
+ * @pad: how many bytes were padded
+ *
+ * This is a callback function which is called by the I/O unit when the
+ * write-buffer is synchronized. We need this to correctly maintain space
+ * accounting in bud logical eraseblocks. This function returns zero in case of
+ * success and a negative error code in case of failure.
+ *
+ * This function actually belongs to the journal, but we keep it here because
+ * we want to keep it static.
+ */
+static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad)
+{
+ return ubifs_update_one_lp(c, lnum, free, pad, 0, 0);
+}
+
+/*
+ * init_constants_sb - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which initializes various UBIFS constants after
+ * the superblock has been read. It also checks various UBIFS parameters and
+ * makes sure they are all right. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int init_constants_sb(struct ubifs_info *c)
+{
+ int tmp, err;
+ long long tmp64;
+
+ c->main_bytes = (long long)c->main_lebs * c->leb_size;
+ c->max_znode_sz = sizeof(struct ubifs_znode) +
+ c->fanout * sizeof(struct ubifs_zbranch);
+
+ tmp = ubifs_idx_node_sz(c, 1);
+ c->ranges[UBIFS_IDX_NODE].min_len = tmp;
+ c->min_idx_node_sz = ALIGN(tmp, 8);
+
+ tmp = ubifs_idx_node_sz(c, c->fanout);
+ c->ranges[UBIFS_IDX_NODE].max_len = tmp;
+ c->max_idx_node_sz = ALIGN(tmp, 8);
+
+ /* Make sure LEB size is large enough to fit full commit */
+ tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt;
+ tmp = ALIGN(tmp, c->min_io_size);
+ if (tmp > c->leb_size) {
+ ubifs_err(c, "too small LEB size %d, at least %d needed",
+ c->leb_size, tmp);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that the log is large enough to fit reference nodes for
+ * all buds plus one reserved LEB.
+ */
+ tmp64 = c->max_bud_bytes + c->leb_size - 1;
+ c->max_bud_cnt = div_u64(tmp64, c->leb_size);
+ tmp = (c->ref_node_alsz * c->max_bud_cnt + c->leb_size - 1);
+ tmp /= c->leb_size;
+ tmp += 1;
+ if (c->log_lebs < tmp) {
+ ubifs_err(c, "too small log %d LEBs, required min. %d LEBs",
+ c->log_lebs, tmp);
+ return -EINVAL;
+ }
+
+ /*
+ * When budgeting we assume worst-case scenarios when the pages are not
+ * be compressed and direntries are of the maximum size.
+ *
+ * Note, data, which may be stored in inodes is budgeted separately, so
+ * it is not included into 'c->bi.inode_budget'.
+ */
+ c->bi.page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE;
+ c->bi.inode_budget = UBIFS_INO_NODE_SZ;
+ c->bi.dent_budget = UBIFS_MAX_DENT_NODE_SZ;
+
+ /*
+ * When the amount of flash space used by buds becomes
+ * 'c->max_bud_bytes', UBIFS just blocks all writers and starts commit.
+ * The writers are unblocked when the commit is finished. To avoid
+ * writers to be blocked UBIFS initiates background commit in advance,
+ * when number of bud bytes becomes above the limit defined below.
+ */
+ c->bg_bud_bytes = (c->max_bud_bytes * 13) >> 4;
+
+ /*
+ * Ensure minimum journal size. All the bytes in the journal heads are
+ * considered to be used, when calculating the current journal usage.
+ * Consequently, if the journal is too small, UBIFS will treat it as
+ * always full.
+ */
+ tmp64 = (long long)(c->jhead_cnt + 1) * c->leb_size + 1;
+ if (c->bg_bud_bytes < tmp64)
+ c->bg_bud_bytes = tmp64;
+ if (c->max_bud_bytes < tmp64 + c->leb_size)
+ c->max_bud_bytes = tmp64 + c->leb_size;
+
+ err = ubifs_calc_lpt_geom(c);
+ if (err)
+ return err;
+
+ /* Initialize effective LEB size used in budgeting calculations */
+ c->idx_leb_size = c->leb_size - c->max_idx_node_sz;
+ return 0;
+}
+
+/*
+ * init_constants_master - initialize UBIFS constants.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which initializes various UBIFS constants after
+ * the master node has been read. It also checks various UBIFS parameters and
+ * makes sure they are all right.
+ */
+static void init_constants_master(struct ubifs_info *c)
+{
+ long long tmp64;
+
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ c->report_rp_size = ubifs_reported_space(c, c->rp_size);
+
+ /*
+ * Calculate total amount of FS blocks. This number is not used
+ * internally because it does not make much sense for UBIFS, but it is
+ * necessary to report something for the 'statfs()' call.
+ *
+ * Subtract the LEB reserved for GC, the LEB which is reserved for
+ * deletions, minimum LEBs for the index, and assume only one journal
+ * head is available.
+ */
+ tmp64 = c->main_lebs - 1 - 1 - MIN_INDEX_LEBS - c->jhead_cnt + 1;
+ tmp64 *= (long long)c->leb_size - c->leb_overhead;
+ tmp64 = ubifs_reported_space(c, tmp64);
+ c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT;
+}
+
+/**
+ * take_gc_lnum - reserve GC LEB.
+ * @c: UBIFS file-system description object
+ *
+ * This function ensures that the LEB reserved for garbage collection is marked
+ * as "taken" in lprops. We also have to set free space to LEB size and dirty
+ * space to zero, because lprops may contain out-of-date information if the
+ * file-system was un-mounted before it has been committed. This function
+ * returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int take_gc_lnum(struct ubifs_info *c)
+{
+ int err;
+
+ if (c->gc_lnum == -1) {
+ ubifs_err(c, "no LEB for GC");
+ return -EINVAL;
+ }
+
+ /* And we have to tell lprops that this LEB is taken */
+ err = ubifs_change_one_lp(c, c->gc_lnum, c->leb_size, 0,
+ LPROPS_TAKEN, 0, 0);
+ return err;
+}
+
+/**
+ * alloc_wbufs - allocate write-buffers.
+ * @c: UBIFS file-system description object
+ *
+ * This helper function allocates and initializes UBIFS write-buffers. Returns
+ * zero in case of success and %-ENOMEM in case of failure.
+ */
+static int alloc_wbufs(struct ubifs_info *c)
+{
+ int i, err;
+
+ c->jheads = kcalloc(c->jhead_cnt, sizeof(struct ubifs_jhead),
+ GFP_KERNEL);
+ if (!c->jheads)
+ return -ENOMEM;
+
+ /* Initialize journal heads */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ INIT_LIST_HEAD(&c->jheads[i].buds_list);
+ err = ubifs_wbuf_init(c, &c->jheads[i].wbuf);
+ if (err)
+ goto out_wbuf;
+
+ c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback;
+ c->jheads[i].wbuf.jhead = i;
+ c->jheads[i].grouped = 1;
+ c->jheads[i].log_hash = ubifs_hash_get_desc(c);
+ if (IS_ERR(c->jheads[i].log_hash)) {
+ err = PTR_ERR(c->jheads[i].log_hash);
+ goto out_log_hash;
+ }
+ }
+
+ /*
+ * Garbage Collector head does not need to be synchronized by timer.
+ * Also GC head nodes are not grouped.
+ */
+ c->jheads[GCHD].wbuf.no_timer = 1;
+ c->jheads[GCHD].grouped = 0;
+
+ return 0;
+
+out_log_hash:
+ kfree(c->jheads[i].wbuf.buf);
+ kfree(c->jheads[i].wbuf.inodes);
+
+out_wbuf:
+ while (i--) {
+ kfree(c->jheads[i].wbuf.buf);
+ kfree(c->jheads[i].wbuf.inodes);
+ kfree(c->jheads[i].log_hash);
+ }
+ kfree(c->jheads);
+ c->jheads = NULL;
+
+ return err;
+}
+
+/**
+ * free_wbufs - free write-buffers.
+ * @c: UBIFS file-system description object
+ */
+static void free_wbufs(struct ubifs_info *c)
+{
+ int i;
+
+ if (c->jheads) {
+ for (i = 0; i < c->jhead_cnt; i++) {
+ kfree(c->jheads[i].wbuf.buf);
+ kfree(c->jheads[i].wbuf.inodes);
+ kfree(c->jheads[i].log_hash);
+ }
+ kfree(c->jheads);
+ c->jheads = NULL;
+ }
+}
+
+/**
+ * free_orphans - free orphans.
+ * @c: UBIFS file-system description object
+ */
+static void free_orphans(struct ubifs_info *c)
+{
+ struct ubifs_orphan *orph;
+
+ while (c->orph_dnext) {
+ orph = c->orph_dnext;
+ c->orph_dnext = orph->dnext;
+ list_del(&orph->list);
+ kfree(orph);
+ }
+
+ while (!list_empty(&c->orph_list)) {
+ orph = list_entry(c->orph_list.next, struct ubifs_orphan, list);
+ list_del(&orph->list);
+ kfree(orph);
+ ubifs_err(c, "orphan list not empty at unmount");
+ }
+
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+}
+
+/**
+ * free_buds - free per-bud objects.
+ * @c: UBIFS file-system description object
+ */
+static void free_buds(struct ubifs_info *c)
+{
+ struct ubifs_bud *bud, *n;
+
+ rbtree_postorder_for_each_entry_safe(bud, n, &c->buds, rb) {
+ kfree(bud->log_hash);
+ kfree(bud);
+ }
+}
+
+/**
+ * check_volume_empty - check if the UBI volume is empty.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks if the UBIFS volume is empty by looking if its LEBs are
+ * mapped or not. The result of checking is stored in the @c->empty variable.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int check_volume_empty(struct ubifs_info *c)
+{
+ int lnum, err;
+
+ c->empty = 1;
+ for (lnum = 0; lnum < c->leb_cnt; lnum++) {
+ err = ubifs_is_mapped(c, lnum);
+ if (unlikely(err < 0))
+ return err;
+ if (err == 1) {
+ c->empty = 0;
+ break;
+ }
+
+ cond_resched();
+ }
+
+ return 0;
+}
+
+/*
+ * UBIFS mount options.
+ *
+ * Opt_fast_unmount: do not run a journal commit before un-mounting
+ * Opt_norm_unmount: run a journal commit before un-mounting
+ * Opt_bulk_read: enable bulk-reads
+ * Opt_no_bulk_read: disable bulk-reads
+ * Opt_chk_data_crc: check CRCs when reading data nodes
+ * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
+ * Opt_override_compr: override default compressor
+ * Opt_assert: set ubifs_assert() action
+ * Opt_auth_key: The key name used for authentication
+ * Opt_auth_hash_name: The hash type used for authentication
+ * Opt_err: just end of array marker
+ */
+enum {
+ Opt_fast_unmount,
+ Opt_norm_unmount,
+ Opt_bulk_read,
+ Opt_no_bulk_read,
+ Opt_chk_data_crc,
+ Opt_no_chk_data_crc,
+ Opt_override_compr,
+ Opt_assert,
+ Opt_auth_key,
+ Opt_auth_hash_name,
+ Opt_ignore,
+ Opt_err,
+};
+
+static const match_table_t tokens = {
+ {Opt_fast_unmount, "fast_unmount"},
+ {Opt_norm_unmount, "norm_unmount"},
+ {Opt_bulk_read, "bulk_read"},
+ {Opt_no_bulk_read, "no_bulk_read"},
+ {Opt_chk_data_crc, "chk_data_crc"},
+ {Opt_no_chk_data_crc, "no_chk_data_crc"},
+ {Opt_override_compr, "compr=%s"},
+ {Opt_auth_key, "auth_key=%s"},
+ {Opt_auth_hash_name, "auth_hash_name=%s"},
+ {Opt_ignore, "ubi=%s"},
+ {Opt_ignore, "vol=%s"},
+ {Opt_assert, "assert=%s"},
+ {Opt_err, NULL},
+};
+
+/**
+ * parse_standard_option - parse a standard mount option.
+ * @option: the option to parse
+ *
+ * Normally, standard mount options like "sync" are passed to file-systems as
+ * flags. However, when a "rootflags=" kernel boot parameter is used, they may
+ * be present in the options string. This function tries to deal with this
+ * situation and parse standard options. Returns 0 if the option was not
+ * recognized, and the corresponding integer flag if it was.
+ *
+ * UBIFS is only interested in the "sync" option, so do not check for anything
+ * else.
+ */
+static int parse_standard_option(const char *option)
+{
+
+ pr_notice("UBIFS: parse %s\n", option);
+ if (!strcmp(option, "sync"))
+ return SB_SYNCHRONOUS;
+ return 0;
+}
+
+/**
+ * ubifs_parse_options - parse mount parameters.
+ * @c: UBIFS file-system description object
+ * @options: parameters to parse
+ * @is_remount: non-zero if this is FS re-mount
+ *
+ * This function parses UBIFS mount options and returns zero in case success
+ * and a negative error code in case of failure.
+ */
+static int ubifs_parse_options(struct ubifs_info *c, char *options,
+ int is_remount)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ","))) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ /*
+ * %Opt_fast_unmount and %Opt_norm_unmount options are ignored.
+ * We accept them in order to be backward-compatible. But this
+ * should be removed at some point.
+ */
+ case Opt_fast_unmount:
+ c->mount_opts.unmount_mode = 2;
+ break;
+ case Opt_norm_unmount:
+ c->mount_opts.unmount_mode = 1;
+ break;
+ case Opt_bulk_read:
+ c->mount_opts.bulk_read = 2;
+ c->bulk_read = 1;
+ break;
+ case Opt_no_bulk_read:
+ c->mount_opts.bulk_read = 1;
+ c->bulk_read = 0;
+ break;
+ case Opt_chk_data_crc:
+ c->mount_opts.chk_data_crc = 2;
+ c->no_chk_data_crc = 0;
+ break;
+ case Opt_no_chk_data_crc:
+ c->mount_opts.chk_data_crc = 1;
+ c->no_chk_data_crc = 1;
+ break;
+ case Opt_override_compr:
+ {
+ char *name = match_strdup(&args[0]);
+
+ if (!name)
+ return -ENOMEM;
+ if (!strcmp(name, "none"))
+ c->mount_opts.compr_type = UBIFS_COMPR_NONE;
+ else if (!strcmp(name, "lzo"))
+ c->mount_opts.compr_type = UBIFS_COMPR_LZO;
+ else if (!strcmp(name, "zlib"))
+ c->mount_opts.compr_type = UBIFS_COMPR_ZLIB;
+ else if (!strcmp(name, "zstd"))
+ c->mount_opts.compr_type = UBIFS_COMPR_ZSTD;
+ else {
+ ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready?
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ c->mount_opts.override_compr = 1;
+ c->default_compr = c->mount_opts.compr_type;
+ break;
+ }
+ case Opt_assert:
+ {
+ char *act = match_strdup(&args[0]);
+
+ if (!act)
+ return -ENOMEM;
+ if (!strcmp(act, "report"))
+ c->assert_action = ASSACT_REPORT;
+ else if (!strcmp(act, "read-only"))
+ c->assert_action = ASSACT_RO;
+ else if (!strcmp(act, "panic"))
+ c->assert_action = ASSACT_PANIC;
+ else {
+ ubifs_err(c, "unknown assert action \"%s\"", act);
+ kfree(act);
+ return -EINVAL;
+ }
+ kfree(act);
+ break;
+ }
+ case Opt_auth_key:
+ if (!is_remount) {
+ c->auth_key_name = kstrdup(args[0].from,
+ GFP_KERNEL);
+ if (!c->auth_key_name)
+ return -ENOMEM;
+ }
+ break;
+ case Opt_auth_hash_name:
+ if (!is_remount) {
+ c->auth_hash_name = kstrdup(args[0].from,
+ GFP_KERNEL);
+ if (!c->auth_hash_name)
+ return -ENOMEM;
+ }
+ break;
+ case Opt_ignore:
+ break;
+ default:
+ {
+ unsigned long flag;
+ struct super_block *sb = c->vfs_sb;
+
+ flag = parse_standard_option(p);
+ if (!flag) {
+ ubifs_err(c, "unrecognized mount option \"%s\" or missing value",
+ p);
+ return -EINVAL;
+ }
+ sb->s_flags |= flag;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * ubifs_release_options - release mount parameters which have been dumped.
+ * @c: UBIFS file-system description object
+ */
+static void ubifs_release_options(struct ubifs_info *c)
+{
+ kfree(c->auth_key_name);
+ c->auth_key_name = NULL;
+ kfree(c->auth_hash_name);
+ c->auth_hash_name = NULL;
+}
+
+/**
+ * destroy_journal - destroy journal data structures.
+ * @c: UBIFS file-system description object
+ *
+ * This function destroys journal data structures including those that may have
+ * been created by recovery functions.
+ */
+static void destroy_journal(struct ubifs_info *c)
+{
+ while (!list_empty(&c->unclean_leb_list)) {
+ struct ubifs_unclean_leb *ucleb;
+
+ ucleb = list_entry(c->unclean_leb_list.next,
+ struct ubifs_unclean_leb, list);
+ list_del(&ucleb->list);
+ kfree(ucleb);
+ }
+ while (!list_empty(&c->old_buds)) {
+ struct ubifs_bud *bud;
+
+ bud = list_entry(c->old_buds.next, struct ubifs_bud, list);
+ list_del(&bud->list);
+ kfree(bud->log_hash);
+ kfree(bud);
+ }
+ ubifs_destroy_idx_gc(c);
+ ubifs_destroy_size_tree(c);
+ ubifs_tnc_close(c);
+ free_buds(c);
+}
+
+/**
+ * bu_init - initialize bulk-read information.
+ * @c: UBIFS file-system description object
+ */
+static void bu_init(struct ubifs_info *c)
+{
+ ubifs_assert(c, c->bulk_read == 1);
+
+ if (c->bu.buf)
+ return; /* Already initialized */
+
+again:
+ c->bu.buf = kmalloc(c->max_bu_buf_len, GFP_KERNEL | __GFP_NOWARN);
+ if (!c->bu.buf) {
+ if (c->max_bu_buf_len > UBIFS_KMALLOC_OK) {
+ c->max_bu_buf_len = UBIFS_KMALLOC_OK;
+ goto again;
+ }
+
+ /* Just disable bulk-read */
+ ubifs_warn(c, "cannot allocate %d bytes of memory for bulk-read, disabling it",
+ c->max_bu_buf_len);
+ c->mount_opts.bulk_read = 1;
+ c->bulk_read = 0;
+ return;
+ }
+}
+
+/**
+ * check_free_space - check if there is enough free space to mount.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure UBIFS has enough free space to be mounted in
+ * read/write mode. UBIFS must always have some free space to allow deletions.
+ */
+static int check_free_space(struct ubifs_info *c)
+{
+ ubifs_assert(c, c->dark_wm > 0);
+ if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) {
+ ubifs_err(c, "insufficient free space to mount in R/W mode");
+ ubifs_dump_budg(c, &c->bi);
+ ubifs_dump_lprops(c);
+ return -ENOSPC;
+ }
+ return 0;
+}
+
+/**
+ * mount_ubifs - mount UBIFS file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function mounts UBIFS file system. Returns zero in case of success and
+ * a negative error code in case of failure.
+ */
+static int mount_ubifs(struct ubifs_info *c)
+{
+ int err;
+ long long x, y;
+ size_t sz;
+
+ c->ro_mount = !!sb_rdonly(c->vfs_sb);
+ /* Suppress error messages while probing if SB_SILENT is set */
+ c->probing = !!(c->vfs_sb->s_flags & SB_SILENT);
+
+ err = init_constants_early(c);
+ if (err)
+ return err;
+
+ err = ubifs_debugging_init(c);
+ if (err)
+ return err;
+
+ err = ubifs_sysfs_register(c);
+ if (err)
+ goto out_debugging;
+
+ err = check_volume_empty(c);
+ if (err)
+ goto out_free;
+
+ if (c->empty && (c->ro_mount || c->ro_media)) {
+ /*
+ * This UBI volume is empty, and read-only, or the file system
+ * is mounted read-only - we cannot format it.
+ */
+ ubifs_err(c, "can't format empty UBI volume: read-only %s",
+ c->ro_media ? "UBI volume" : "mount");
+ err = -EROFS;
+ goto out_free;
+ }
+
+ if (c->ro_media && !c->ro_mount) {
+ ubifs_err(c, "cannot mount read-write - read-only media");
+ err = -EROFS;
+ goto out_free;
+ }
+
+ /*
+ * The requirement for the buffer is that it should fit indexing B-tree
+ * height amount of integers. We assume the height if the TNC tree will
+ * never exceed 64.
+ */
+ err = -ENOMEM;
+ c->bottom_up_buf = kmalloc_array(BOTTOM_UP_HEIGHT, sizeof(int),
+ GFP_KERNEL);
+ if (!c->bottom_up_buf)
+ goto out_free;
+
+ c->sbuf = vmalloc(c->leb_size);
+ if (!c->sbuf)
+ goto out_free;
+
+ if (!c->ro_mount) {
+ c->ileb_buf = vmalloc(c->leb_size);
+ if (!c->ileb_buf)
+ goto out_free;
+ }
+
+ if (c->bulk_read == 1)
+ bu_init(c);
+
+ if (!c->ro_mount) {
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
+ UBIFS_CIPHER_BLOCK_SIZE,
+ GFP_KERNEL);
+ if (!c->write_reserve_buf)
+ goto out_free;
+ }
+
+ c->mounting = 1;
+
+ if (c->auth_key_name) {
+ if (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) {
+ err = ubifs_init_authentication(c);
+ if (err)
+ goto out_free;
+ } else {
+ ubifs_err(c, "auth_key_name, but UBIFS is built without"
+ " authentication support");
+ err = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ err = ubifs_read_superblock(c);
+ if (err)
+ goto out_auth;
+
+ c->probing = 0;
+
+ /*
+ * Make sure the compressor which is set as default in the superblock
+ * or overridden by mount options is actually compiled in.
+ */
+ if (!ubifs_compr_present(c, c->default_compr)) {
+ ubifs_err(c, "'compressor \"%s\" is not compiled in",
+ ubifs_compr_name(c, c->default_compr));
+ err = -ENOTSUPP;
+ goto out_auth;
+ }
+
+ err = init_constants_sb(c);
+ if (err)
+ goto out_auth;
+
+ sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2;
+ c->cbuf = kmalloc(sz, GFP_NOFS);
+ if (!c->cbuf) {
+ err = -ENOMEM;
+ goto out_auth;
+ }
+
+ err = alloc_wbufs(c);
+ if (err)
+ goto out_cbuf;
+
+ sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id);
+ if (!c->ro_mount) {
+ /* Create background thread */
+ c->bgt = kthread_run(ubifs_bg_thread, c, "%s", c->bgt_name);
+ if (IS_ERR(c->bgt)) {
+ err = PTR_ERR(c->bgt);
+ c->bgt = NULL;
+ ubifs_err(c, "cannot spawn \"%s\", error %d",
+ c->bgt_name, err);
+ goto out_wbufs;
+ }
+ }
+
+ err = ubifs_read_master(c);
+ if (err)
+ goto out_master;
+
+ init_constants_master(c);
+
+ if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) {
+ ubifs_msg(c, "recovery needed");
+ c->need_recovery = 1;
+ }
+
+ if (c->need_recovery && !c->ro_mount) {
+ err = ubifs_recover_inl_heads(c, c->sbuf);
+ if (err)
+ goto out_master;
+ }
+
+ err = ubifs_lpt_init(c, 1, !c->ro_mount);
+ if (err)
+ goto out_master;
+
+ if (!c->ro_mount && c->space_fixup) {
+ err = ubifs_fixup_free_space(c);
+ if (err)
+ goto out_lpt;
+ }
+
+ if (!c->ro_mount && !c->need_recovery) {
+ /*
+ * Set the "dirty" flag so that if we reboot uncleanly we
+ * will notice this immediately on the next mount.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = ubifs_write_master(c);
+ if (err)
+ goto out_lpt;
+ }
+
+ /*
+ * Handle offline signed images: Now that the master node is
+ * written and its validation no longer depends on the hash
+ * in the superblock, we can update the offline signed
+ * superblock with a HMAC version,
+ */
+ if (ubifs_authenticated(c) && ubifs_hmac_zero(c, c->sup_node->hmac)) {
+ err = ubifs_hmac_wkm(c, c->sup_node->hmac_wkm);
+ if (err)
+ goto out_lpt;
+ c->superblock_need_write = 1;
+ }
+
+ if (!c->ro_mount && c->superblock_need_write) {
+ err = ubifs_write_sb_node(c, c->sup_node);
+ if (err)
+ goto out_lpt;
+ c->superblock_need_write = 0;
+ }
+
+ err = dbg_check_idx_size(c, c->bi.old_idx_sz);
+ if (err)
+ goto out_lpt;
+
+ err = ubifs_replay_journal(c);
+ if (err)
+ goto out_journal;
+
+ /* Calculate 'min_idx_lebs' after journal replay */
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+ err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount);
+ if (err)
+ goto out_orphans;
+
+ if (!c->ro_mount) {
+ int lnum;
+
+ err = check_free_space(c);
+ if (err)
+ goto out_orphans;
+
+ /* Check for enough log space */
+ lnum = c->lhead_lnum + 1;
+ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ lnum = UBIFS_LOG_LNUM;
+ if (lnum == c->ltail_lnum) {
+ err = ubifs_consolidate_log(c);
+ if (err)
+ goto out_orphans;
+ }
+
+ if (c->need_recovery) {
+ if (!ubifs_authenticated(c)) {
+ err = ubifs_recover_size(c, true);
+ if (err)
+ goto out_orphans;
+ }
+
+ err = ubifs_rcvry_gc_commit(c);
+ if (err)
+ goto out_orphans;
+
+ if (ubifs_authenticated(c)) {
+ err = ubifs_recover_size(c, false);
+ if (err)
+ goto out_orphans;
+ }
+ } else {
+ err = take_gc_lnum(c);
+ if (err)
+ goto out_orphans;
+
+ /*
+ * GC LEB may contain garbage if there was an unclean
+ * reboot, and it should be un-mapped.
+ */
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ goto out_orphans;
+ }
+
+ err = dbg_check_lprops(c);
+ if (err)
+ goto out_orphans;
+ } else if (c->need_recovery) {
+ err = ubifs_recover_size(c, false);
+ if (err)
+ goto out_orphans;
+ } else {
+ /*
+ * Even if we mount read-only, we have to set space in GC LEB
+ * to proper value because this affects UBIFS free space
+ * reporting. We do not want to have a situation when
+ * re-mounting from R/O to R/W changes amount of free space.
+ */
+ err = take_gc_lnum(c);
+ if (err)
+ goto out_orphans;
+ }
+
+ spin_lock(&ubifs_infos_lock);
+ list_add_tail(&c->infos_list, &ubifs_infos);
+ spin_unlock(&ubifs_infos_lock);
+
+ if (c->need_recovery) {
+ if (c->ro_mount)
+ ubifs_msg(c, "recovery deferred");
+ else {
+ c->need_recovery = 0;
+ ubifs_msg(c, "recovery completed");
+ /*
+ * GC LEB has to be empty and taken at this point. But
+ * the journal head LEBs may also be accounted as
+ * "empty taken" if they are empty.
+ */
+ ubifs_assert(c, c->lst.taken_empty_lebs > 0);
+ }
+ } else
+ ubifs_assert(c, c->lst.taken_empty_lebs > 0);
+
+ err = dbg_check_filesystem(c);
+ if (err)
+ goto out_infos;
+
+ dbg_debugfs_init_fs(c);
+
+ c->mounting = 0;
+
+ ubifs_msg(c, "UBIFS: mounted UBI device %d, volume %d, name \"%s\"%s",
+ c->vi.ubi_num, c->vi.vol_id, c->vi.name,
+ c->ro_mount ? ", R/O mode" : "");
+ x = (long long)c->main_lebs * c->leb_size;
+ y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes;
+ ubifs_msg(c, "LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes",
+ c->leb_size, c->leb_size >> 10, c->min_io_size,
+ c->max_write_size);
+ ubifs_msg(c, "FS size: %lld bytes (%lld MiB, %d LEBs), max %d LEBs, journal size %lld bytes (%lld MiB, %d LEBs)",
+ x, x >> 20, c->main_lebs, c->max_leb_cnt,
+ y, y >> 20, c->log_lebs + c->max_bud_cnt);
+ ubifs_msg(c, "reserved for root: %llu bytes (%llu KiB)",
+ c->report_rp_size, c->report_rp_size >> 10);
+ ubifs_msg(c, "media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION, c->uuid,
+ c->big_lpt ? ", big LPT model" : ", small LPT model");
+
+ dbg_gen("default compressor: %s", ubifs_compr_name(c, c->default_compr));
+ dbg_gen("data journal heads: %d",
+ c->jhead_cnt - NONDATA_JHEADS_CNT);
+ dbg_gen("log LEBs: %d (%d - %d)",
+ c->log_lebs, UBIFS_LOG_LNUM, c->log_last);
+ dbg_gen("LPT area LEBs: %d (%d - %d)",
+ c->lpt_lebs, c->lpt_first, c->lpt_last);
+ dbg_gen("orphan area LEBs: %d (%d - %d)",
+ c->orph_lebs, c->orph_first, c->orph_last);
+ dbg_gen("main area LEBs: %d (%d - %d)",
+ c->main_lebs, c->main_first, c->leb_cnt - 1);
+ dbg_gen("index LEBs: %d", c->lst.idx_lebs);
+ dbg_gen("total index bytes: %llu (%llu KiB, %llu MiB)",
+ c->bi.old_idx_sz, c->bi.old_idx_sz >> 10,
+ c->bi.old_idx_sz >> 20);
+ dbg_gen("key hash type: %d", c->key_hash_type);
+ dbg_gen("tree fanout: %d", c->fanout);
+ dbg_gen("reserved GC LEB: %d", c->gc_lnum);
+ dbg_gen("max. znode size %d", c->max_znode_sz);
+ dbg_gen("max. index node size %d", c->max_idx_node_sz);
+ dbg_gen("node sizes: data %zu, inode %zu, dentry %zu",
+ UBIFS_DATA_NODE_SZ, UBIFS_INO_NODE_SZ, UBIFS_DENT_NODE_SZ);
+ dbg_gen("node sizes: trun %zu, sb %zu, master %zu",
+ UBIFS_TRUN_NODE_SZ, UBIFS_SB_NODE_SZ, UBIFS_MST_NODE_SZ);
+ dbg_gen("node sizes: ref %zu, cmt. start %zu, orph %zu",
+ UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ);
+ dbg_gen("max. node sizes: data %zu, inode %zu dentry %zu, idx %d",
+ UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ,
+ UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout));
+ dbg_gen("dead watermark: %d", c->dead_wm);
+ dbg_gen("dark watermark: %d", c->dark_wm);
+ dbg_gen("LEB overhead: %d", c->leb_overhead);
+ x = (long long)c->main_lebs * c->dark_wm;
+ dbg_gen("max. dark space: %lld (%lld KiB, %lld MiB)",
+ x, x >> 10, x >> 20);
+ dbg_gen("maximum bud bytes: %lld (%lld KiB, %lld MiB)",
+ c->max_bud_bytes, c->max_bud_bytes >> 10,
+ c->max_bud_bytes >> 20);
+ dbg_gen("BG commit bud bytes: %lld (%lld KiB, %lld MiB)",
+ c->bg_bud_bytes, c->bg_bud_bytes >> 10,
+ c->bg_bud_bytes >> 20);
+ dbg_gen("current bud bytes %lld (%lld KiB, %lld MiB)",
+ c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20);
+ dbg_gen("max. seq. number: %llu", c->max_sqnum);
+ dbg_gen("commit number: %llu", c->cmt_no);
+ dbg_gen("max. xattrs per inode: %d", ubifs_xattr_max_cnt(c));
+ dbg_gen("max orphans: %d", c->max_orphans);
+
+ return 0;
+
+out_infos:
+ spin_lock(&ubifs_infos_lock);
+ list_del(&c->infos_list);
+ spin_unlock(&ubifs_infos_lock);
+out_orphans:
+ free_orphans(c);
+out_journal:
+ destroy_journal(c);
+out_lpt:
+ ubifs_lpt_free(c, 0);
+out_master:
+ kfree(c->mst_node);
+ kfree(c->rcvrd_mst_node);
+ if (c->bgt)
+ kthread_stop(c->bgt);
+out_wbufs:
+ free_wbufs(c);
+out_cbuf:
+ kfree(c->cbuf);
+out_auth:
+ ubifs_exit_authentication(c);
+out_free:
+ kfree(c->write_reserve_buf);
+ kfree(c->bu.buf);
+ vfree(c->ileb_buf);
+ vfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ kfree(c->sup_node);
+ ubifs_sysfs_unregister(c);
+out_debugging:
+ ubifs_debugging_exit(c);
+ return err;
+}
+
+/**
+ * ubifs_umount - un-mount UBIFS file-system.
+ * @c: UBIFS file-system description object
+ *
+ * Note, this function is called to free allocated resourced when un-mounting,
+ * as well as free resources when an error occurred while we were half way
+ * through mounting (error path cleanup function). So it has to make sure the
+ * resource was actually allocated before freeing it.
+ */
+static void ubifs_umount(struct ubifs_info *c)
+{
+ dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num,
+ c->vi.vol_id);
+
+ dbg_debugfs_exit_fs(c);
+ spin_lock(&ubifs_infos_lock);
+ list_del(&c->infos_list);
+ spin_unlock(&ubifs_infos_lock);
+
+ if (c->bgt)
+ kthread_stop(c->bgt);
+
+ destroy_journal(c);
+ free_wbufs(c);
+ free_orphans(c);
+ ubifs_lpt_free(c, 0);
+ ubifs_exit_authentication(c);
+
+ ubifs_release_options(c);
+ kfree(c->cbuf);
+ kfree(c->rcvrd_mst_node);
+ kfree(c->mst_node);
+ kfree(c->write_reserve_buf);
+ kfree(c->bu.buf);
+ vfree(c->ileb_buf);
+ vfree(c->sbuf);
+ kfree(c->bottom_up_buf);
+ kfree(c->sup_node);
+ ubifs_debugging_exit(c);
+ ubifs_sysfs_unregister(c);
+}
+
+/**
+ * ubifs_remount_rw - re-mount in read-write mode.
+ * @c: UBIFS file-system description object
+ *
+ * UBIFS avoids allocating many unnecessary resources when mounted in read-only
+ * mode. This function allocates the needed resources and re-mounts UBIFS in
+ * read-write mode.
+ */
+static int ubifs_remount_rw(struct ubifs_info *c)
+{
+ int err, lnum;
+
+ if (c->rw_incompat) {
+ ubifs_err(c, "the file-system is not R/W-compatible");
+ ubifs_msg(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d",
+ c->fmt_version, c->ro_compat_version,
+ UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION);
+ return -EROFS;
+ }
+
+ mutex_lock(&c->umount_mutex);
+ dbg_save_space_info(c);
+ c->remounting_rw = 1;
+ c->ro_mount = 0;
+
+ if (c->space_fixup) {
+ err = ubifs_fixup_free_space(c);
+ if (err)
+ goto out;
+ }
+
+ err = check_free_space(c);
+ if (err)
+ goto out;
+
+ if (c->need_recovery) {
+ ubifs_msg(c, "completing deferred recovery");
+ err = ubifs_write_rcvrd_mst_node(c);
+ if (err)
+ goto out;
+ if (!ubifs_authenticated(c)) {
+ err = ubifs_recover_size(c, true);
+ if (err)
+ goto out;
+ }
+ err = ubifs_clean_lebs(c, c->sbuf);
+ if (err)
+ goto out;
+ err = ubifs_recover_inl_heads(c, c->sbuf);
+ if (err)
+ goto out;
+ } else {
+ /* A readonly mount is not allowed to have orphans */
+ ubifs_assert(c, c->tot_orphans == 0);
+ err = ubifs_clear_orphans(c);
+ if (err)
+ goto out;
+ }
+
+ if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) {
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = ubifs_write_master(c);
+ if (err)
+ goto out;
+ }
+
+ if (c->superblock_need_write) {
+ struct ubifs_sb_node *sup = c->sup_node;
+
+ err = ubifs_write_sb_node(c, sup);
+ if (err)
+ goto out;
+
+ c->superblock_need_write = 0;
+ }
+
+ c->ileb_buf = vmalloc(c->leb_size);
+ if (!c->ileb_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
+ UBIFS_CIPHER_BLOCK_SIZE, GFP_KERNEL);
+ if (!c->write_reserve_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = ubifs_lpt_init(c, 0, 1);
+ if (err)
+ goto out;
+
+ /* Create background thread */
+ c->bgt = kthread_run(ubifs_bg_thread, c, "%s", c->bgt_name);
+ if (IS_ERR(c->bgt)) {
+ err = PTR_ERR(c->bgt);
+ c->bgt = NULL;
+ ubifs_err(c, "cannot spawn \"%s\", error %d",
+ c->bgt_name, err);
+ goto out;
+ }
+
+ c->orph_buf = vmalloc(c->leb_size);
+ if (!c->orph_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Check for enough log space */
+ lnum = c->lhead_lnum + 1;
+ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs)
+ lnum = UBIFS_LOG_LNUM;
+ if (lnum == c->ltail_lnum) {
+ err = ubifs_consolidate_log(c);
+ if (err)
+ goto out;
+ }
+
+ if (c->need_recovery) {
+ err = ubifs_rcvry_gc_commit(c);
+ if (err)
+ goto out;
+
+ if (ubifs_authenticated(c)) {
+ err = ubifs_recover_size(c, false);
+ if (err)
+ goto out;
+ }
+ } else {
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ }
+ if (err)
+ goto out;
+
+ dbg_gen("re-mounted read-write");
+ c->remounting_rw = 0;
+
+ if (c->need_recovery) {
+ c->need_recovery = 0;
+ ubifs_msg(c, "deferred recovery completed");
+ } else {
+ /*
+ * Do not run the debugging space check if the were doing
+ * recovery, because when we saved the information we had the
+ * file-system in a state where the TNC and lprops has been
+ * modified in memory, but all the I/O operations (including a
+ * commit) were deferred. So the file-system was in
+ * "non-committed" state. Now the file-system is in committed
+ * state, and of course the amount of free space will change
+ * because, for example, the old index size was imprecise.
+ */
+ err = dbg_check_space_info(c);
+ }
+
+ mutex_unlock(&c->umount_mutex);
+ return err;
+
+out:
+ c->ro_mount = 1;
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+ kfree(c->write_reserve_buf);
+ c->write_reserve_buf = NULL;
+ vfree(c->ileb_buf);
+ c->ileb_buf = NULL;
+ ubifs_lpt_free(c, 1);
+ c->remounting_rw = 0;
+ mutex_unlock(&c->umount_mutex);
+ return err;
+}
+
+/**
+ * ubifs_remount_ro - re-mount in read-only mode.
+ * @c: UBIFS file-system description object
+ *
+ * We assume VFS has stopped writing. Possibly the background thread could be
+ * running a commit, however kthread_stop will wait in that case.
+ */
+static void ubifs_remount_ro(struct ubifs_info *c)
+{
+ int i, err;
+
+ ubifs_assert(c, !c->need_recovery);
+ ubifs_assert(c, !c->ro_mount);
+
+ mutex_lock(&c->umount_mutex);
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+
+ dbg_save_space_info(c);
+
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ ubifs_ro_mode(c, err);
+ }
+
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
+ err = ubifs_write_master(c);
+ if (err)
+ ubifs_ro_mode(c, err);
+
+ vfree(c->orph_buf);
+ c->orph_buf = NULL;
+ kfree(c->write_reserve_buf);
+ c->write_reserve_buf = NULL;
+ vfree(c->ileb_buf);
+ c->ileb_buf = NULL;
+ ubifs_lpt_free(c, 1);
+ c->ro_mount = 1;
+ err = dbg_check_space_info(c);
+ if (err)
+ ubifs_ro_mode(c, err);
+ mutex_unlock(&c->umount_mutex);
+}
+
+static void ubifs_put_super(struct super_block *sb)
+{
+ int i;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ ubifs_msg(c, "un-mount UBI device %d", c->vi.ubi_num);
+
+ /*
+ * The following asserts are only valid if there has not been a failure
+ * of the media. For example, there will be dirty inodes if we failed
+ * to write them back because of I/O errors.
+ */
+ if (!c->ro_error) {
+ ubifs_assert(c, c->bi.idx_growth == 0);
+ ubifs_assert(c, c->bi.dd_growth == 0);
+ ubifs_assert(c, c->bi.data_growth == 0);
+ }
+
+ /*
+ * The 'c->umount_lock' prevents races between UBIFS memory shrinker
+ * and file system un-mount. Namely, it prevents the shrinker from
+ * picking this superblock for shrinking - it will be just skipped if
+ * the mutex is locked.
+ */
+ mutex_lock(&c->umount_mutex);
+ if (!c->ro_mount) {
+ /*
+ * First of all kill the background thread to make sure it does
+ * not interfere with un-mounting and freeing resources.
+ */
+ if (c->bgt) {
+ kthread_stop(c->bgt);
+ c->bgt = NULL;
+ }
+
+ /*
+ * On fatal errors c->ro_error is set to 1, in which case we do
+ * not write the master node.
+ */
+ if (!c->ro_error) {
+ int err;
+
+ /* Synchronize write-buffers */
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ ubifs_ro_mode(c, err);
+ }
+
+ /*
+ * We are being cleanly unmounted which means the
+ * orphans were killed - indicate this in the master
+ * node. Also save the reserved GC LEB number.
+ */
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
+ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
+ err = ubifs_write_master(c);
+ if (err)
+ /*
+ * Recovery will attempt to fix the master area
+ * next mount, so we just print a message and
+ * continue to unmount normally.
+ */
+ ubifs_err(c, "failed to write master node, error %d",
+ err);
+ } else {
+ for (i = 0; i < c->jhead_cnt; i++)
+ /* Make sure write-buffer timers are canceled */
+ hrtimer_cancel(&c->jheads[i].wbuf.timer);
+ }
+ }
+
+ ubifs_umount(c);
+ ubi_close_volume(c->ubi);
+ mutex_unlock(&c->umount_mutex);
+}
+
+static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ int err;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ sync_filesystem(sb);
+ dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
+
+ err = ubifs_parse_options(c, data, 1);
+ if (err) {
+ ubifs_err(c, "invalid or unknown remount parameter");
+ return err;
+ }
+
+ if (c->ro_mount && !(*flags & SB_RDONLY)) {
+ if (c->ro_error) {
+ ubifs_msg(c, "cannot re-mount R/W due to prior errors");
+ return -EROFS;
+ }
+ if (c->ro_media) {
+ ubifs_msg(c, "cannot re-mount R/W - UBI volume is R/O");
+ return -EROFS;
+ }
+ err = ubifs_remount_rw(c);
+ if (err)
+ return err;
+ } else if (!c->ro_mount && (*flags & SB_RDONLY)) {
+ if (c->ro_error) {
+ ubifs_msg(c, "cannot re-mount R/O due to prior errors");
+ return -EROFS;
+ }
+ ubifs_remount_ro(c);
+ }
+
+ if (c->bulk_read == 1)
+ bu_init(c);
+ else {
+ dbg_gen("disable bulk-read");
+ mutex_lock(&c->bu_mutex);
+ kfree(c->bu.buf);
+ c->bu.buf = NULL;
+ mutex_unlock(&c->bu_mutex);
+ }
+
+ if (!c->need_recovery)
+ ubifs_assert(c, c->lst.taken_empty_lebs > 0);
+
+ return 0;
+}
+
+const struct super_operations ubifs_super_operations = {
+ .alloc_inode = ubifs_alloc_inode,
+ .free_inode = ubifs_free_inode,
+ .put_super = ubifs_put_super,
+ .write_inode = ubifs_write_inode,
+ .drop_inode = ubifs_drop_inode,
+ .evict_inode = ubifs_evict_inode,
+ .statfs = ubifs_statfs,
+ .dirty_inode = ubifs_dirty_inode,
+ .remount_fs = ubifs_remount_fs,
+ .show_options = ubifs_show_options,
+ .sync_fs = ubifs_sync_fs,
+};
+
+/**
+ * open_ubi - parse UBI device name string and open the UBI device.
+ * @name: UBI volume name
+ * @mode: UBI volume open mode
+ *
+ * The primary method of mounting UBIFS is by specifying the UBI volume
+ * character device node path. However, UBIFS may also be mounted without any
+ * character device node using one of the following methods:
+ *
+ * o ubiX_Y - mount UBI device number X, volume Y;
+ * o ubiY - mount UBI device number 0, volume Y;
+ * o ubiX:NAME - mount UBI device X, volume with name NAME;
+ * o ubi:NAME - mount UBI device 0, volume with name NAME.
+ *
+ * Alternative '!' separator may be used instead of ':' (because some shells
+ * like busybox may interpret ':' as an NFS host name separator). This function
+ * returns UBI volume description object in case of success and a negative
+ * error code in case of failure.
+ */
+static struct ubi_volume_desc *open_ubi(const char *name, int mode)
+{
+ struct ubi_volume_desc *ubi;
+ int dev, vol;
+ char *endptr;
+
+ if (!name || !*name)
+ return ERR_PTR(-EINVAL);
+
+ /* First, try to open using the device node path method */
+ ubi = ubi_open_volume_path(name, mode);
+ if (!IS_ERR(ubi))
+ return ubi;
+
+ /* Try the "nodev" method */
+ if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i')
+ return ERR_PTR(-EINVAL);
+
+ /* ubi:NAME method */
+ if ((name[3] == ':' || name[3] == '!') && name[4] != '\0')
+ return ubi_open_volume_nm(0, name + 4, mode);
+
+ if (!isdigit(name[3]))
+ return ERR_PTR(-EINVAL);
+
+ dev = simple_strtoul(name + 3, &endptr, 0);
+
+ /* ubiY method */
+ if (*endptr == '\0')
+ return ubi_open_volume(0, dev, mode);
+
+ /* ubiX_Y method */
+ if (*endptr == '_' && isdigit(endptr[1])) {
+ vol = simple_strtoul(endptr + 1, &endptr, 0);
+ if (*endptr != '\0')
+ return ERR_PTR(-EINVAL);
+ return ubi_open_volume(dev, vol, mode);
+ }
+
+ /* ubiX:NAME method */
+ if ((*endptr == ':' || *endptr == '!') && endptr[1] != '\0')
+ return ubi_open_volume_nm(dev, ++endptr, mode);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
+{
+ struct ubifs_info *c;
+
+ c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
+ if (c) {
+ spin_lock_init(&c->cnt_lock);
+ spin_lock_init(&c->cs_lock);
+ spin_lock_init(&c->buds_lock);
+ spin_lock_init(&c->space_lock);
+ spin_lock_init(&c->orphan_lock);
+ init_rwsem(&c->commit_sem);
+ mutex_init(&c->lp_mutex);
+ mutex_init(&c->tnc_mutex);
+ mutex_init(&c->log_mutex);
+ mutex_init(&c->umount_mutex);
+ mutex_init(&c->bu_mutex);
+ mutex_init(&c->write_reserve_mutex);
+ init_waitqueue_head(&c->cmt_wq);
+ c->buds = RB_ROOT;
+ c->old_idx = RB_ROOT;
+ c->size_tree = RB_ROOT;
+ c->orph_tree = RB_ROOT;
+ INIT_LIST_HEAD(&c->infos_list);
+ INIT_LIST_HEAD(&c->idx_gc);
+ INIT_LIST_HEAD(&c->replay_list);
+ INIT_LIST_HEAD(&c->replay_buds);
+ INIT_LIST_HEAD(&c->uncat_list);
+ INIT_LIST_HEAD(&c->empty_list);
+ INIT_LIST_HEAD(&c->freeable_list);
+ INIT_LIST_HEAD(&c->frdi_idx_list);
+ INIT_LIST_HEAD(&c->unclean_leb_list);
+ INIT_LIST_HEAD(&c->old_buds);
+ INIT_LIST_HEAD(&c->orph_list);
+ INIT_LIST_HEAD(&c->orph_new);
+ c->no_chk_data_crc = 1;
+ c->assert_action = ASSACT_RO;
+
+ c->highest_inum = UBIFS_FIRST_INO;
+ c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
+
+ ubi_get_volume_info(ubi, &c->vi);
+ ubi_get_device_info(c->vi.ubi_num, &c->di);
+ }
+ return c;
+}
+
+static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct ubifs_info *c = sb->s_fs_info;
+ struct inode *root;
+ int err;
+
+ c->vfs_sb = sb;
+ /* Re-open the UBI device in read-write mode */
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE);
+ if (IS_ERR(c->ubi)) {
+ err = PTR_ERR(c->ubi);
+ goto out;
+ }
+
+ err = ubifs_parse_options(c, data, 0);
+ if (err)
+ goto out_close;
+
+ /*
+ * UBIFS provides 'backing_dev_info' in order to disable read-ahead. For
+ * UBIFS, I/O is not deferred, it is done immediately in read_folio,
+ * which means the user would have to wait not just for their own I/O
+ * but the read-ahead I/O as well i.e. completely pointless.
+ *
+ * Read-ahead will be disabled because @sb->s_bdi->ra_pages is 0. Also
+ * @sb->s_bdi->capabilities are initialized to 0 so there won't be any
+ * writeback happening.
+ */
+ err = super_setup_bdi_name(sb, "ubifs_%d_%d", c->vi.ubi_num,
+ c->vi.vol_id);
+ if (err)
+ goto out_close;
+ sb->s_bdi->ra_pages = 0;
+ sb->s_bdi->io_pages = 0;
+
+ sb->s_fs_info = c;
+ sb->s_magic = UBIFS_SUPER_MAGIC;
+ sb->s_blocksize = UBIFS_BLOCK_SIZE;
+ sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT;
+ sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c);
+ if (c->max_inode_sz > MAX_LFS_FILESIZE)
+ sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
+ sb->s_op = &ubifs_super_operations;
+ sb->s_xattr = ubifs_xattr_handlers;
+ fscrypt_set_ops(sb, &ubifs_crypt_operations);
+
+ mutex_lock(&c->umount_mutex);
+ err = mount_ubifs(c);
+ if (err) {
+ ubifs_assert(c, err < 0);
+ goto out_unlock;
+ }
+
+ /* Read the root inode */
+ root = ubifs_iget(sb, UBIFS_ROOT_INO);
+ if (IS_ERR(root)) {
+ err = PTR_ERR(root);
+ goto out_umount;
+ }
+
+ sb->s_root = d_make_root(root);
+ if (!sb->s_root) {
+ err = -ENOMEM;
+ goto out_umount;
+ }
+
+ import_uuid(&sb->s_uuid, c->uuid);
+
+ mutex_unlock(&c->umount_mutex);
+ return 0;
+
+out_umount:
+ ubifs_umount(c);
+out_unlock:
+ mutex_unlock(&c->umount_mutex);
+out_close:
+ ubifs_release_options(c);
+ ubi_close_volume(c->ubi);
+out:
+ return err;
+}
+
+static int sb_test(struct super_block *sb, void *data)
+{
+ struct ubifs_info *c1 = data;
+ struct ubifs_info *c = sb->s_fs_info;
+
+ return c->vi.cdev == c1->vi.cdev;
+}
+
+static int sb_set(struct super_block *sb, void *data)
+{
+ sb->s_fs_info = data;
+ return set_anon_super(sb, NULL);
+}
+
+static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
+ const char *name, void *data)
+{
+ struct ubi_volume_desc *ubi;
+ struct ubifs_info *c;
+ struct super_block *sb;
+ int err;
+
+ dbg_gen("name %s, flags %#x", name, flags);
+
+ /*
+ * Get UBI device number and volume ID. Mount it read-only so far
+ * because this might be a new mount point, and UBI allows only one
+ * read-write user at a time.
+ */
+ ubi = open_ubi(name, UBI_READONLY);
+ if (IS_ERR(ubi)) {
+ if (!(flags & SB_SILENT))
+ pr_err("UBIFS error (pid: %d): cannot open \"%s\", error %d",
+ current->pid, name, (int)PTR_ERR(ubi));
+ return ERR_CAST(ubi);
+ }
+
+ c = alloc_ubifs_info(ubi);
+ if (!c) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+
+ dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
+
+ sb = sget(fs_type, sb_test, sb_set, flags, c);
+ if (IS_ERR(sb)) {
+ err = PTR_ERR(sb);
+ kfree(c);
+ goto out_close;
+ }
+
+ if (sb->s_root) {
+ struct ubifs_info *c1 = sb->s_fs_info;
+ kfree(c);
+ /* A new mount point for already mounted UBIFS */
+ dbg_gen("this ubi volume is already mounted");
+ if (!!(flags & SB_RDONLY) != c1->ro_mount) {
+ err = -EBUSY;
+ goto out_deact;
+ }
+ } else {
+ err = ubifs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
+ if (err)
+ goto out_deact;
+ /* We do not support atime */
+ sb->s_flags |= SB_ACTIVE;
+ if (IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
+ ubifs_msg(c, "full atime support is enabled.");
+ else
+ sb->s_flags |= SB_NOATIME;
+ }
+
+ /* 'fill_super()' opens ubi again so we must close it here */
+ ubi_close_volume(ubi);
+
+ return dget(sb->s_root);
+
+out_deact:
+ deactivate_locked_super(sb);
+out_close:
+ ubi_close_volume(ubi);
+ return ERR_PTR(err);
+}
+
+static void kill_ubifs_super(struct super_block *s)
+{
+ struct ubifs_info *c = s->s_fs_info;
+ kill_anon_super(s);
+ kfree(c);
+}
+
+static struct file_system_type ubifs_fs_type = {
+ .name = "ubifs",
+ .owner = THIS_MODULE,
+ .mount = ubifs_mount,
+ .kill_sb = kill_ubifs_super,
+};
+MODULE_ALIAS_FS("ubifs");
+
+/*
+ * Inode slab cache constructor.
+ */
+static void inode_slab_ctor(void *obj)
+{
+ struct ubifs_inode *ui = obj;
+ inode_init_once(&ui->vfs_inode);
+}
+
+static int __init ubifs_init(void)
+{
+ int err = -ENOMEM;
+
+ BUILD_BUG_ON(sizeof(struct ubifs_ch) != 24);
+
+ /* Make sure node sizes are 8-byte aligned */
+ BUILD_BUG_ON(UBIFS_CH_SZ & 7);
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_SB_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MST_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_REF_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_CS_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_ORPH_NODE_SZ & 7);
+
+ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ & 7);
+ BUILD_BUG_ON(UBIFS_MAX_NODE_SZ & 7);
+ BUILD_BUG_ON(MIN_WRITE_SZ & 7);
+
+ /* Check min. node size */
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ < MIN_WRITE_SZ);
+ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ < MIN_WRITE_SZ);
+
+ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ > UBIFS_MAX_NODE_SZ);
+ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ > UBIFS_MAX_NODE_SZ);
+
+ /* Defined node sizes */
+ BUILD_BUG_ON(UBIFS_SB_NODE_SZ != 4096);
+ BUILD_BUG_ON(UBIFS_MST_NODE_SZ != 512);
+ BUILD_BUG_ON(UBIFS_INO_NODE_SZ != 160);
+ BUILD_BUG_ON(UBIFS_REF_NODE_SZ != 64);
+
+ /*
+ * We use 2 bit wide bit-fields to store compression type, which should
+ * be amended if more compressors are added. The bit-fields are:
+ * @compr_type in 'struct ubifs_inode', @default_compr in
+ * 'struct ubifs_info' and @compr_type in 'struct ubifs_mount_opts'.
+ */
+ BUILD_BUG_ON(UBIFS_COMPR_TYPES_CNT > 4);
+
+ /*
+ * We require that PAGE_SIZE is greater-than-or-equal-to
+ * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2.
+ */
+ if (PAGE_SIZE < UBIFS_BLOCK_SIZE) {
+ pr_err("UBIFS error (pid %d): VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes",
+ current->pid, (unsigned int)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab",
+ sizeof(struct ubifs_inode), 0,
+ SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT |
+ SLAB_ACCOUNT, &inode_slab_ctor);
+ if (!ubifs_inode_slab)
+ return -ENOMEM;
+
+ ubifs_shrinker_info = shrinker_alloc(0, "ubifs-slab");
+ if (!ubifs_shrinker_info)
+ goto out_slab;
+
+ ubifs_shrinker_info->count_objects = ubifs_shrink_count;
+ ubifs_shrinker_info->scan_objects = ubifs_shrink_scan;
+
+ shrinker_register(ubifs_shrinker_info);
+
+ err = ubifs_compressors_init();
+ if (err)
+ goto out_shrinker;
+
+ dbg_debugfs_init();
+
+ err = ubifs_sysfs_init();
+ if (err)
+ goto out_dbg;
+
+ err = register_filesystem(&ubifs_fs_type);
+ if (err) {
+ pr_err("UBIFS error (pid %d): cannot register file system, error %d",
+ current->pid, err);
+ goto out_sysfs;
+ }
+ return 0;
+
+out_sysfs:
+ ubifs_sysfs_exit();
+out_dbg:
+ dbg_debugfs_exit();
+ ubifs_compressors_exit();
+out_shrinker:
+ shrinker_free(ubifs_shrinker_info);
+out_slab:
+ kmem_cache_destroy(ubifs_inode_slab);
+ return err;
+}
+/* late_initcall to let compressors initialize first */
+late_initcall(ubifs_init);
+
+static void __exit ubifs_exit(void)
+{
+ WARN_ON(!list_empty(&ubifs_infos));
+ WARN_ON(atomic_long_read(&ubifs_clean_zn_cnt) != 0);
+
+ dbg_debugfs_exit();
+ ubifs_sysfs_exit();
+ ubifs_compressors_exit();
+ shrinker_free(ubifs_shrinker_info);
+
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
+ kmem_cache_destroy(ubifs_inode_slab);
+ unregister_filesystem(&ubifs_fs_type);
+}
+module_exit(ubifs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(__stringify(UBIFS_VERSION));
+MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");
+MODULE_DESCRIPTION("UBIFS - UBI File System");
diff --git a/ubifs-utils/libubifs/tnc.c b/ubifs-utils/libubifs/tnc.c
new file mode 100644
index 00000000..0fabecd9
--- /dev/null
+++ b/ubifs-utils/libubifs/tnc.c
@@ -0,0 +1,3553 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file implements TNC (Tree Node Cache) which caches indexing nodes of
+ * the UBIFS B-tree.
+ *
+ * At the moment the locking rules of the TNC tree are quite simple and
+ * straightforward. We just have a mutex and lock it when we traverse the
+ * tree. If a znode is not in memory, we read it from flash while still having
+ * the mutex locked.
+ */
+
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include "ubifs.h"
+
+static int try_read_node(const struct ubifs_info *c, void *buf, int type,
+ struct ubifs_zbranch *zbr);
+static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_zbranch *zbr, void *node);
+
+/*
+ * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions.
+ * @NAME_LESS: name corresponding to the first argument is less than second
+ * @NAME_MATCHES: names match
+ * @NAME_GREATER: name corresponding to the second argument is greater than
+ * first
+ * @NOT_ON_MEDIA: node referred by zbranch does not exist on the media
+ *
+ * These constants were introduce to improve readability.
+ */
+enum {
+ NAME_LESS = 0,
+ NAME_MATCHES = 1,
+ NAME_GREATER = 2,
+ NOT_ON_MEDIA = 3,
+};
+
+static void do_insert_old_idx(struct ubifs_info *c,
+ struct ubifs_old_idx *old_idx)
+{
+ struct ubifs_old_idx *o;
+ struct rb_node **p, *parent = NULL;
+
+ p = &c->old_idx.rb_node;
+ while (*p) {
+ parent = *p;
+ o = rb_entry(parent, struct ubifs_old_idx, rb);
+ if (old_idx->lnum < o->lnum)
+ p = &(*p)->rb_left;
+ else if (old_idx->lnum > o->lnum)
+ p = &(*p)->rb_right;
+ else if (old_idx->offs < o->offs)
+ p = &(*p)->rb_left;
+ else if (old_idx->offs > o->offs)
+ p = &(*p)->rb_right;
+ else {
+ ubifs_err(c, "old idx added twice!");
+ kfree(old_idx);
+ return;
+ }
+ }
+ rb_link_node(&old_idx->rb, parent, p);
+ rb_insert_color(&old_idx->rb, &c->old_idx);
+}
+
+/**
+ * insert_old_idx - record an index node obsoleted since the last commit start.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of obsoleted index node
+ * @offs: offset of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ *
+ * For recovery, there must always be a complete intact version of the index on
+ * flash at all times. That is called the "old index". It is the index as at the
+ * time of the last successful commit. Many of the index nodes in the old index
+ * may be dirty, but they must not be erased until the next successful commit
+ * (at which point that index becomes the old index).
+ *
+ * That means that the garbage collection and the in-the-gaps method of
+ * committing must be able to determine if an index node is in the old index.
+ * Most of the old index nodes can be found by looking up the TNC using the
+ * 'lookup_znode()' function. However, some of the old index nodes may have
+ * been deleted from the current index or may have been changed so much that
+ * they cannot be easily found. In those cases, an entry is added to an RB-tree.
+ * That is what this function does. The RB-tree is ordered by LEB number and
+ * offset because they uniquely identify the old index node.
+ */
+static int insert_old_idx(struct ubifs_info *c, int lnum, int offs)
+{
+ struct ubifs_old_idx *old_idx;
+
+ old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS);
+ if (unlikely(!old_idx))
+ return -ENOMEM;
+ old_idx->lnum = lnum;
+ old_idx->offs = offs;
+ do_insert_old_idx(c, old_idx);
+
+ return 0;
+}
+
+/**
+ * insert_old_idx_znode - record a znode obsoleted since last commit start.
+ * @c: UBIFS file-system description object
+ * @znode: znode of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ */
+int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode)
+{
+ if (znode->parent) {
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->parent->zbranch[znode->iip];
+ if (zbr->len)
+ return insert_old_idx(c, zbr->lnum, zbr->offs);
+ } else
+ if (c->zroot.len)
+ return insert_old_idx(c, c->zroot.lnum,
+ c->zroot.offs);
+ return 0;
+}
+
+/**
+ * ins_clr_old_idx_znode - record a znode obsoleted since last commit start.
+ * @c: UBIFS file-system description object
+ * @znode: znode of obsoleted index node
+ *
+ * Returns %0 on success, and a negative error code on failure.
+ */
+static int ins_clr_old_idx_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int err;
+
+ if (znode->parent) {
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->parent->zbranch[znode->iip];
+ if (zbr->len) {
+ err = insert_old_idx(c, zbr->lnum, zbr->offs);
+ if (err)
+ return err;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+ }
+ } else
+ if (c->zroot.len) {
+ err = insert_old_idx(c, c->zroot.lnum, c->zroot.offs);
+ if (err)
+ return err;
+ c->zroot.lnum = 0;
+ c->zroot.offs = 0;
+ c->zroot.len = 0;
+ }
+ return 0;
+}
+
+/**
+ * destroy_old_idx - destroy the old_idx RB-tree.
+ * @c: UBIFS file-system description object
+ *
+ * During start commit, the old_idx RB-tree is used to avoid overwriting index
+ * nodes that were in the index last commit but have since been deleted. This
+ * is necessary for recovery i.e. the old index must be kept intact until the
+ * new index is successfully written. The old-idx RB-tree is used for the
+ * in-the-gaps method of writing index nodes and is destroyed every commit.
+ */
+void destroy_old_idx(struct ubifs_info *c)
+{
+ struct ubifs_old_idx *old_idx, *n;
+
+ rbtree_postorder_for_each_entry_safe(old_idx, n, &c->old_idx, rb)
+ kfree(old_idx);
+
+ c->old_idx = RB_ROOT;
+}
+
+/**
+ * copy_znode - copy a dirty znode.
+ * @c: UBIFS file-system description object
+ * @znode: znode to copy
+ *
+ * A dirty znode being committed may not be changed, so it is copied.
+ */
+static struct ubifs_znode *copy_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn;
+
+ zn = kmemdup(znode, c->max_znode_sz, GFP_NOFS);
+ if (unlikely(!zn))
+ return ERR_PTR(-ENOMEM);
+
+ zn->cnext = NULL;
+ __set_bit(DIRTY_ZNODE, &zn->flags);
+ __clear_bit(COW_ZNODE, &zn->flags);
+
+ return zn;
+}
+
+/**
+ * add_idx_dirt - add dirt due to a dirty znode.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of index node
+ * @dirt: size of index node
+ *
+ * This function updates lprops dirty space and the new size of the index.
+ */
+static int add_idx_dirt(struct ubifs_info *c, int lnum, int dirt)
+{
+ c->calc_idx_sz -= ALIGN(dirt, 8);
+ return ubifs_add_dirt(c, lnum, dirt);
+}
+
+/**
+ * replace_znode - replace old znode with new znode.
+ * @c: UBIFS file-system description object
+ * @new_zn: new znode
+ * @old_zn: old znode
+ * @zbr: the branch of parent znode
+ *
+ * Replace old znode with new znode in TNC.
+ */
+static void replace_znode(struct ubifs_info *c, struct ubifs_znode *new_zn,
+ struct ubifs_znode *old_zn, struct ubifs_zbranch *zbr)
+{
+ ubifs_assert(c, !ubifs_zn_obsolete(old_zn));
+ __set_bit(OBSOLETE_ZNODE, &old_zn->flags);
+
+ if (old_zn->level != 0) {
+ int i;
+ const int n = new_zn->child_cnt;
+
+ /* The children now have new parent */
+ for (i = 0; i < n; i++) {
+ struct ubifs_zbranch *child = &new_zn->zbranch[i];
+
+ if (child->znode)
+ child->znode->parent = new_zn;
+ }
+ }
+
+ zbr->znode = new_zn;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+
+ atomic_long_inc(&c->dirty_zn_cnt);
+}
+
+/**
+ * dirty_cow_znode - ensure a znode is not being committed.
+ * @c: UBIFS file-system description object
+ * @zbr: branch of znode to check
+ *
+ * Returns dirtied znode on success or negative error code on failure.
+ */
+static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr)
+{
+ struct ubifs_znode *znode = zbr->znode;
+ struct ubifs_znode *zn;
+ int err;
+
+ if (!ubifs_zn_cow(znode)) {
+ /* znode is not being committed */
+ if (!test_and_set_bit(DIRTY_ZNODE, &znode->flags)) {
+ atomic_long_inc(&c->dirty_zn_cnt);
+ atomic_long_dec(&c->clean_zn_cnt);
+ atomic_long_dec(&ubifs_clean_zn_cnt);
+ err = add_idx_dirt(c, zbr->lnum, zbr->len);
+ if (unlikely(err))
+ return ERR_PTR(err);
+ }
+ return znode;
+ }
+
+ zn = copy_znode(c, znode);
+ if (IS_ERR(zn))
+ return zn;
+
+ if (zbr->len) {
+ struct ubifs_old_idx *old_idx;
+
+ old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS);
+ if (unlikely(!old_idx)) {
+ err = -ENOMEM;
+ goto out;
+ }
+ old_idx->lnum = zbr->lnum;
+ old_idx->offs = zbr->offs;
+
+ err = add_idx_dirt(c, zbr->lnum, zbr->len);
+ if (err) {
+ kfree(old_idx);
+ goto out;
+ }
+
+ do_insert_old_idx(c, old_idx);
+ }
+
+ replace_znode(c, zn, znode, zbr);
+
+ return zn;
+
+out:
+ kfree(zn);
+ return ERR_PTR(err);
+}
+
+/**
+ * lnc_add - add a leaf node to the leaf node cache.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of leaf node
+ * @node: leaf node
+ *
+ * Leaf nodes are non-index nodes directory entry nodes or data nodes. The
+ * purpose of the leaf node cache is to save re-reading the same leaf node over
+ * and over again. Most things are cached by VFS, however the file system must
+ * cache directory entries for readdir and for resolving hash collisions. The
+ * present implementation of the leaf node cache is extremely simple, and
+ * allows for error returns that are not used but that may be needed if a more
+ * complex implementation is created.
+ *
+ * Note, this function does not add the @node object to LNC directly, but
+ * allocates a copy of the object and adds the copy to LNC. The reason for this
+ * is that @node has been allocated outside of the TNC subsystem and will be
+ * used with @c->tnc_mutex unlock upon return from the TNC subsystem. But LNC
+ * may be changed at any time, e.g. freed by the shrinker.
+ */
+static int lnc_add(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ const void *node)
+{
+ int err;
+ void *lnc_node;
+ const struct ubifs_dent_node *dent = node;
+
+ ubifs_assert(c, !zbr->leaf);
+ ubifs_assert(c, zbr->len != 0);
+ ubifs_assert(c, is_hash_key(c, &zbr->key));
+
+ err = ubifs_validate_entry(c, dent);
+ if (err) {
+ dump_stack();
+ ubifs_dump_node(c, dent, zbr->len);
+ return err;
+ }
+
+ lnc_node = kmemdup(node, zbr->len, GFP_NOFS);
+ if (!lnc_node)
+ /* We don't have to have the cache, so no error */
+ return 0;
+
+ zbr->leaf = lnc_node;
+ return 0;
+}
+
+ /**
+ * lnc_add_directly - add a leaf node to the leaf-node-cache.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of leaf node
+ * @node: leaf node
+ *
+ * This function is similar to 'lnc_add()', but it does not create a copy of
+ * @node but inserts @node to TNC directly.
+ */
+static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ int err;
+
+ ubifs_assert(c, !zbr->leaf);
+ ubifs_assert(c, zbr->len != 0);
+
+ err = ubifs_validate_entry(c, node);
+ if (err) {
+ dump_stack();
+ ubifs_dump_node(c, node, zbr->len);
+ return err;
+ }
+
+ zbr->leaf = node;
+ return 0;
+}
+
+/**
+ * lnc_free - remove a leaf node from the leaf node cache.
+ * @zbr: zbranch of leaf node
+ */
+static void lnc_free(struct ubifs_zbranch *zbr)
+{
+ if (!zbr->leaf)
+ return;
+ kfree(zbr->leaf);
+ zbr->leaf = NULL;
+}
+
+/**
+ * tnc_read_hashed_node - read a "hashed" leaf node.
+ * @c: UBIFS file-system description object
+ * @zbr: key and position of the node
+ * @node: node is returned here
+ *
+ * This function reads a "hashed" node defined by @zbr from the leaf node cache
+ * (in it is there) or from the hash media, in which case the node is also
+ * added to LNC. Returns zero in case of success or a negative error
+ * code in case of failure.
+ */
+static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ int err;
+
+ ubifs_assert(c, is_hash_key(c, &zbr->key));
+
+ if (zbr->leaf) {
+ /* Read from the leaf node cache */
+ ubifs_assert(c, zbr->len != 0);
+ memcpy(node, zbr->leaf, zbr->len);
+ return 0;
+ }
+
+ if (c->replaying) {
+ err = fallible_read_node(c, &zbr->key, zbr, node);
+ /*
+ * When the node was not found, return -ENOENT, 0 otherwise.
+ * Negative return codes stay as-is.
+ */
+ if (err == 0)
+ err = -ENOENT;
+ else if (err == 1)
+ err = 0;
+ } else {
+ err = ubifs_tnc_read_node(c, zbr, node);
+ }
+ if (err)
+ return err;
+
+ /* Add the node to the leaf node cache */
+ err = lnc_add(c, zbr, node);
+ return err;
+}
+
+/**
+ * try_read_node - read a node if it is a node.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to read to
+ * @type: node type
+ * @zbr: the zbranch describing the node to read
+ *
+ * This function tries to read a node of known type and length, checks it and
+ * stores it in @buf. This function returns %1 if a node is present and %0 if
+ * a node is not present. A negative error code is returned for I/O errors.
+ * This function performs that same function as ubifs_read_node except that
+ * it does not require that there is actually a node present and instead
+ * the return code indicates if a node was read.
+ *
+ * Note, this function does not check CRC of data nodes if @c->no_chk_data_crc
+ * is true (it is controlled by corresponding mount option). However, if
+ * @c->mounting or @c->remounting_rw is true (we are mounting or re-mounting to
+ * R/W mode), @c->no_chk_data_crc is ignored and CRC is checked. This is
+ * because during mounting or re-mounting from R/O mode to R/W mode we may read
+ * journal nodes (when replying the journal or doing the recovery) and the
+ * journal nodes may potentially be corrupted, so checking is required.
+ */
+static int try_read_node(const struct ubifs_info *c, void *buf, int type,
+ struct ubifs_zbranch *zbr)
+{
+ int len = zbr->len;
+ int lnum = zbr->lnum;
+ int offs = zbr->offs;
+ int err, node_len;
+ struct ubifs_ch *ch = buf;
+ uint32_t crc, node_crc;
+
+ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
+
+ err = ubifs_leb_read(c, lnum, buf, offs, len, 1);
+ if (err) {
+ ubifs_err(c, "cannot read node type %d from LEB %d:%d, error %d",
+ type, lnum, offs, err);
+ return err;
+ }
+
+ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC)
+ return 0;
+
+ if (ch->node_type != type)
+ return 0;
+
+ node_len = le32_to_cpu(ch->len);
+ if (node_len != len)
+ return 0;
+
+ if (type != UBIFS_DATA_NODE || !c->no_chk_data_crc || c->mounting ||
+ c->remounting_rw) {
+ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
+ node_crc = le32_to_cpu(ch->crc);
+ if (crc != node_crc)
+ return 0;
+ }
+
+ err = ubifs_node_check_hash(c, buf, zbr->hash);
+ if (err) {
+ ubifs_bad_hash(c, buf, zbr->hash, lnum, offs);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * fallible_read_node - try to read a leaf node.
+ * @c: UBIFS file-system description object
+ * @key: key of node to read
+ * @zbr: position of node
+ * @node: node returned
+ *
+ * This function tries to read a node and returns %1 if the node is read, %0
+ * if the node is not present, and a negative error code in the case of error.
+ */
+static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_zbranch *zbr, void *node)
+{
+ int ret;
+
+ dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs);
+
+ ret = try_read_node(c, node, key_type(c, key), zbr);
+ if (ret == 1) {
+ union ubifs_key node_key;
+ struct ubifs_dent_node *dent = node;
+
+ /* All nodes have key in the same place */
+ key_read(c, &dent->key, &node_key);
+ if (keys_cmp(c, key, &node_key) != 0)
+ ret = 0;
+ }
+ if (ret == 0 && c->replaying)
+ dbg_mntk(key, "dangling branch LEB %d:%d len %d, key ",
+ zbr->lnum, zbr->offs, zbr->len);
+ return ret;
+}
+
+/**
+ * matches_name - determine if a direntry or xattr entry matches a given name.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of dent
+ * @nm: name to match
+ *
+ * This function checks if xentry/direntry referred by zbranch @zbr matches name
+ * @nm. Returns %NAME_MATCHES if it does, %NAME_LESS if the name referred by
+ * @zbr is less than @nm, and %NAME_GREATER if it is greater than @nm. In case
+ * of failure, a negative error code is returned.
+ */
+static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ const struct fscrypt_name *nm)
+{
+ struct ubifs_dent_node *dent;
+ int nlen, err;
+
+ /* If possible, match against the dent in the leaf node cache */
+ if (!zbr->leaf) {
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ err = ubifs_tnc_read_node(c, zbr, dent);
+ if (err)
+ goto out_free;
+
+ /* Add the node to the leaf node cache */
+ err = lnc_add_directly(c, zbr, dent);
+ if (err)
+ goto out_free;
+ } else
+ dent = zbr->leaf;
+
+ nlen = le16_to_cpu(dent->nlen);
+ err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm)));
+ if (err == 0) {
+ if (nlen == fname_len(nm))
+ return NAME_MATCHES;
+ else if (nlen < fname_len(nm))
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+ } else if (err < 0)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+
+out_free:
+ kfree(dent);
+ return err;
+}
+
+/**
+ * get_znode - get a TNC znode that may not be loaded yet.
+ * @c: UBIFS file-system description object
+ * @znode: parent znode
+ * @n: znode branch slot number
+ *
+ * This function returns the znode or a negative error code.
+ */
+static struct ubifs_znode *get_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode, int n)
+{
+ struct ubifs_zbranch *zbr;
+
+ zbr = &znode->zbranch[n];
+ if (zbr->znode)
+ znode = zbr->znode;
+ else
+ znode = ubifs_load_znode(c, zbr, znode, n);
+ return znode;
+}
+
+/**
+ * tnc_next - find next TNC entry.
+ * @c: UBIFS file-system description object
+ * @zn: znode is passed and returned here
+ * @n: znode branch slot number is passed and returned here
+ *
+ * This function returns %0 if the next TNC entry is found, %-ENOENT if there is
+ * no next entry, or a negative error code otherwise.
+ */
+static int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n)
+{
+ struct ubifs_znode *znode = *zn;
+ int nn = *n;
+
+ nn += 1;
+ if (nn < znode->child_cnt) {
+ *n = nn;
+ return 0;
+ }
+ while (1) {
+ struct ubifs_znode *zp;
+
+ zp = znode->parent;
+ if (!zp)
+ return -ENOENT;
+ nn = znode->iip + 1;
+ znode = zp;
+ if (nn < znode->child_cnt) {
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ while (znode->level != 0) {
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+ nn = 0;
+ break;
+ }
+ }
+ *zn = znode;
+ *n = nn;
+ return 0;
+}
+
+/**
+ * tnc_prev - find previous TNC entry.
+ * @c: UBIFS file-system description object
+ * @zn: znode is returned here
+ * @n: znode branch slot number is passed and returned here
+ *
+ * This function returns %0 if the previous TNC entry is found, %-ENOENT if
+ * there is no next entry, or a negative error code otherwise.
+ */
+static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n)
+{
+ struct ubifs_znode *znode = *zn;
+ int nn = *n;
+
+ if (nn > 0) {
+ *n = nn - 1;
+ return 0;
+ }
+ while (1) {
+ struct ubifs_znode *zp;
+
+ zp = znode->parent;
+ if (!zp)
+ return -ENOENT;
+ nn = znode->iip - 1;
+ znode = zp;
+ if (nn >= 0) {
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ while (znode->level != 0) {
+ nn = znode->child_cnt - 1;
+ znode = get_znode(c, znode, nn);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+ nn = znode->child_cnt - 1;
+ break;
+ }
+ }
+ *zn = znode;
+ *n = nn;
+ return 0;
+}
+
+/**
+ * resolve_collision - resolve a collision.
+ * @c: UBIFS file-system description object
+ * @key: key of a directory or extended attribute entry
+ * @zn: znode is returned here
+ * @n: zbranch number is passed and returned here
+ * @nm: name of the entry
+ *
+ * This function is called for "hashed" keys to make sure that the found key
+ * really corresponds to the looked up node (directory or extended attribute
+ * entry). It returns %1 and sets @zn and @n if the collision is resolved.
+ * %0 is returned if @nm is not found and @zn and @n are set to the previous
+ * entry, i.e. to the entry after which @nm could follow if it were in TNC.
+ * This means that @n may be set to %-1 if the leftmost key in @zn is the
+ * previous one. A negative error code is returned on failures.
+ */
+static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ const struct fscrypt_name *nm)
+{
+ int err;
+
+ err = matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (unlikely(err < 0))
+ return err;
+ if (err == NAME_MATCHES)
+ return 1;
+
+ if (err == NAME_GREATER) {
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, zn, n);
+ if (err == -ENOENT) {
+ ubifs_assert(c, *n == 0);
+ *n = -1;
+ return 0;
+ }
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) {
+ /*
+ * We have found the branch after which we would
+ * like to insert, but inserting in this znode
+ * may still be wrong. Consider the following 3
+ * znodes, in the case where we are resolving a
+ * collision with Key2.
+ *
+ * znode zp
+ * ----------------------
+ * level 1 | Key0 | Key1 |
+ * -----------------------
+ * | |
+ * znode za | | znode zb
+ * ------------ ------------
+ * level 0 | Key0 | | Key2 |
+ * ------------ ------------
+ *
+ * The lookup finds Key2 in znode zb. Lets say
+ * there is no match and the name is greater so
+ * we look left. When we find Key0, we end up
+ * here. If we return now, we will insert into
+ * znode za at slot n = 1. But that is invalid
+ * according to the parent's keys. Key2 must
+ * be inserted into znode zb.
+ *
+ * Note, this problem is not relevant for the
+ * case when we go right, because
+ * 'tnc_insert()' would correct the parent key.
+ */
+ if (*n == (*zn)->child_cnt - 1) {
+ err = tnc_next(c, zn, n);
+ if (err) {
+ /* Should be impossible */
+ ubifs_assert(c, 0);
+ if (err == -ENOENT)
+ err = -EINVAL;
+ return err;
+ }
+ ubifs_assert(c, *n == 0);
+ *n = -1;
+ }
+ return 0;
+ }
+ err = matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_LESS)
+ return 0;
+ if (err == NAME_MATCHES)
+ return 1;
+ ubifs_assert(c, err == NAME_GREATER);
+ }
+ } else {
+ int nn = *n;
+ struct ubifs_znode *znode = *zn;
+
+ /* Look right */
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ return 0;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ return 0;
+ err = matches_name(c, &znode->zbranch[nn], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_GREATER)
+ return 0;
+ *zn = znode;
+ *n = nn;
+ if (err == NAME_MATCHES)
+ return 1;
+ ubifs_assert(c, err == NAME_LESS);
+ }
+ }
+}
+
+/**
+ * fallible_matches_name - determine if a dent matches a given name.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of dent
+ * @nm: name to match
+ *
+ * This is a "fallible" version of 'matches_name()' function which does not
+ * panic if the direntry/xentry referred by @zbr does not exist on the media.
+ *
+ * This function checks if xentry/direntry referred by zbranch @zbr matches name
+ * @nm. Returns %NAME_MATCHES it does, %NAME_LESS if the name referred by @zbr
+ * is less than @nm, %NAME_GREATER if it is greater than @nm, and @NOT_ON_MEDIA
+ * if xentry/direntry referred by @zbr does not exist on the media. A negative
+ * error code is returned in case of failure.
+ */
+static int fallible_matches_name(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ const struct fscrypt_name *nm)
+{
+ struct ubifs_dent_node *dent;
+ int nlen, err;
+
+ /* If possible, match against the dent in the leaf node cache */
+ if (!zbr->leaf) {
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ err = fallible_read_node(c, &zbr->key, zbr, dent);
+ if (err < 0)
+ goto out_free;
+ if (err == 0) {
+ /* The node was not present */
+ err = NOT_ON_MEDIA;
+ goto out_free;
+ }
+ ubifs_assert(c, err == 1);
+
+ err = lnc_add_directly(c, zbr, dent);
+ if (err)
+ goto out_free;
+ } else
+ dent = zbr->leaf;
+
+ nlen = le16_to_cpu(dent->nlen);
+ err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm)));
+ if (err == 0) {
+ if (nlen == fname_len(nm))
+ return NAME_MATCHES;
+ else if (nlen < fname_len(nm))
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+ } else if (err < 0)
+ return NAME_LESS;
+ else
+ return NAME_GREATER;
+
+out_free:
+ kfree(dent);
+ return err;
+}
+
+/**
+ * fallible_resolve_collision - resolve a collision even if nodes are missing.
+ * @c: UBIFS file-system description object
+ * @key: key
+ * @zn: znode is returned here
+ * @n: branch number is passed and returned here
+ * @nm: name of directory entry
+ * @adding: indicates caller is adding a key to the TNC
+ *
+ * This is a "fallible" version of the 'resolve_collision()' function which
+ * does not panic if one of the nodes referred to by TNC does not exist on the
+ * media. This may happen when replaying the journal if a deleted node was
+ * Garbage-collected and the commit was not done. A branch that refers to a node
+ * that is not present is called a dangling branch. The following are the return
+ * codes for this function:
+ * o if @nm was found, %1 is returned and @zn and @n are set to the found
+ * branch;
+ * o if we are @adding and @nm was not found, %0 is returned;
+ * o if we are not @adding and @nm was not found, but a dangling branch was
+ * found, then %1 is returned and @zn and @n are set to the dangling branch;
+ * o a negative error code is returned in case of failure.
+ */
+static int fallible_resolve_collision(struct ubifs_info *c,
+ const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ const struct fscrypt_name *nm,
+ int adding)
+{
+ struct ubifs_znode *o_znode = NULL, *znode = *zn;
+ int o_n, err, cmp, unsure = 0, nn = *n;
+
+ cmp = fallible_matches_name(c, &znode->zbranch[nn], nm);
+ if (unlikely(cmp < 0))
+ return cmp;
+ if (cmp == NAME_MATCHES)
+ return 1;
+ if (cmp == NOT_ON_MEDIA) {
+ o_znode = znode;
+ o_n = nn;
+ /*
+ * We are unlucky and hit a dangling branch straight away.
+ * Now we do not really know where to go to find the needed
+ * branch - to the left or to the right. Well, let's try left.
+ */
+ unsure = 1;
+ } else if (!adding)
+ unsure = 1; /* Remove a dangling branch wherever it is */
+
+ if (cmp == NAME_GREATER || unsure) {
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, zn, n);
+ if (err == -ENOENT) {
+ ubifs_assert(c, *n == 0);
+ *n = -1;
+ break;
+ }
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) {
+ /* See comments in 'resolve_collision()' */
+ if (*n == (*zn)->child_cnt - 1) {
+ err = tnc_next(c, zn, n);
+ if (err) {
+ /* Should be impossible */
+ ubifs_assert(c, 0);
+ if (err == -ENOENT)
+ err = -EINVAL;
+ return err;
+ }
+ ubifs_assert(c, *n == 0);
+ *n = -1;
+ }
+ break;
+ }
+ err = fallible_matches_name(c, &(*zn)->zbranch[*n], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_MATCHES)
+ return 1;
+ if (err == NOT_ON_MEDIA) {
+ o_znode = *zn;
+ o_n = *n;
+ continue;
+ }
+ if (!adding)
+ continue;
+ if (err == NAME_LESS)
+ break;
+ else
+ unsure = 0;
+ }
+ }
+
+ if (cmp == NAME_LESS || unsure) {
+ /* Look right */
+ *zn = znode;
+ *n = nn;
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ break;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ break;
+ err = fallible_matches_name(c, &znode->zbranch[nn], nm);
+ if (err < 0)
+ return err;
+ if (err == NAME_GREATER)
+ break;
+ *zn = znode;
+ *n = nn;
+ if (err == NAME_MATCHES)
+ return 1;
+ if (err == NOT_ON_MEDIA) {
+ o_znode = znode;
+ o_n = nn;
+ }
+ }
+ }
+
+ /* Never match a dangling branch when adding */
+ if (adding || !o_znode)
+ return 0;
+
+ dbg_mntk(key, "dangling match LEB %d:%d len %d key ",
+ o_znode->zbranch[o_n].lnum, o_znode->zbranch[o_n].offs,
+ o_znode->zbranch[o_n].len);
+ *zn = o_znode;
+ *n = o_n;
+ return 1;
+}
+
+/**
+ * matches_position - determine if a zbranch matches a given position.
+ * @zbr: zbranch of dent
+ * @lnum: LEB number of dent to match
+ * @offs: offset of dent to match
+ *
+ * This function returns %1 if @lnum:@offs matches, and %0 otherwise.
+ */
+static int matches_position(struct ubifs_zbranch *zbr, int lnum, int offs)
+{
+ if (zbr->lnum == lnum && zbr->offs == offs)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * resolve_collision_directly - resolve a collision directly.
+ * @c: UBIFS file-system description object
+ * @key: key of directory entry
+ * @zn: znode is passed and returned here
+ * @n: zbranch number is passed and returned here
+ * @lnum: LEB number of dent node to match
+ * @offs: offset of dent node to match
+ *
+ * This function is used for "hashed" keys to make sure the found directory or
+ * extended attribute entry node is what was looked for. It is used when the
+ * flash address of the right node is known (@lnum:@offs) which makes it much
+ * easier to resolve collisions (no need to read entries and match full
+ * names). This function returns %1 and sets @zn and @n if the collision is
+ * resolved, %0 if @lnum:@offs is not found and @zn and @n are set to the
+ * previous directory entry. Otherwise a negative error code is returned.
+ */
+static int resolve_collision_directly(struct ubifs_info *c,
+ const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+ int nn, err;
+
+ znode = *zn;
+ nn = *n;
+ if (matches_position(&znode->zbranch[nn], lnum, offs))
+ return 1;
+
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, &znode, &nn);
+ if (err == -ENOENT)
+ break;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ break;
+ if (matches_position(&znode->zbranch[nn], lnum, offs)) {
+ *zn = znode;
+ *n = nn;
+ return 1;
+ }
+ }
+
+ /* Look right */
+ znode = *zn;
+ nn = *n;
+ while (1) {
+ err = tnc_next(c, &znode, &nn);
+ if (err == -ENOENT)
+ return 0;
+ if (err < 0)
+ return err;
+ if (keys_cmp(c, &znode->zbranch[nn].key, key))
+ return 0;
+ *zn = znode;
+ *n = nn;
+ if (matches_position(&znode->zbranch[nn], lnum, offs))
+ return 1;
+ }
+}
+
+/**
+ * dirty_cow_bottom_up - dirty a znode and its ancestors.
+ * @c: UBIFS file-system description object
+ * @znode: znode to dirty
+ *
+ * If we do not have a unique key that resides in a znode, then we cannot
+ * dirty that znode from the top down (i.e. by using lookup_level0_dirty)
+ * This function records the path back to the last dirty ancestor, and then
+ * dirties the znodes on that path.
+ */
+static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zp;
+ int *path = c->bottom_up_buf, p = 0;
+
+ ubifs_assert(c, c->zroot.znode);
+ ubifs_assert(c, znode);
+ if (c->zroot.znode->level > BOTTOM_UP_HEIGHT) {
+ kfree(c->bottom_up_buf);
+ c->bottom_up_buf = kmalloc_array(c->zroot.znode->level,
+ sizeof(int),
+ GFP_NOFS);
+ if (!c->bottom_up_buf)
+ return ERR_PTR(-ENOMEM);
+ path = c->bottom_up_buf;
+ }
+ if (c->zroot.znode->level) {
+ /* Go up until parent is dirty */
+ while (1) {
+ int n;
+
+ zp = znode->parent;
+ if (!zp)
+ break;
+ n = znode->iip;
+ ubifs_assert(c, p < c->zroot.znode->level);
+ path[p++] = n;
+ if (!zp->cnext && ubifs_zn_dirty(znode))
+ break;
+ znode = zp;
+ }
+ }
+
+ /* Come back down, dirtying as we go */
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ zp = znode->parent;
+ if (zp) {
+ ubifs_assert(c, path[p - 1] >= 0);
+ ubifs_assert(c, path[p - 1] < zp->child_cnt);
+ zbr = &zp->zbranch[path[--p]];
+ znode = dirty_cow_znode(c, zbr);
+ } else {
+ ubifs_assert(c, znode == c->zroot.znode);
+ znode = dirty_cow_znode(c, &c->zroot);
+ }
+ if (IS_ERR(znode) || !p)
+ break;
+ ubifs_assert(c, path[p - 1] >= 0);
+ ubifs_assert(c, path[p - 1] < znode->child_cnt);
+ znode = znode->zbranch[path[p - 1]].znode;
+ }
+
+ return znode;
+}
+
+/**
+ * ubifs_lookup_level0 - search for zero-level znode.
+ * @c: UBIFS file-system description object
+ * @key: key to lookup
+ * @zn: znode is returned here
+ * @n: znode branch slot number is returned here
+ *
+ * This function looks up the TNC tree and search for zero-level znode which
+ * refers key @key. The found zero-level znode is returned in @zn. There are 3
+ * cases:
+ * o exact match, i.e. the found zero-level znode contains key @key, then %1
+ * is returned and slot number of the matched branch is stored in @n;
+ * o not exact match, which means that zero-level znode does not contain
+ * @key, then %0 is returned and slot number of the closest branch or %-1
+ * is stored in @n; In this case calling tnc_next() is mandatory.
+ * o @key is so small that it is even less than the lowest key of the
+ * leftmost zero-level node, then %0 is returned and %0 is stored in @n.
+ *
+ * Note, when the TNC tree is traversed, some znodes may be absent, then this
+ * function reads corresponding indexing nodes and inserts them to TNC. In
+ * case of failure, a negative error code is returned.
+ */
+int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n)
+{
+ int err, exact;
+ struct ubifs_znode *znode;
+ time64_t time = ktime_get_seconds();
+
+ dbg_tnck(key, "search key ");
+ ubifs_assert(c, key_type(c, key) < UBIFS_INVALID_KEY);
+
+ znode = c->zroot.znode;
+ if (unlikely(!znode)) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ znode->time = time;
+
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ exact = ubifs_search_zbranch(c, znode, key, n);
+
+ if (znode->level == 0)
+ break;
+
+ if (*n < 0)
+ *n = 0;
+ zbr = &znode->zbranch[*n];
+
+ if (zbr->znode) {
+ znode->time = time;
+ znode = zbr->znode;
+ continue;
+ }
+
+ /* znode is not in TNC cache, load it from the media */
+ znode = ubifs_load_znode(c, zbr, znode, *n);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ *zn = znode;
+ if (exact || !is_hash_key(c, key) || *n != -1) {
+ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n);
+ return exact;
+ }
+
+ /*
+ * Here is a tricky place. We have not found the key and this is a
+ * "hashed" key, which may collide. The rest of the code deals with
+ * situations like this:
+ *
+ * | 3 | 5 |
+ * / \
+ * | 3 | 5 | | 6 | 7 | (x)
+ *
+ * Or more a complex example:
+ *
+ * | 1 | 5 |
+ * / \
+ * | 1 | 3 | | 5 | 8 |
+ * \ /
+ * | 5 | 5 | | 6 | 7 | (x)
+ *
+ * In the examples, if we are looking for key "5", we may reach nodes
+ * marked with "(x)". In this case what we have do is to look at the
+ * left and see if there is "5" key there. If there is, we have to
+ * return it.
+ *
+ * Note, this whole situation is possible because we allow to have
+ * elements which are equivalent to the next key in the parent in the
+ * children of current znode. For example, this happens if we split a
+ * znode like this: | 3 | 5 | 5 | 6 | 7 |, which results in something
+ * like this:
+ * | 3 | 5 |
+ * / \
+ * | 3 | 5 | | 5 | 6 | 7 |
+ * ^
+ * And this becomes what is at the first "picture" after key "5" marked
+ * with "^" is removed. What could be done is we could prohibit
+ * splitting in the middle of the colliding sequence. Also, when
+ * removing the leftmost key, we would have to correct the key of the
+ * parent node, which would introduce additional complications. Namely,
+ * if we changed the leftmost key of the parent znode, the garbage
+ * collector would be unable to find it (GC is doing this when GC'ing
+ * indexing LEBs). Although we already have an additional RB-tree where
+ * we save such changed znodes (see 'ins_clr_old_idx_znode()') until
+ * after the commit. But anyway, this does not look easy to implement
+ * so we did not try this.
+ */
+ err = tnc_prev(c, &znode, n);
+ if (err == -ENOENT) {
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ *n = -1;
+ return 0;
+ }
+ if (unlikely(err < 0))
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[*n].key)) {
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ *n = -1;
+ return 0;
+ }
+
+ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n);
+ *zn = znode;
+ return 1;
+}
+
+/**
+ * lookup_level0_dirty - search for zero-level znode dirtying.
+ * @c: UBIFS file-system description object
+ * @key: key to lookup
+ * @zn: znode is returned here
+ * @n: znode branch slot number is returned here
+ *
+ * This function looks up the TNC tree and search for zero-level znode which
+ * refers key @key. The found zero-level znode is returned in @zn. There are 3
+ * cases:
+ * o exact match, i.e. the found zero-level znode contains key @key, then %1
+ * is returned and slot number of the matched branch is stored in @n;
+ * o not exact match, which means that zero-level znode does not contain @key
+ * then %0 is returned and slot number of the closed branch is stored in
+ * @n;
+ * o @key is so small that it is even less than the lowest key of the
+ * leftmost zero-level node, then %0 is returned and %-1 is stored in @n.
+ *
+ * Additionally all znodes in the path from the root to the located zero-level
+ * znode are marked as dirty.
+ *
+ * Note, when the TNC tree is traversed, some znodes may be absent, then this
+ * function reads corresponding indexing nodes and inserts them to TNC. In
+ * case of failure, a negative error code is returned.
+ */
+static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n)
+{
+ int err, exact;
+ struct ubifs_znode *znode;
+ time64_t time = ktime_get_seconds();
+
+ dbg_tnck(key, "search and dirty key ");
+
+ znode = c->zroot.znode;
+ if (unlikely(!znode)) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ znode = dirty_cow_znode(c, &c->zroot);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+
+ znode->time = time;
+
+ while (1) {
+ struct ubifs_zbranch *zbr;
+
+ exact = ubifs_search_zbranch(c, znode, key, n);
+
+ if (znode->level == 0)
+ break;
+
+ if (*n < 0)
+ *n = 0;
+ zbr = &znode->zbranch[*n];
+
+ if (zbr->znode) {
+ znode->time = time;
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ continue;
+ }
+
+ /* znode is not in TNC cache, load it from the media */
+ znode = ubifs_load_znode(c, zbr, znode, *n);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ *zn = znode;
+ if (exact || !is_hash_key(c, key) || *n != -1) {
+ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n);
+ return exact;
+ }
+
+ /*
+ * See huge comment at 'lookup_level0_dirty()' what is the rest of the
+ * code.
+ */
+ err = tnc_prev(c, &znode, n);
+ if (err == -ENOENT) {
+ *n = -1;
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ return 0;
+ }
+ if (unlikely(err < 0))
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[*n].key)) {
+ *n = -1;
+ dbg_tnc("found 0, lvl %d, n -1", znode->level);
+ return 0;
+ }
+
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ }
+
+ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n);
+ *zn = znode;
+ return 1;
+}
+
+/**
+ * maybe_leb_gced - determine if a LEB may have been garbage collected.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number
+ * @gc_seq1: garbage collection sequence number
+ *
+ * This function determines if @lnum may have been garbage collected since
+ * sequence number @gc_seq1. If it may have been then %1 is returned, otherwise
+ * %0 is returned.
+ */
+static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1)
+{
+ int gc_seq2, gced_lnum;
+
+ gced_lnum = c->gced_lnum;
+ smp_rmb();
+ gc_seq2 = c->gc_seq;
+ /* Same seq means no GC */
+ if (gc_seq1 == gc_seq2)
+ return 0;
+ /* Different by more than 1 means we don't know */
+ if (gc_seq1 + 1 != gc_seq2)
+ return 1;
+ /*
+ * We have seen the sequence number has increased by 1. Now we need to
+ * be sure we read the right LEB number, so read it again.
+ */
+ smp_rmb();
+ if (gced_lnum != c->gced_lnum)
+ return 1;
+ /* Finally we can check lnum */
+ if (gced_lnum == lnum)
+ return 1;
+ return 0;
+}
+
+/**
+ * ubifs_tnc_locate - look up a file-system node and return it and its location.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @lnum: LEB number is returned here
+ * @offs: offset is returned here
+ *
+ * This function looks up and reads node with key @key. The caller has to make
+ * sure the @node buffer is large enough to fit the node. Returns zero in case
+ * of success, %-ENOENT if the node was not found, and a negative error code in
+ * case of failure. The node location can be returned in @lnum and @offs.
+ */
+int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, int *lnum, int *offs)
+{
+ int found, n, err;
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zt;
+
+ mutex_lock(&c->tnc_mutex);
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (!found) {
+ err = -ENOENT;
+ goto out;
+ } else if (found < 0) {
+ err = found;
+ goto out;
+ }
+ zt = &znode->zbranch[n];
+ if (lnum) {
+ *lnum = zt->lnum;
+ *offs = zt->offs;
+ }
+ if (is_hash_key(c, key)) {
+ /*
+ * In this case the leaf node cache gets used, so we pass the
+ * address of the zbranch and keep the mutex locked
+ */
+ err = tnc_read_hashed_node(c, zt, node);
+ goto out;
+ }
+ err = ubifs_tnc_read_node(c, zt, node);
+
+out:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_get_bu_keys - lookup keys for bulk-read.
+ * @c: UBIFS file-system description object
+ * @bu: bulk-read parameters and results
+ *
+ * Lookup consecutive data node keys for the same inode that reside
+ * consecutively in the same LEB. This function returns zero in case of success
+ * and a negative error code in case of failure.
+ *
+ * Note, if the bulk-read buffer length (@bu->buf_len) is known, this function
+ * makes sure bulk-read nodes fit the buffer. Otherwise, this function prepares
+ * maximum possible amount of nodes for bulk-read.
+ */
+int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu)
+{
+ int n, err = 0, lnum = -1, offs;
+ int len;
+ unsigned int block = key_block(c, &bu->key);
+ struct ubifs_znode *znode;
+
+ bu->cnt = 0;
+ bu->blk_cnt = 0;
+ bu->eof = 0;
+
+ mutex_lock(&c->tnc_mutex);
+ /* Find first key */
+ err = ubifs_lookup_level0(c, &bu->key, &znode, &n);
+ if (err < 0)
+ goto out;
+ if (err) {
+ /* Key found */
+ len = znode->zbranch[n].len;
+ /* The buffer must be big enough for at least 1 node */
+ if (len > bu->buf_len) {
+ err = -EINVAL;
+ goto out;
+ }
+ /* Add this key */
+ bu->zbranch[bu->cnt++] = znode->zbranch[n];
+ bu->blk_cnt += 1;
+ lnum = znode->zbranch[n].lnum;
+ offs = ALIGN(znode->zbranch[n].offs + len, 8);
+ }
+ while (1) {
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *key;
+ unsigned int next_block;
+
+ /* Find next key */
+ err = tnc_next(c, &znode, &n);
+ if (err)
+ goto out;
+ zbr = &znode->zbranch[n];
+ key = &zbr->key;
+ /* See if there is another data key for this file */
+ if (key_inum(c, key) != key_inum(c, &bu->key) ||
+ key_type(c, key) != UBIFS_DATA_KEY) {
+ err = -ENOENT;
+ goto out;
+ }
+ if (lnum < 0) {
+ /* First key found */
+ lnum = zbr->lnum;
+ offs = ALIGN(zbr->offs + zbr->len, 8);
+ len = zbr->len;
+ if (len > bu->buf_len) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ /*
+ * The data nodes must be in consecutive positions in
+ * the same LEB.
+ */
+ if (zbr->lnum != lnum || zbr->offs != offs)
+ goto out;
+ offs += ALIGN(zbr->len, 8);
+ len = ALIGN(len, 8) + zbr->len;
+ /* Must not exceed buffer length */
+ if (len > bu->buf_len)
+ goto out;
+ }
+ /* Allow for holes */
+ next_block = key_block(c, key);
+ bu->blk_cnt += (next_block - block - 1);
+ if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ block = next_block;
+ /* Add this key */
+ bu->zbranch[bu->cnt++] = *zbr;
+ bu->blk_cnt += 1;
+ /* See if we have room for more */
+ if (bu->cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
+ goto out;
+ }
+out:
+ if (err == -ENOENT) {
+ bu->eof = 1;
+ err = 0;
+ }
+ bu->gc_seq = c->gc_seq;
+ mutex_unlock(&c->tnc_mutex);
+ if (err)
+ return err;
+ /*
+ * An enormous hole could cause bulk-read to encompass too many
+ * page cache pages, so limit the number here.
+ */
+ if (bu->blk_cnt > UBIFS_MAX_BULK_READ)
+ bu->blk_cnt = UBIFS_MAX_BULK_READ;
+ /*
+ * Ensure that bulk-read covers a whole number of page cache
+ * pages.
+ */
+ if (UBIFS_BLOCKS_PER_PAGE == 1 ||
+ !(bu->blk_cnt & (UBIFS_BLOCKS_PER_PAGE - 1)))
+ return 0;
+ if (bu->eof) {
+ /* At the end of file we can round up */
+ bu->blk_cnt += UBIFS_BLOCKS_PER_PAGE - 1;
+ return 0;
+ }
+ /* Exclude data nodes that do not make up a whole page cache page */
+ block = key_block(c, &bu->key) + bu->blk_cnt;
+ block &= ~(UBIFS_BLOCKS_PER_PAGE - 1);
+ while (bu->cnt) {
+ if (key_block(c, &bu->zbranch[bu->cnt - 1].key) < block)
+ break;
+ bu->cnt -= 1;
+ }
+ return 0;
+}
+
+/**
+ * read_wbuf - bulk-read from a LEB with a wbuf.
+ * @wbuf: wbuf that may overlap the read
+ * @buf: buffer into which to read
+ * @len: read length
+ * @lnum: LEB number from which to read
+ * @offs: offset from which to read
+ *
+ * This functions returns %0 on success or a negative error code on failure.
+ */
+static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum,
+ int offs)
+{
+ const struct ubifs_info *c = wbuf->c;
+ int rlen, overlap;
+
+ dbg_io("LEB %d:%d, length %d", lnum, offs, len);
+ ubifs_assert(c, wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
+ ubifs_assert(c, !(offs & 7) && offs < c->leb_size);
+ ubifs_assert(c, offs + len <= c->leb_size);
+
+ spin_lock(&wbuf->lock);
+ overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
+ if (!overlap) {
+ /* We may safely unlock the write-buffer and read the data */
+ spin_unlock(&wbuf->lock);
+ return ubifs_leb_read(c, lnum, buf, offs, len, 0);
+ }
+
+ /* Don't read under wbuf */
+ rlen = wbuf->offs - offs;
+ if (rlen < 0)
+ rlen = 0;
+
+ /* Copy the rest from the write-buffer */
+ memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
+ spin_unlock(&wbuf->lock);
+
+ if (rlen > 0)
+ /* Read everything that goes before write-buffer */
+ return ubifs_leb_read(c, lnum, buf, offs, rlen, 0);
+
+ return 0;
+}
+
+/**
+ * validate_data_node - validate data nodes for bulk-read.
+ * @c: UBIFS file-system description object
+ * @buf: buffer containing data node to validate
+ * @zbr: zbranch of data node to validate
+ *
+ * This functions returns %0 on success or a negative error code on failure.
+ */
+static int validate_data_node(struct ubifs_info *c, void *buf,
+ struct ubifs_zbranch *zbr)
+{
+ union ubifs_key key1;
+ struct ubifs_ch *ch = buf;
+ int err, len;
+
+ if (ch->node_type != UBIFS_DATA_NODE) {
+ ubifs_err(c, "bad node type (%d but expected %d)",
+ ch->node_type, UBIFS_DATA_NODE);
+ goto out_err;
+ }
+
+ err = ubifs_check_node(c, buf, zbr->len, zbr->lnum, zbr->offs, 0, 0);
+ if (err) {
+ ubifs_err(c, "expected node type %d", UBIFS_DATA_NODE);
+ goto out;
+ }
+
+ err = ubifs_node_check_hash(c, buf, zbr->hash);
+ if (err) {
+ ubifs_bad_hash(c, buf, zbr->hash, zbr->lnum, zbr->offs);
+ return err;
+ }
+
+ len = le32_to_cpu(ch->len);
+ if (len != zbr->len) {
+ ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
+ goto out_err;
+ }
+
+ /* Make sure the key of the read node is correct */
+ key_read(c, buf + UBIFS_KEY_OFFSET, &key1);
+ if (!keys_eq(c, &zbr->key, &key1)) {
+ ubifs_err(c, "bad key in node at LEB %d:%d",
+ zbr->lnum, zbr->offs);
+ dbg_tnck(&zbr->key, "looked for key ");
+ dbg_tnck(&key1, "found node's key ");
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ err = -EINVAL;
+out:
+ ubifs_err(c, "bad node at LEB %d:%d", zbr->lnum, zbr->offs);
+ ubifs_dump_node(c, buf, zbr->len);
+ dump_stack();
+ return err;
+}
+
+/**
+ * ubifs_tnc_bulk_read - read a number of data nodes in one go.
+ * @c: UBIFS file-system description object
+ * @bu: bulk-read parameters and results
+ *
+ * This functions reads and validates the data nodes that were identified by the
+ * 'ubifs_tnc_get_bu_keys()' function. This functions returns %0 on success,
+ * -EAGAIN to indicate a race with GC, or another negative error code on
+ * failure.
+ */
+int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
+{
+ int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i;
+ struct ubifs_wbuf *wbuf;
+ void *buf;
+
+ len = bu->zbranch[bu->cnt - 1].offs;
+ len += bu->zbranch[bu->cnt - 1].len - offs;
+ if (len > bu->buf_len) {
+ ubifs_err(c, "buffer too small %d vs %d", bu->buf_len, len);
+ return -EINVAL;
+ }
+
+ /* Do the read */
+ wbuf = ubifs_get_wbuf(c, lnum);
+ if (wbuf)
+ err = read_wbuf(wbuf, bu->buf, len, lnum, offs);
+ else
+ err = ubifs_leb_read(c, lnum, bu->buf, offs, len, 0);
+
+ /* Check for a race with GC */
+ if (maybe_leb_gced(c, lnum, bu->gc_seq))
+ return -EAGAIN;
+
+ if (err && err != -EBADMSG) {
+ ubifs_err(c, "failed to read from LEB %d:%d, error %d",
+ lnum, offs, err);
+ dump_stack();
+ dbg_tnck(&bu->key, "key ");
+ return err;
+ }
+
+ /* Validate the nodes read */
+ buf = bu->buf;
+ for (i = 0; i < bu->cnt; i++) {
+ err = validate_data_node(c, buf, &bu->zbranch[i]);
+ if (err)
+ return err;
+ buf = buf + ALIGN(bu->zbranch[i].len, 8);
+ }
+
+ return 0;
+}
+
+/**
+ * do_lookup_nm- look up a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @nm: node name
+ *
+ * This function looks up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one is
+ * found. This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct fscrypt_name *nm)
+{
+ int found, n, err;
+ struct ubifs_znode *znode;
+
+ dbg_tnck(key, "key ");
+ mutex_lock(&c->tnc_mutex);
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (!found) {
+ err = -ENOENT;
+ goto out_unlock;
+ } else if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ ubifs_assert(c, n >= 0);
+
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+ if (err == 0) {
+ err = -ENOENT;
+ goto out_unlock;
+ }
+
+ err = tnc_read_hashed_node(c, &znode->zbranch[n], node);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_lookup_nm - look up a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @nm: node name
+ *
+ * This function looks up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one is
+ * found. This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct fscrypt_name *nm)
+{
+ int err, len;
+ const struct ubifs_dent_node *dent = node;
+
+ /*
+ * We assume that in most of the cases there are no name collisions and
+ * 'ubifs_tnc_lookup()' returns us the right direntry.
+ */
+ err = ubifs_tnc_lookup(c, key, node);
+ if (err)
+ return err;
+
+ len = le16_to_cpu(dent->nlen);
+ if (fname_len(nm) == len && !memcmp(dent->name, fname_name(nm), len))
+ return 0;
+
+ /*
+ * Unluckily, there are hash collisions and we have to iterate over
+ * them look at each direntry with colliding name hash sequentially.
+ */
+
+ return do_lookup_nm(c, key, node, nm);
+}
+
+static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_dent_node *dent, uint32_t cookie,
+ struct ubifs_znode **zn, int *n, int exact)
+{
+ int err;
+ struct ubifs_znode *znode = *zn;
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *dkey;
+
+ if (!exact) {
+ err = tnc_next(c, &znode, n);
+ if (err)
+ return err;
+ }
+
+ for (;;) {
+ zbr = &znode->zbranch[*n];
+ dkey = &zbr->key;
+
+ if (key_inum(c, dkey) != key_inum(c, key) ||
+ key_type(c, dkey) != key_type(c, key)) {
+ return -ENOENT;
+ }
+
+ err = tnc_read_hashed_node(c, zbr, dent);
+ if (err)
+ return err;
+
+ if (key_hash(c, key) == key_hash(c, dkey) &&
+ le32_to_cpu(dent->cookie) == cookie) {
+ *zn = znode;
+ return 0;
+ }
+
+ err = tnc_next(c, &znode, n);
+ if (err)
+ return err;
+ }
+}
+
+static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_dent_node *dent, uint32_t cookie)
+{
+ int n, err;
+ struct ubifs_znode *znode;
+ union ubifs_key start_key;
+
+ ubifs_assert(c, is_hash_key(c, key));
+
+ lowest_dent_key(c, &start_key, key_inum(c, key));
+
+ mutex_lock(&c->tnc_mutex);
+ err = ubifs_lookup_level0(c, &start_key, &znode, &n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+
+ err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_lookup_dh - look up a "double hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @cookie: node cookie for collision resolution
+ *
+ * This function looks up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one
+ * with the same cookie value is found.
+ * This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, uint32_t cookie)
+{
+ int err;
+ const struct ubifs_dent_node *dent = node;
+
+ if (!c->double_hash)
+ return -EOPNOTSUPP;
+
+ /*
+ * We assume that in most of the cases there are no name collisions and
+ * 'ubifs_tnc_lookup()' returns us the right direntry.
+ */
+ err = ubifs_tnc_lookup(c, key, node);
+ if (err)
+ return err;
+
+ if (le32_to_cpu(dent->cookie) == cookie)
+ return 0;
+
+ /*
+ * Unluckily, there are hash collisions and we have to iterate over
+ * them look at each direntry with colliding name hash sequentially.
+ */
+ return do_lookup_dh(c, key, node, cookie);
+}
+
+/**
+ * correct_parent_keys - correct parent znodes' keys.
+ * @c: UBIFS file-system description object
+ * @znode: znode to correct parent znodes for
+ *
+ * This is a helper function for 'tnc_insert()'. When the key of the leftmost
+ * zbranch changes, keys of parent znodes have to be corrected. This helper
+ * function is called in such situations and corrects the keys if needed.
+ */
+static void correct_parent_keys(const struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ union ubifs_key *key, *key1;
+
+ ubifs_assert(c, znode->parent);
+ ubifs_assert(c, znode->iip == 0);
+
+ key = &znode->zbranch[0].key;
+ key1 = &znode->parent->zbranch[0].key;
+
+ while (keys_cmp(c, key, key1) < 0) {
+ key_copy(c, key, key1);
+ znode = znode->parent;
+ znode->alt = 1;
+ if (!znode->parent || znode->iip)
+ break;
+ key1 = &znode->parent->zbranch[0].key;
+ }
+}
+
+/**
+ * insert_zbranch - insert a zbranch into a znode.
+ * @c: UBIFS file-system description object
+ * @znode: znode into which to insert
+ * @zbr: zbranch to insert
+ * @n: slot number to insert to
+ *
+ * This is a helper function for 'tnc_insert()'. UBIFS does not allow "gaps" in
+ * znode's array of zbranches and keeps zbranches consolidated, so when a new
+ * zbranch has to be inserted to the @znode->zbranches[]' array at the @n-th
+ * slot, zbranches starting from @n have to be moved right.
+ */
+static void insert_zbranch(struct ubifs_info *c, struct ubifs_znode *znode,
+ const struct ubifs_zbranch *zbr, int n)
+{
+ int i;
+
+ ubifs_assert(c, ubifs_zn_dirty(znode));
+
+ if (znode->level) {
+ for (i = znode->child_cnt; i > n; i--) {
+ znode->zbranch[i] = znode->zbranch[i - 1];
+ if (znode->zbranch[i].znode)
+ znode->zbranch[i].znode->iip = i;
+ }
+ if (zbr->znode)
+ zbr->znode->iip = n;
+ } else
+ for (i = znode->child_cnt; i > n; i--)
+ znode->zbranch[i] = znode->zbranch[i - 1];
+
+ znode->zbranch[n] = *zbr;
+ znode->child_cnt += 1;
+
+ /*
+ * After inserting at slot zero, the lower bound of the key range of
+ * this znode may have changed. If this znode is subsequently split
+ * then the upper bound of the key range may change, and furthermore
+ * it could change to be lower than the original lower bound. If that
+ * happens, then it will no longer be possible to find this znode in the
+ * TNC using the key from the index node on flash. That is bad because
+ * if it is not found, we will assume it is obsolete and may overwrite
+ * it. Then if there is an unclean unmount, we will start using the
+ * old index which will be broken.
+ *
+ * So we first mark znodes that have insertions at slot zero, and then
+ * if they are split we add their lnum/offs to the old_idx tree.
+ */
+ if (n == 0)
+ znode->alt = 1;
+}
+
+/**
+ * tnc_insert - insert a node into TNC.
+ * @c: UBIFS file-system description object
+ * @znode: znode to insert into
+ * @zbr: branch to insert
+ * @n: slot number to insert new zbranch to
+ *
+ * This function inserts a new node described by @zbr into znode @znode. If
+ * znode does not have a free slot for new zbranch, it is split. Parent znodes
+ * are splat as well if needed. Returns zero in case of success or a negative
+ * error code in case of failure.
+ */
+static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
+ struct ubifs_zbranch *zbr, int n)
+{
+ struct ubifs_znode *zn, *zi, *zp;
+ int i, keep, move, appending = 0;
+ union ubifs_key *key = &zbr->key, *key1;
+
+ ubifs_assert(c, n >= 0 && n <= c->fanout);
+
+ /* Implement naive insert for now */
+again:
+ zp = znode->parent;
+ if (znode->child_cnt < c->fanout) {
+ ubifs_assert(c, n != c->fanout);
+ dbg_tnck(key, "inserted at %d level %d, key ", n, znode->level);
+
+ insert_zbranch(c, znode, zbr, n);
+
+ /* Ensure parent's key is correct */
+ if (n == 0 && zp && znode->iip == 0)
+ correct_parent_keys(c, znode);
+
+ return 0;
+ }
+
+ /*
+ * Unfortunately, @znode does not have more empty slots and we have to
+ * split it.
+ */
+ dbg_tnck(key, "splitting level %d, key ", znode->level);
+
+ if (znode->alt)
+ /*
+ * We can no longer be sure of finding this znode by key, so we
+ * record it in the old_idx tree.
+ */
+ ins_clr_old_idx_znode(c, znode);
+
+ zn = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!zn)
+ return -ENOMEM;
+ zn->parent = zp;
+ zn->level = znode->level;
+
+ /* Decide where to split */
+ if (znode->level == 0 && key_type(c, key) == UBIFS_DATA_KEY) {
+ /* Try not to split consecutive data keys */
+ if (n == c->fanout) {
+ key1 = &znode->zbranch[n - 1].key;
+ if (key_inum(c, key1) == key_inum(c, key) &&
+ key_type(c, key1) == UBIFS_DATA_KEY)
+ appending = 1;
+ } else
+ goto check_split;
+ } else if (appending && n != c->fanout) {
+ /* Try not to split consecutive data keys */
+ appending = 0;
+check_split:
+ if (n >= (c->fanout + 1) / 2) {
+ key1 = &znode->zbranch[0].key;
+ if (key_inum(c, key1) == key_inum(c, key) &&
+ key_type(c, key1) == UBIFS_DATA_KEY) {
+ key1 = &znode->zbranch[n].key;
+ if (key_inum(c, key1) != key_inum(c, key) ||
+ key_type(c, key1) != UBIFS_DATA_KEY) {
+ keep = n;
+ move = c->fanout - keep;
+ zi = znode;
+ goto do_split;
+ }
+ }
+ }
+ }
+
+ if (appending) {
+ keep = c->fanout;
+ move = 0;
+ } else {
+ keep = (c->fanout + 1) / 2;
+ move = c->fanout - keep;
+ }
+
+ /*
+ * Although we don't at present, we could look at the neighbors and see
+ * if we can move some zbranches there.
+ */
+
+ if (n < keep) {
+ /* Insert into existing znode */
+ zi = znode;
+ move += 1;
+ keep -= 1;
+ } else {
+ /* Insert into new znode */
+ zi = zn;
+ n -= keep;
+ /* Re-parent */
+ if (zn->level != 0)
+ zbr->znode->parent = zn;
+ }
+
+do_split:
+
+ __set_bit(DIRTY_ZNODE, &zn->flags);
+ atomic_long_inc(&c->dirty_zn_cnt);
+
+ zn->child_cnt = move;
+ znode->child_cnt = keep;
+
+ dbg_tnc("moving %d, keeping %d", move, keep);
+
+ /* Move zbranch */
+ for (i = 0; i < move; i++) {
+ zn->zbranch[i] = znode->zbranch[keep + i];
+ /* Re-parent */
+ if (zn->level != 0)
+ if (zn->zbranch[i].znode) {
+ zn->zbranch[i].znode->parent = zn;
+ zn->zbranch[i].znode->iip = i;
+ }
+ }
+
+ /* Insert new key and branch */
+ dbg_tnck(key, "inserting at %d level %d, key ", n, zn->level);
+
+ insert_zbranch(c, zi, zbr, n);
+
+ /* Insert new znode (produced by spitting) into the parent */
+ if (zp) {
+ if (n == 0 && zi == znode && znode->iip == 0)
+ correct_parent_keys(c, znode);
+
+ /* Locate insertion point */
+ n = znode->iip + 1;
+
+ /* Tail recursion */
+ zbr->key = zn->zbranch[0].key;
+ zbr->znode = zn;
+ zbr->lnum = 0;
+ zbr->offs = 0;
+ zbr->len = 0;
+ znode = zp;
+
+ goto again;
+ }
+
+ /* We have to split root znode */
+ dbg_tnc("creating new zroot at level %d", znode->level + 1);
+
+ zi = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!zi)
+ return -ENOMEM;
+
+ zi->child_cnt = 2;
+ zi->level = znode->level + 1;
+
+ __set_bit(DIRTY_ZNODE, &zi->flags);
+ atomic_long_inc(&c->dirty_zn_cnt);
+
+ zi->zbranch[0].key = znode->zbranch[0].key;
+ zi->zbranch[0].znode = znode;
+ zi->zbranch[0].lnum = c->zroot.lnum;
+ zi->zbranch[0].offs = c->zroot.offs;
+ zi->zbranch[0].len = c->zroot.len;
+ zi->zbranch[1].key = zn->zbranch[0].key;
+ zi->zbranch[1].znode = zn;
+
+ c->zroot.lnum = 0;
+ c->zroot.offs = 0;
+ c->zroot.len = 0;
+ c->zroot.znode = zi;
+
+ zn->parent = zi;
+ zn->iip = 1;
+ znode->parent = zi;
+ znode->iip = 0;
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_add - add a node to TNC.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ * @hash: The hash over the node
+ *
+ * This function adds a node with key @key to TNC. The node may be new or it may
+ * obsolete some existing one. Returns %0 on success or negative error code on
+ * failure.
+ */
+int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
+ int offs, int len, const u8 *hash)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "%d:%d, len %d, key ", lnum, offs, len);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (!found) {
+ struct ubifs_zbranch zbr;
+
+ zbr.znode = NULL;
+ zbr.lnum = lnum;
+ zbr.offs = offs;
+ zbr.len = len;
+ ubifs_copy_hash(c, hash, zbr.hash);
+ key_copy(c, key, &zbr.key);
+ err = tnc_insert(c, znode, &zbr, n + 1);
+ } else if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ ubifs_copy_hash(c, hash, zbr->hash);
+ } else
+ err = found;
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+
+ return err;
+}
+
+/**
+ * ubifs_tnc_replace - replace a node in the TNC only if the old node is found.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @old_lnum: LEB number of old node
+ * @old_offs: old node offset
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ *
+ * This function replaces a node with key @key in the TNC only if the old node
+ * is found. This function is called by garbage collection when node are moved.
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
+ int old_lnum, int old_offs, int lnum, int offs, int len)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "old LEB %d:%d, new LEB %d:%d, len %d, key ", old_lnum,
+ old_offs, lnum, offs, len);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ found = 0;
+ if (zbr->lnum == old_lnum && zbr->offs == old_offs) {
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ if (err)
+ goto out_unlock;
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ found = 1;
+ } else if (is_hash_key(c, key)) {
+ found = resolve_collision_directly(c, key, &znode, &n,
+ old_lnum, old_offs);
+ dbg_tnc("rc returned %d, znode %p, n %d, LEB %d:%d",
+ found, znode, n, old_lnum, old_offs);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found) {
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+ zbr = &znode->zbranch[n];
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum,
+ zbr->len);
+ if (err)
+ goto out_unlock;
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ }
+ }
+ }
+
+ if (!found)
+ err = ubifs_add_dirt(c, lnum, len);
+
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_add_nm - add a "hashed" node to TNC.
+ * @c: UBIFS file-system description object
+ * @key: key to add
+ * @lnum: LEB number of node
+ * @offs: node offset
+ * @len: node length
+ * @hash: The hash over the node
+ * @nm: node name
+ *
+ * This is the same as 'ubifs_tnc_add()' but it should be used with keys which
+ * may have collisions, like directory entry keys.
+ */
+int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs, int len, const u8 *hash,
+ const struct fscrypt_name *nm)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "LEB %d:%d, key ", lnum, offs);
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ if (found == 1) {
+ if (c->replaying)
+ found = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 1);
+ else
+ found = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", found, znode, n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+
+ if (found == 1) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ lnc_free(zbr);
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ ubifs_copy_hash(c, hash, zbr->hash);
+ goto out_unlock;
+ }
+ }
+
+ if (!found) {
+ struct ubifs_zbranch zbr;
+
+ zbr.znode = NULL;
+ zbr.lnum = lnum;
+ zbr.offs = offs;
+ zbr.len = len;
+ ubifs_copy_hash(c, hash, zbr.hash);
+ key_copy(c, key, &zbr.key);
+ err = tnc_insert(c, znode, &zbr, n + 1);
+ if (err)
+ goto out_unlock;
+ if (c->replaying) {
+ /*
+ * We did not find it in the index so there may be a
+ * dangling branch still in the index. So we remove it
+ * by passing 'ubifs_tnc_remove_nm()' the same key but
+ * an unmatchable name.
+ */
+ struct fscrypt_name noname = { .disk_name = { .name = "", .len = 1 } };
+
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ if (err)
+ return err;
+ return ubifs_tnc_remove_nm(c, key, &noname);
+ }
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * tnc_delete - delete a znode form TNC.
+ * @c: UBIFS file-system description object
+ * @znode: znode to delete from
+ * @n: zbranch slot number to delete
+ *
+ * This function deletes a leaf node from @n-th slot of @znode. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n)
+{
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *zp;
+ int i, err;
+
+ /* Delete without merge for now */
+ ubifs_assert(c, znode->level == 0);
+ ubifs_assert(c, n >= 0 && n < c->fanout);
+ dbg_tnck(&znode->zbranch[n].key, "deleting key ");
+
+ zbr = &znode->zbranch[n];
+ lnc_free(zbr);
+
+ err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
+ if (err) {
+ ubifs_dump_znode(c, znode);
+ return err;
+ }
+
+ /* We do not "gap" zbranch slots */
+ for (i = n; i < znode->child_cnt - 1; i++)
+ znode->zbranch[i] = znode->zbranch[i + 1];
+ znode->child_cnt -= 1;
+
+ if (znode->child_cnt > 0)
+ return 0;
+
+ /*
+ * This was the last zbranch, we have to delete this znode from the
+ * parent.
+ */
+
+ do {
+ ubifs_assert(c, !ubifs_zn_obsolete(znode));
+ ubifs_assert(c, ubifs_zn_dirty(znode));
+
+ zp = znode->parent;
+ n = znode->iip;
+
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ err = insert_old_idx_znode(c, znode);
+ if (err)
+ return err;
+
+ if (znode->cnext) {
+ __set_bit(OBSOLETE_ZNODE, &znode->flags);
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ } else
+ kfree(znode);
+ znode = zp;
+ } while (znode->child_cnt == 1); /* while removing last child */
+
+ /* Remove from znode, entry n - 1 */
+ znode->child_cnt -= 1;
+ ubifs_assert(c, znode->level != 0);
+ for (i = n; i < znode->child_cnt; i++) {
+ znode->zbranch[i] = znode->zbranch[i + 1];
+ if (znode->zbranch[i].znode)
+ znode->zbranch[i].znode->iip = i;
+ }
+
+ /*
+ * If this is the root and it has only 1 child then
+ * collapse the tree.
+ */
+ if (!znode->parent) {
+ while (znode->child_cnt == 1 && znode->level != 0) {
+ zp = znode;
+ zbr = &znode->zbranch[0];
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode = dirty_cow_znode(c, zbr);
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+ znode->parent = NULL;
+ znode->iip = 0;
+ if (c->zroot.len) {
+ err = insert_old_idx(c, c->zroot.lnum,
+ c->zroot.offs);
+ if (err)
+ return err;
+ }
+ c->zroot.lnum = zbr->lnum;
+ c->zroot.offs = zbr->offs;
+ c->zroot.len = zbr->len;
+ c->zroot.znode = znode;
+ ubifs_assert(c, !ubifs_zn_obsolete(zp));
+ ubifs_assert(c, ubifs_zn_dirty(zp));
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ if (zp->cnext) {
+ __set_bit(OBSOLETE_ZNODE, &zp->flags);
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ } else
+ kfree(zp);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_remove - remove an index entry of a node.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key)
+{
+ int found, n, err = 0;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "key ");
+ found = lookup_level0_dirty(c, key, &znode, &n);
+ if (found < 0) {
+ err = found;
+ goto out_unlock;
+ }
+ if (found == 1)
+ err = tnc_delete(c, znode, n);
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_remove_nm - remove an index entry for a "hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ * @nm: directory entry name
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
+ const struct fscrypt_name *nm)
+{
+ int n, err;
+ struct ubifs_znode *znode;
+
+ mutex_lock(&c->tnc_mutex);
+ dbg_tnck(key, "key ");
+ err = lookup_level0_dirty(c, key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err) {
+ if (c->replaying)
+ err = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 0);
+ else
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n);
+ if (err < 0)
+ goto out_unlock;
+ if (err) {
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+ err = tnc_delete(c, znode, n);
+ }
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_remove_dh - remove an index entry for a "double hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: key of node
+ * @cookie: node cookie for collision resolution
+ *
+ * Returns %0 on success or negative error code on failure.
+ */
+int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
+ uint32_t cookie)
+{
+ int n, err;
+ struct ubifs_znode *znode;
+ struct ubifs_dent_node *dent;
+ struct ubifs_zbranch *zbr;
+
+ if (!c->double_hash)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&c->tnc_mutex);
+ err = lookup_level0_dirty(c, key, &znode, &n);
+ if (err <= 0)
+ goto out_unlock;
+
+ zbr = &znode->zbranch[n];
+ dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ err = tnc_read_hashed_node(c, zbr, dent);
+ if (err)
+ goto out_free;
+
+ /* If the cookie does not match, we're facing a hash collision. */
+ if (le32_to_cpu(dent->cookie) != cookie) {
+ union ubifs_key start_key;
+
+ lowest_dent_key(c, &start_key, key_inum(c, key));
+
+ err = ubifs_lookup_level0(c, &start_key, &znode, &n);
+ if (unlikely(err < 0))
+ goto out_free;
+
+ err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
+ if (err)
+ goto out_free;
+ }
+
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_free;
+ }
+ }
+ err = tnc_delete(c, znode, n);
+
+out_free:
+ kfree(dent);
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * key_in_range - determine if a key falls within a range of keys.
+ * @c: UBIFS file-system description object
+ * @key: key to check
+ * @from_key: lowest key in range
+ * @to_key: highest key in range
+ *
+ * This function returns %1 if the key is in range and %0 otherwise.
+ */
+static int key_in_range(struct ubifs_info *c, union ubifs_key *key,
+ union ubifs_key *from_key, union ubifs_key *to_key)
+{
+ if (keys_cmp(c, key, from_key) < 0)
+ return 0;
+ if (keys_cmp(c, key, to_key) > 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * ubifs_tnc_remove_range - remove index entries in range.
+ * @c: UBIFS file-system description object
+ * @from_key: lowest key to remove
+ * @to_key: highest key to remove
+ *
+ * This function removes index entries starting at @from_key and ending at
+ * @to_key. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
+ union ubifs_key *to_key)
+{
+ int i, n, k, err = 0;
+ struct ubifs_znode *znode;
+ union ubifs_key *key;
+
+ mutex_lock(&c->tnc_mutex);
+ while (1) {
+ /* Find first level 0 znode that contains keys to remove */
+ err = ubifs_lookup_level0(c, from_key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err)
+ key = from_key;
+ else {
+ err = tnc_next(c, &znode, &n);
+ if (err == -ENOENT) {
+ err = 0;
+ goto out_unlock;
+ }
+ if (err < 0)
+ goto out_unlock;
+ key = &znode->zbranch[n].key;
+ if (!key_in_range(c, key, from_key, to_key)) {
+ err = 0;
+ goto out_unlock;
+ }
+ }
+
+ /* Ensure the znode is dirtied */
+ if (znode->cnext || !ubifs_zn_dirty(znode)) {
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ }
+
+ /* Remove all keys in range except the first */
+ for (i = n + 1, k = 0; i < znode->child_cnt; i++, k++) {
+ key = &znode->zbranch[i].key;
+ if (!key_in_range(c, key, from_key, to_key))
+ break;
+ lnc_free(&znode->zbranch[i]);
+ err = ubifs_add_dirt(c, znode->zbranch[i].lnum,
+ znode->zbranch[i].len);
+ if (err) {
+ ubifs_dump_znode(c, znode);
+ goto out_unlock;
+ }
+ dbg_tnck(key, "removing key ");
+ }
+ if (k) {
+ for (i = n + 1 + k; i < znode->child_cnt; i++)
+ znode->zbranch[i - k] = znode->zbranch[i];
+ znode->child_cnt -= k;
+ }
+
+ /* Now delete the first */
+ err = tnc_delete(c, znode, n);
+ if (err)
+ goto out_unlock;
+ }
+
+out_unlock:
+ if (!err)
+ err = dbg_check_tnc(c, 0);
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_remove_ino - remove an inode from TNC.
+ * @c: UBIFS file-system description object
+ * @inum: inode number to remove
+ *
+ * This function remove inode @inum and all the extended attributes associated
+ * with the anode from TNC and returns zero in case of success or a negative
+ * error code in case of failure.
+ */
+int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
+{
+ union ubifs_key key1, key2;
+ struct ubifs_dent_node *xent, *pxent = NULL;
+ struct fscrypt_name nm = {0};
+
+ dbg_tnc("ino %lu", (unsigned long)inum);
+
+ /*
+ * Walk all extended attribute entries and remove them together with
+ * corresponding extended attribute inodes.
+ */
+ lowest_xent_key(c, &key1, inum);
+ while (1) {
+ ino_t xattr_inum;
+ int err;
+
+ xent = ubifs_tnc_next_ent(c, &key1, &nm);
+ if (IS_ERR(xent)) {
+ err = PTR_ERR(xent);
+ if (err == -ENOENT)
+ break;
+ kfree(pxent);
+ return err;
+ }
+
+ xattr_inum = le64_to_cpu(xent->inum);
+ dbg_tnc("xent '%s', ino %lu", xent->name,
+ (unsigned long)xattr_inum);
+
+ ubifs_evict_xattr_inode(c, xattr_inum);
+
+ fname_name(&nm) = xent->name;
+ fname_len(&nm) = le16_to_cpu(xent->nlen);
+ err = ubifs_tnc_remove_nm(c, &key1, &nm);
+ if (err) {
+ kfree(pxent);
+ kfree(xent);
+ return err;
+ }
+
+ lowest_ino_key(c, &key1, xattr_inum);
+ highest_ino_key(c, &key2, xattr_inum);
+ err = ubifs_tnc_remove_range(c, &key1, &key2);
+ if (err) {
+ kfree(pxent);
+ kfree(xent);
+ return err;
+ }
+
+ kfree(pxent);
+ pxent = xent;
+ key_read(c, &xent->key, &key1);
+ }
+
+ kfree(pxent);
+ lowest_ino_key(c, &key1, inum);
+ highest_ino_key(c, &key2, inum);
+
+ return ubifs_tnc_remove_range(c, &key1, &key2);
+}
+
+/**
+ * ubifs_tnc_next_ent - walk directory or extended attribute entries.
+ * @c: UBIFS file-system description object
+ * @key: key of last entry
+ * @nm: name of last entry found or %NULL
+ *
+ * This function finds and reads the next directory or extended attribute entry
+ * after the given key (@key) if there is one. @nm is used to resolve
+ * collisions.
+ *
+ * If the name of the current entry is not known and only the key is known,
+ * @nm->name has to be %NULL. In this case the semantics of this function is a
+ * little bit different and it returns the entry corresponding to this key, not
+ * the next one. If the key was not found, the closest "right" entry is
+ * returned.
+ *
+ * If the fist entry has to be found, @key has to contain the lowest possible
+ * key value for this inode and @name has to be %NULL.
+ *
+ * This function returns the found directory or extended attribute entry node
+ * in case of success, %-ENOENT is returned if no entry was found, and a
+ * negative error code is returned in case of failure.
+ */
+struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
+ union ubifs_key *key,
+ const struct fscrypt_name *nm)
+{
+ int n, err, type = key_type(c, key);
+ struct ubifs_znode *znode;
+ struct ubifs_dent_node *dent;
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *dkey;
+
+ dbg_tnck(key, "key ");
+ ubifs_assert(c, is_hash_key(c, key));
+
+ mutex_lock(&c->tnc_mutex);
+ err = ubifs_lookup_level0(c, key, &znode, &n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+
+ if (fname_len(nm) > 0) {
+ if (err) {
+ /* Handle collisions */
+ if (c->replaying)
+ err = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 0);
+ else
+ err = resolve_collision(c, key, &znode, &n, nm);
+ dbg_tnc("rc returned %d, znode %p, n %d",
+ err, znode, n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+ }
+
+ /* Now find next entry */
+ err = tnc_next(c, &znode, &n);
+ if (unlikely(err))
+ goto out_unlock;
+ } else {
+ /*
+ * The full name of the entry was not given, in which case the
+ * behavior of this function is a little different and it
+ * returns current entry, not the next one.
+ */
+ if (!err) {
+ /*
+ * However, the given key does not exist in the TNC
+ * tree and @znode/@n variables contain the closest
+ * "preceding" element. Switch to the next one.
+ */
+ err = tnc_next(c, &znode, &n);
+ if (err)
+ goto out_unlock;
+ }
+ }
+
+ zbr = &znode->zbranch[n];
+ dent = kmalloc(zbr->len, GFP_NOFS);
+ if (unlikely(!dent)) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ /*
+ * The above 'tnc_next()' call could lead us to the next inode, check
+ * this.
+ */
+ dkey = &zbr->key;
+ if (key_inum(c, dkey) != key_inum(c, key) ||
+ key_type(c, dkey) != type) {
+ err = -ENOENT;
+ goto out_free;
+ }
+
+ err = tnc_read_hashed_node(c, zbr, dent);
+ if (unlikely(err))
+ goto out_free;
+
+ mutex_unlock(&c->tnc_mutex);
+ return dent;
+
+out_free:
+ kfree(dent);
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return ERR_PTR(err);
+}
+
+/**
+ * tnc_destroy_cnext - destroy left-over obsolete znodes from a failed commit.
+ * @c: UBIFS file-system description object
+ *
+ * Destroy left-over obsolete znodes from a failed commit.
+ */
+static void tnc_destroy_cnext(struct ubifs_info *c)
+{
+ struct ubifs_znode *cnext;
+
+ if (!c->cnext)
+ return;
+ ubifs_assert(c, c->cmt_state == COMMIT_BROKEN);
+ cnext = c->cnext;
+ do {
+ struct ubifs_znode *znode = cnext;
+
+ cnext = cnext->cnext;
+ if (ubifs_zn_obsolete(znode))
+ kfree(znode);
+ else if (!ubifs_zn_cow(znode)) {
+ /*
+ * Don't forget to update clean znode count after
+ * committing failed, because ubifs will check this
+ * count while closing tnc. Non-obsolete znode could
+ * be re-dirtied during committing process, so dirty
+ * flag is untrustable. The flag 'COW_ZNODE' is set
+ * for each dirty znode before committing, and it is
+ * cleared as long as the znode become clean, so we
+ * can statistic clean znode count according to this
+ * flag.
+ */
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ }
+ } while (cnext && cnext != c->cnext);
+}
+
+/**
+ * ubifs_tnc_close - close TNC subsystem and free all related resources.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_tnc_close(struct ubifs_info *c)
+{
+ tnc_destroy_cnext(c);
+ ubifs_destroy_tnc_tree(c);
+ kfree(c->gap_lebs);
+ kfree(c->ilebs);
+ destroy_old_idx(c);
+}
+
+/**
+ * left_znode - get the znode to the left.
+ * @c: UBIFS file-system description object
+ * @znode: znode
+ *
+ * This function returns a pointer to the znode to the left of @znode or NULL if
+ * there is not one. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *left_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int level = znode->level;
+
+ while (1) {
+ int n = znode->iip - 1;
+
+ /* Go up until we can go left */
+ znode = znode->parent;
+ if (!znode)
+ return NULL;
+ if (n >= 0) {
+ /* Now go down the rightmost branch to 'level' */
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ while (znode->level != level) {
+ n = znode->child_cnt - 1;
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ break;
+ }
+ }
+ return znode;
+}
+
+/**
+ * right_znode - get the znode to the right.
+ * @c: UBIFS file-system description object
+ * @znode: znode
+ *
+ * This function returns a pointer to the znode to the right of @znode or NULL
+ * if there is not one. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *right_znode(struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ int level = znode->level;
+
+ while (1) {
+ int n = znode->iip + 1;
+
+ /* Go up until we can go right */
+ znode = znode->parent;
+ if (!znode)
+ return NULL;
+ if (n < znode->child_cnt) {
+ /* Now go down the leftmost branch to 'level' */
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ while (znode->level != level) {
+ znode = get_znode(c, znode, 0);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ break;
+ }
+ }
+ return znode;
+}
+
+/**
+ * lookup_znode - find a particular indexing node from TNC.
+ * @c: UBIFS file-system description object
+ * @key: index node key to lookup
+ * @level: index node level
+ * @lnum: index node LEB number
+ * @offs: index node offset
+ *
+ * This function searches an indexing node by its first key @key and its
+ * address @lnum:@offs. It looks up the indexing tree by pulling all indexing
+ * nodes it traverses to TNC. This function is called for indexing nodes which
+ * were found on the media by scanning, for example when garbage-collecting or
+ * when doing in-the-gaps commit. This means that the indexing node which is
+ * looked for does not have to have exactly the same leftmost key @key, because
+ * the leftmost key may have been changed, in which case TNC will contain a
+ * dirty znode which still refers the same @lnum:@offs. This function is clever
+ * enough to recognize such indexing nodes.
+ *
+ * Note, if a znode was deleted or changed too much, then this function will
+ * not find it. For situations like this UBIFS has the old index RB-tree
+ * (indexed by @lnum:@offs).
+ *
+ * This function returns a pointer to the znode found or %NULL if it is not
+ * found. A negative error code is returned on failure.
+ */
+static struct ubifs_znode *lookup_znode(struct ubifs_info *c,
+ union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode, *zn;
+ int n, nn;
+
+ ubifs_assert(c, key_type(c, key) < UBIFS_INVALID_KEY);
+
+ /*
+ * The arguments have probably been read off flash, so don't assume
+ * they are valid.
+ */
+ if (level < 0)
+ return ERR_PTR(-EINVAL);
+
+ /* Get the root znode */
+ znode = c->zroot.znode;
+ if (!znode) {
+ znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ /* Check if it is the one we are looking for */
+ if (c->zroot.lnum == lnum && c->zroot.offs == offs)
+ return znode;
+ /* Descend to the parent level i.e. (level + 1) */
+ if (level >= znode->level)
+ return NULL;
+ while (1) {
+ ubifs_search_zbranch(c, znode, key, &n);
+ if (n < 0) {
+ /*
+ * We reached a znode where the leftmost key is greater
+ * than the key we are searching for. This is the same
+ * situation as the one described in a huge comment at
+ * the end of the 'ubifs_lookup_level0()' function. And
+ * for exactly the same reasons we have to try to look
+ * left before giving up.
+ */
+ znode = left_znode(c, znode);
+ if (!znode)
+ return NULL;
+ if (IS_ERR(znode))
+ return znode;
+ ubifs_search_zbranch(c, znode, key, &n);
+ ubifs_assert(c, n >= 0);
+ }
+ if (znode->level == level + 1)
+ break;
+ znode = get_znode(c, znode, n);
+ if (IS_ERR(znode))
+ return znode;
+ }
+ /* Check if the child is the one we are looking for */
+ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* If the key is unique, there is nowhere else to look */
+ if (!is_hash_key(c, key))
+ return NULL;
+ /*
+ * The key is not unique and so may be also in the znodes to either
+ * side.
+ */
+ zn = znode;
+ nn = n;
+ /* Look left */
+ while (1) {
+ /* Move one branch to the left */
+ if (n)
+ n -= 1;
+ else {
+ znode = left_znode(c, znode);
+ if (!znode)
+ break;
+ if (IS_ERR(znode))
+ return znode;
+ n = znode->child_cnt - 1;
+ }
+ /* Check it */
+ if (znode->zbranch[n].lnum == lnum &&
+ znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* Stop if the key is less than the one we are looking for */
+ if (keys_cmp(c, &znode->zbranch[n].key, key) < 0)
+ break;
+ }
+ /* Back to the middle */
+ znode = zn;
+ n = nn;
+ /* Look right */
+ while (1) {
+ /* Move one branch to the right */
+ if (++n >= znode->child_cnt) {
+ znode = right_znode(c, znode);
+ if (!znode)
+ break;
+ if (IS_ERR(znode))
+ return znode;
+ n = 0;
+ }
+ /* Check it */
+ if (znode->zbranch[n].lnum == lnum &&
+ znode->zbranch[n].offs == offs)
+ return get_znode(c, znode, n);
+ /* Stop if the key is greater than the one we are looking for */
+ if (keys_cmp(c, &znode->zbranch[n].key, key) > 0)
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * is_idx_node_in_tnc - determine if an index node is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: key of index node
+ * @level: index node level
+ * @lnum: LEB number of index node
+ * @offs: offset of index node
+ *
+ * This function returns %0 if the index node is not referred to in the TNC, %1
+ * if the index node is referred to in the TNC and the corresponding znode is
+ * dirty, %2 if an index node is referred to in the TNC and the corresponding
+ * znode is clean, and a negative error code in case of failure.
+ *
+ * Note, the @key argument has to be the key of the first child. Also note,
+ * this function relies on the fact that 0:0 is never a valid LEB number and
+ * offset for a main-area node.
+ */
+int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+
+ znode = lookup_znode(c, key, level, lnum, offs);
+ if (!znode)
+ return 0;
+ if (IS_ERR(znode))
+ return PTR_ERR(znode);
+
+ return ubifs_zn_dirty(znode) ? 1 : 2;
+}
+
+/**
+ * is_leaf_node_in_tnc - determine if a non-indexing not is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @lnum: node LEB number
+ * @offs: node offset
+ *
+ * This function returns %1 if the node is referred to in the TNC, %0 if it is
+ * not, and a negative error code in case of failure.
+ *
+ * Note, this function relies on the fact that 0:0 is never a valid LEB number
+ * and offset for a main-area node.
+ */
+static int is_leaf_node_in_tnc(struct ubifs_info *c, union ubifs_key *key,
+ int lnum, int offs)
+{
+ struct ubifs_zbranch *zbr;
+ struct ubifs_znode *znode, *zn;
+ int n, found, err, nn;
+ const int unique = !is_hash_key(c, key);
+
+ found = ubifs_lookup_level0(c, key, &znode, &n);
+ if (found < 0)
+ return found; /* Error code */
+ if (!found)
+ return 0;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ if (unique)
+ return 0;
+ /*
+ * Because the key is not unique, we have to look left
+ * and right as well
+ */
+ zn = znode;
+ nn = n;
+ /* Look left */
+ while (1) {
+ err = tnc_prev(c, &znode, &n);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ return err;
+ if (keys_cmp(c, key, &znode->zbranch[n].key))
+ break;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ }
+ /* Look right */
+ znode = zn;
+ n = nn;
+ while (1) {
+ err = tnc_next(c, &znode, &n);
+ if (err) {
+ if (err == -ENOENT)
+ return 0;
+ return err;
+ }
+ if (keys_cmp(c, key, &znode->zbranch[n].key))
+ break;
+ zbr = &znode->zbranch[n];
+ if (lnum == zbr->lnum && offs == zbr->offs)
+ return 1; /* Found it */
+ }
+ return 0;
+}
+
+/**
+ * ubifs_tnc_has_node - determine whether a node is in the TNC.
+ * @c: UBIFS file-system description object
+ * @key: node key
+ * @level: index node level (if it is an index node)
+ * @lnum: node LEB number
+ * @offs: node offset
+ * @is_idx: non-zero if the node is an index node
+ *
+ * This function returns %1 if the node is in the TNC, %0 if it is not, and a
+ * negative error code in case of failure. For index nodes, @key has to be the
+ * key of the first child. An index node is considered to be in the TNC only if
+ * the corresponding znode is clean or has not been loaded.
+ */
+int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs, int is_idx)
+{
+ int err;
+
+ mutex_lock(&c->tnc_mutex);
+ if (is_idx) {
+ err = is_idx_node_in_tnc(c, key, level, lnum, offs);
+ if (err < 0)
+ goto out_unlock;
+ if (err == 1)
+ /* The index node was found but it was dirty */
+ err = 0;
+ else if (err == 2)
+ /* The index node was found and it was clean */
+ err = 1;
+ else
+ BUG_ON(err != 0);
+ } else
+ err = is_leaf_node_in_tnc(c, key, lnum, offs);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_dirty_idx_node - dirty an index node.
+ * @c: UBIFS file-system description object
+ * @key: index node key
+ * @level: index node level
+ * @lnum: index node LEB number
+ * @offs: index node offset
+ *
+ * This function loads and dirties an index node so that it can be garbage
+ * collected. The @key argument has to be the key of the first child. This
+ * function relies on the fact that 0:0 is never a valid LEB number and offset
+ * for a main-area node. Returns %0 on success and a negative error code on
+ * failure.
+ */
+int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs)
+{
+ struct ubifs_znode *znode;
+ int err = 0;
+
+ mutex_lock(&c->tnc_mutex);
+ znode = lookup_znode(c, key, level, lnum, offs);
+ if (!znode)
+ goto out_unlock;
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+ znode = dirty_cow_bottom_up(c, znode);
+ if (IS_ERR(znode)) {
+ err = PTR_ERR(znode);
+ goto out_unlock;
+ }
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * dbg_check_inode_size - check if inode size is correct.
+ * @c: UBIFS file-system description object
+ * @inode: inode to check
+ * @size: inode size
+ *
+ * This function makes sure that the inode size (@size) is correct and it does
+ * not have any pages beyond @size. Returns zero if the inode is OK, %-EINVAL
+ * if it has a data page beyond @size, and other negative error code in case of
+ * other errors.
+ */
+int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
+ loff_t size)
+{
+ int err, n;
+ union ubifs_key from_key, to_key, *key;
+ struct ubifs_znode *znode;
+ unsigned int block;
+
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+ if (!dbg_is_chk_gen(c))
+ return 0;
+
+ block = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
+ data_key_init(c, &from_key, inode->i_ino, block);
+ highest_data_key(c, &to_key, inode->i_ino);
+
+ mutex_lock(&c->tnc_mutex);
+ err = ubifs_lookup_level0(c, &from_key, &znode, &n);
+ if (err < 0)
+ goto out_unlock;
+
+ if (err) {
+ key = &from_key;
+ goto out_dump;
+ }
+
+ err = tnc_next(c, &znode, &n);
+ if (err == -ENOENT) {
+ err = 0;
+ goto out_unlock;
+ }
+ if (err < 0)
+ goto out_unlock;
+
+ ubifs_assert(c, err == 0);
+ key = &znode->zbranch[n].key;
+ if (!key_in_range(c, key, &from_key, &to_key))
+ goto out_unlock;
+
+out_dump:
+ block = key_block(c, key);
+ ubifs_err(c, "inode %lu has size %lld, but there are data at offset %lld",
+ (unsigned long)inode->i_ino, size,
+ ((loff_t)block) << UBIFS_BLOCK_SHIFT);
+ mutex_unlock(&c->tnc_mutex);
+ ubifs_dump_inode(c, inode);
+ dump_stack();
+ return -EINVAL;
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
diff --git a/ubifs-utils/libubifs/tnc_commit.c b/ubifs-utils/libubifs/tnc_commit.c
new file mode 100644
index 00000000..a55e0482
--- /dev/null
+++ b/ubifs-utils/libubifs/tnc_commit.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* This file implements TNC functions for committing */
+
+#include <linux/random.h>
+#include "ubifs.h"
+
+/**
+ * make_idx_node - make an index node for fill-the-gaps method of TNC commit.
+ * @c: UBIFS file-system description object
+ * @idx: buffer in which to place new index node
+ * @znode: znode from which to make new index node
+ * @lnum: LEB number where new index node will be written
+ * @offs: offset where new index node will be written
+ * @len: length of new index node
+ */
+static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
+ struct ubifs_znode *znode, int lnum, int offs, int len)
+{
+ struct ubifs_znode *zp;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+ int i, err;
+
+ /* Make index node */
+ idx->ch.node_type = UBIFS_IDX_NODE;
+ idx->child_cnt = cpu_to_le16(znode->child_cnt);
+ idx->level = cpu_to_le16(znode->level);
+ for (i = 0; i < znode->child_cnt; i++) {
+ struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
+ struct ubifs_zbranch *zbr = &znode->zbranch[i];
+
+ key_write_idx(c, &zbr->key, &br->key);
+ br->lnum = cpu_to_le32(zbr->lnum);
+ br->offs = cpu_to_le32(zbr->offs);
+ br->len = cpu_to_le32(zbr->len);
+ ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
+ if (!zbr->lnum || !zbr->len) {
+ ubifs_err(c, "bad ref in znode");
+ ubifs_dump_znode(c, znode);
+ if (zbr->znode)
+ ubifs_dump_znode(c, zbr->znode);
+
+ return -EINVAL;
+ }
+ }
+ ubifs_prepare_node(c, idx, len, 0);
+ ubifs_node_calc_hash(c, idx, hash);
+
+ znode->lnum = lnum;
+ znode->offs = offs;
+ znode->len = len;
+
+ err = insert_old_idx_znode(c, znode);
+
+ /* Update the parent */
+ zp = znode->parent;
+ if (zp) {
+ struct ubifs_zbranch *zbr;
+
+ zbr = &zp->zbranch[znode->iip];
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ ubifs_copy_hash(c, hash, zbr->hash);
+ } else {
+ c->zroot.lnum = lnum;
+ c->zroot.offs = offs;
+ c->zroot.len = len;
+ ubifs_copy_hash(c, hash, c->zroot.hash);
+ }
+ c->calc_idx_sz += ALIGN(len, 8);
+
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ ubifs_assert(c, ubifs_zn_dirty(znode));
+ ubifs_assert(c, ubifs_zn_cow(znode));
+
+ /*
+ * Note, unlike 'write_index()' we do not add memory barriers here
+ * because this function is called with @c->tnc_mutex locked.
+ */
+ __clear_bit(DIRTY_ZNODE, &znode->flags);
+ __clear_bit(COW_ZNODE, &znode->flags);
+
+ return err;
+}
+
+/**
+ * fill_gap - make index nodes in gaps in dirty index LEBs.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number that gap appears in
+ * @gap_start: offset of start of gap
+ * @gap_end: offset of end of gap
+ * @dirt: adds dirty space to this
+ *
+ * This function returns the number of index nodes written into the gap.
+ */
+static int fill_gap(struct ubifs_info *c, int lnum, int gap_start, int gap_end,
+ int *dirt)
+{
+ int len, gap_remains, gap_pos, written, pad_len;
+
+ ubifs_assert(c, (gap_start & 7) == 0);
+ ubifs_assert(c, (gap_end & 7) == 0);
+ ubifs_assert(c, gap_end >= gap_start);
+
+ gap_remains = gap_end - gap_start;
+ if (!gap_remains)
+ return 0;
+ gap_pos = gap_start;
+ written = 0;
+ while (c->enext) {
+ len = ubifs_idx_node_sz(c, c->enext->child_cnt);
+ if (len < gap_remains) {
+ struct ubifs_znode *znode = c->enext;
+ const int alen = ALIGN(len, 8);
+ int err;
+
+ ubifs_assert(c, alen <= gap_remains);
+ err = make_idx_node(c, c->ileb_buf + gap_pos, znode,
+ lnum, gap_pos, len);
+ if (err)
+ return err;
+ gap_remains -= alen;
+ gap_pos += alen;
+ c->enext = znode->cnext;
+ if (c->enext == c->cnext)
+ c->enext = NULL;
+ written += 1;
+ } else
+ break;
+ }
+ if (gap_end == c->leb_size) {
+ c->ileb_len = ALIGN(gap_pos, c->min_io_size);
+ /* Pad to end of min_io_size */
+ pad_len = c->ileb_len - gap_pos;
+ } else
+ /* Pad to end of gap */
+ pad_len = gap_remains;
+ dbg_gc("LEB %d:%d to %d len %d nodes written %d wasted bytes %d",
+ lnum, gap_start, gap_end, gap_end - gap_start, written, pad_len);
+ ubifs_pad(c, c->ileb_buf + gap_pos, pad_len);
+ *dirt += pad_len;
+ return written;
+}
+
+/**
+ * find_old_idx - find an index node obsoleted since the last commit start.
+ * @c: UBIFS file-system description object
+ * @lnum: LEB number of obsoleted index node
+ * @offs: offset of obsoleted index node
+ *
+ * Returns %1 if found and %0 otherwise.
+ */
+static int find_old_idx(struct ubifs_info *c, int lnum, int offs)
+{
+ struct ubifs_old_idx *o;
+ struct rb_node *p;
+
+ p = c->old_idx.rb_node;
+ while (p) {
+ o = rb_entry(p, struct ubifs_old_idx, rb);
+ if (lnum < o->lnum)
+ p = p->rb_left;
+ else if (lnum > o->lnum)
+ p = p->rb_right;
+ else if (offs < o->offs)
+ p = p->rb_left;
+ else if (offs > o->offs)
+ p = p->rb_right;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * is_idx_node_in_use - determine if an index node can be overwritten.
+ * @c: UBIFS file-system description object
+ * @key: key of index node
+ * @level: index node level
+ * @lnum: LEB number of index node
+ * @offs: offset of index node
+ *
+ * If @key / @lnum / @offs identify an index node that was not part of the old
+ * index, then this function returns %0 (obsolete). Else if the index node was
+ * part of the old index but is now dirty %1 is returned, else if it is clean %2
+ * is returned. A negative error code is returned on failure.
+ */
+static int is_idx_node_in_use(struct ubifs_info *c, union ubifs_key *key,
+ int level, int lnum, int offs)
+{
+ int ret;
+
+ ret = is_idx_node_in_tnc(c, key, level, lnum, offs);
+ if (ret < 0)
+ return ret; /* Error code */
+ if (ret == 0)
+ if (find_old_idx(c, lnum, offs))
+ return 1;
+ return ret;
+}
+
+/**
+ * layout_leb_in_gaps - layout index nodes using in-the-gaps method.
+ * @c: UBIFS file-system description object
+ * @p: return LEB number in @c->gap_lebs[p]
+ *
+ * This function lays out new index nodes for dirty znodes using in-the-gaps
+ * method of TNC commit.
+ * This function merely puts the next znode into the next gap, making no attempt
+ * to try to maximise the number of znodes that fit.
+ * This function returns the number of index nodes written into the gaps, or a
+ * negative error code on failure.
+ */
+static int layout_leb_in_gaps(struct ubifs_info *c, int p)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int lnum, dirt = 0, gap_start, gap_end, err, written, tot_written;
+
+ tot_written = 0;
+ /* Get an index LEB with lots of obsolete index nodes */
+ lnum = ubifs_find_dirty_idx_leb(c);
+ if (lnum < 0)
+ /*
+ * There also may be dirt in the index head that could be
+ * filled, however we do not check there at present.
+ */
+ return lnum; /* Error code */
+ c->gap_lebs[p] = lnum;
+ dbg_gc("LEB %d", lnum);
+ /*
+ * Scan the index LEB. We use the generic scan for this even though
+ * it is more comprehensive and less efficient than is needed for this
+ * purpose.
+ */
+ sleb = ubifs_scan(c, lnum, 0, c->ileb_buf, 0);
+ c->ileb_len = 0;
+ if (IS_ERR(sleb))
+ return PTR_ERR(sleb);
+ gap_start = 0;
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ struct ubifs_idx_node *idx;
+ int in_use, level;
+
+ ubifs_assert(c, snod->type == UBIFS_IDX_NODE);
+ idx = snod->node;
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ level = le16_to_cpu(idx->level);
+ /* Determine if the index node is in use (not obsolete) */
+ in_use = is_idx_node_in_use(c, &snod->key, level, lnum,
+ snod->offs);
+ if (in_use < 0) {
+ ubifs_scan_destroy(sleb);
+ return in_use; /* Error code */
+ }
+ if (in_use) {
+ if (in_use == 1)
+ dirt += ALIGN(snod->len, 8);
+ /*
+ * The obsolete index nodes form gaps that can be
+ * overwritten. This gap has ended because we have
+ * found an index node that is still in use
+ * i.e. not obsolete
+ */
+ gap_end = snod->offs;
+ /* Try to fill gap */
+ written = fill_gap(c, lnum, gap_start, gap_end, &dirt);
+ if (written < 0) {
+ ubifs_scan_destroy(sleb);
+ return written; /* Error code */
+ }
+ tot_written += written;
+ gap_start = ALIGN(snod->offs + snod->len, 8);
+ }
+ }
+ ubifs_scan_destroy(sleb);
+ c->ileb_len = c->leb_size;
+ gap_end = c->leb_size;
+ /* Try to fill gap */
+ written = fill_gap(c, lnum, gap_start, gap_end, &dirt);
+ if (written < 0)
+ return written; /* Error code */
+ tot_written += written;
+ if (tot_written == 0) {
+ struct ubifs_lprops lp;
+
+ dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written);
+ err = ubifs_read_one_lp(c, lnum, &lp);
+ if (err)
+ return err;
+ if (lp.free == c->leb_size) {
+ /*
+ * We must have snatched this LEB from the idx_gc list
+ * so we need to correct the free and dirty space.
+ */
+ err = ubifs_change_one_lp(c, lnum,
+ c->leb_size - c->ileb_len,
+ dirt, 0, 0, 0);
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+ err = ubifs_change_one_lp(c, lnum, c->leb_size - c->ileb_len, dirt,
+ 0, 0, 0);
+ if (err)
+ return err;
+ err = ubifs_leb_change(c, lnum, c->ileb_buf, c->ileb_len);
+ if (err)
+ return err;
+ dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written);
+ return tot_written;
+}
+
+/**
+ * get_leb_cnt - calculate the number of empty LEBs needed to commit.
+ * @c: UBIFS file-system description object
+ * @cnt: number of znodes to commit
+ *
+ * This function returns the number of empty LEBs needed to commit @cnt znodes
+ * to the current index head. The number is not exact and may be more than
+ * needed.
+ */
+static int get_leb_cnt(struct ubifs_info *c, int cnt)
+{
+ int d;
+
+ /* Assume maximum index node size (i.e. overestimate space needed) */
+ cnt -= (c->leb_size - c->ihead_offs) / c->max_idx_node_sz;
+ if (cnt < 0)
+ cnt = 0;
+ d = c->leb_size / c->max_idx_node_sz;
+ return DIV_ROUND_UP(cnt, d);
+}
+
+/**
+ * layout_in_gaps - in-the-gaps method of committing TNC.
+ * @c: UBIFS file-system description object
+ * @cnt: number of dirty znodes to commit.
+ *
+ * This function lays out new index nodes for dirty znodes using in-the-gaps
+ * method of TNC commit.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int layout_in_gaps(struct ubifs_info *c, int cnt)
+{
+ int err, leb_needed_cnt, written, p = 0, old_idx_lebs, *gap_lebs;
+
+ dbg_gc("%d znodes to write", cnt);
+
+ c->gap_lebs = kmalloc_array(c->lst.idx_lebs + 1, sizeof(int),
+ GFP_NOFS);
+ if (!c->gap_lebs)
+ return -ENOMEM;
+
+ old_idx_lebs = c->lst.idx_lebs;
+ do {
+ ubifs_assert(c, p < c->lst.idx_lebs);
+ written = layout_leb_in_gaps(c, p);
+ if (written < 0) {
+ err = written;
+ if (err != -ENOSPC) {
+ kfree(c->gap_lebs);
+ c->gap_lebs = NULL;
+ return err;
+ }
+ if (!dbg_is_chk_index(c)) {
+ /*
+ * Do not print scary warnings if the debugging
+ * option which forces in-the-gaps is enabled.
+ */
+ ubifs_warn(c, "out of space");
+ ubifs_dump_budg(c, &c->bi);
+ ubifs_dump_lprops(c);
+ }
+ /* Try to commit anyway */
+ break;
+ }
+ p++;
+ cnt -= written;
+ leb_needed_cnt = get_leb_cnt(c, cnt);
+ dbg_gc("%d znodes remaining, need %d LEBs, have %d", cnt,
+ leb_needed_cnt, c->ileb_cnt);
+ /*
+ * Dynamically change the size of @c->gap_lebs to prevent
+ * oob, because @c->lst.idx_lebs could be increased by
+ * function @get_idx_gc_leb (called by layout_leb_in_gaps->
+ * ubifs_find_dirty_idx_leb) during loop. Only enlarge
+ * @c->gap_lebs when needed.
+ *
+ */
+ if (leb_needed_cnt > c->ileb_cnt && p >= old_idx_lebs &&
+ old_idx_lebs < c->lst.idx_lebs) {
+ old_idx_lebs = c->lst.idx_lebs;
+ gap_lebs = krealloc(c->gap_lebs, sizeof(int) *
+ (old_idx_lebs + 1), GFP_NOFS);
+ if (!gap_lebs) {
+ kfree(c->gap_lebs);
+ c->gap_lebs = NULL;
+ return -ENOMEM;
+ }
+ c->gap_lebs = gap_lebs;
+ }
+ } while (leb_needed_cnt > c->ileb_cnt);
+
+ c->gap_lebs[p] = -1;
+ return 0;
+}
+
+/**
+ * layout_in_empty_space - layout index nodes in empty space.
+ * @c: UBIFS file-system description object
+ *
+ * This function lays out new index nodes for dirty znodes using empty LEBs.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int layout_in_empty_space(struct ubifs_info *c)
+{
+ struct ubifs_znode *znode, *cnext, *zp;
+ int lnum, offs, len, next_len, buf_len, buf_offs, used, avail;
+ int wlen, blen, err;
+
+ cnext = c->enext;
+ if (!cnext)
+ return 0;
+
+ lnum = c->ihead_lnum;
+ buf_offs = c->ihead_offs;
+
+ buf_len = ubifs_idx_node_sz(c, c->fanout);
+ buf_len = ALIGN(buf_len, c->min_io_size);
+ used = 0;
+ avail = buf_len;
+
+ /* Ensure there is enough room for first write */
+ next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
+ if (buf_offs + next_len > c->leb_size)
+ lnum = -1;
+
+ while (1) {
+ znode = cnext;
+
+ len = ubifs_idx_node_sz(c, znode->child_cnt);
+
+ /* Determine the index node position */
+ if (lnum == -1) {
+ if (c->ileb_nxt >= c->ileb_cnt) {
+ ubifs_err(c, "out of space");
+ return -ENOSPC;
+ }
+ lnum = c->ilebs[c->ileb_nxt++];
+ buf_offs = 0;
+ used = 0;
+ avail = buf_len;
+ }
+
+ offs = buf_offs + used;
+
+ znode->lnum = lnum;
+ znode->offs = offs;
+ znode->len = len;
+
+ /* Update the parent */
+ zp = znode->parent;
+ if (zp) {
+ struct ubifs_zbranch *zbr;
+ int i;
+
+ i = znode->iip;
+ zbr = &zp->zbranch[i];
+ zbr->lnum = lnum;
+ zbr->offs = offs;
+ zbr->len = len;
+ } else {
+ c->zroot.lnum = lnum;
+ c->zroot.offs = offs;
+ c->zroot.len = len;
+ }
+ c->calc_idx_sz += ALIGN(len, 8);
+
+ /*
+ * Once lprops is updated, we can decrease the dirty znode count
+ * but it is easier to just do it here.
+ */
+ atomic_long_dec(&c->dirty_zn_cnt);
+
+ /*
+ * Calculate the next index node length to see if there is
+ * enough room for it
+ */
+ cnext = znode->cnext;
+ if (cnext == c->cnext)
+ next_len = 0;
+ else
+ next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
+
+ /* Update buffer positions */
+ wlen = used + len;
+ used += ALIGN(len, 8);
+ avail -= ALIGN(len, 8);
+
+ if (next_len != 0 &&
+ buf_offs + used + next_len <= c->leb_size &&
+ avail > 0)
+ continue;
+
+ if (avail <= 0 && next_len &&
+ buf_offs + used + next_len <= c->leb_size)
+ blen = buf_len;
+ else
+ blen = ALIGN(wlen, c->min_io_size);
+
+ /* The buffer is full or there are no more znodes to do */
+ buf_offs += blen;
+ if (next_len) {
+ if (buf_offs + next_len > c->leb_size) {
+ err = ubifs_update_one_lp(c, lnum,
+ c->leb_size - buf_offs, blen - used,
+ 0, 0);
+ if (err)
+ return err;
+ lnum = -1;
+ }
+ used -= blen;
+ if (used < 0)
+ used = 0;
+ avail = buf_len - used;
+ continue;
+ }
+ err = ubifs_update_one_lp(c, lnum, c->leb_size - buf_offs,
+ blen - used, 0, 0);
+ if (err)
+ return err;
+ break;
+ }
+
+ c->dbg->new_ihead_lnum = lnum;
+ c->dbg->new_ihead_offs = buf_offs;
+
+ return 0;
+}
+
+/**
+ * layout_commit - determine positions of index nodes to commit.
+ * @c: UBIFS file-system description object
+ * @no_space: indicates that insufficient empty LEBs were allocated
+ * @cnt: number of znodes to commit
+ *
+ * Calculate and update the positions of index nodes to commit. If there were
+ * an insufficient number of empty LEBs allocated, then index nodes are placed
+ * into the gaps created by obsolete index nodes in non-empty index LEBs. For
+ * this purpose, an obsolete index node is one that was not in the index as at
+ * the end of the last commit. To write "in-the-gaps" requires that those index
+ * LEBs are updated atomically in-place.
+ */
+static int layout_commit(struct ubifs_info *c, int no_space, int cnt)
+{
+ int err;
+
+ if (no_space) {
+ err = layout_in_gaps(c, cnt);
+ if (err)
+ return err;
+ }
+ err = layout_in_empty_space(c);
+ return err;
+}
+
+/**
+ * find_first_dirty - find first dirty znode.
+ * @znode: znode to begin searching from
+ */
+static struct ubifs_znode *find_first_dirty(struct ubifs_znode *znode)
+{
+ int i, cont;
+
+ if (!znode)
+ return NULL;
+
+ while (1) {
+ if (znode->level == 0) {
+ if (ubifs_zn_dirty(znode))
+ return znode;
+ return NULL;
+ }
+ cont = 0;
+ for (i = 0; i < znode->child_cnt; i++) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[i];
+
+ if (zbr->znode && ubifs_zn_dirty(zbr->znode)) {
+ znode = zbr->znode;
+ cont = 1;
+ break;
+ }
+ }
+ if (!cont) {
+ if (ubifs_zn_dirty(znode))
+ return znode;
+ return NULL;
+ }
+ }
+}
+
+/**
+ * find_next_dirty - find next dirty znode.
+ * @znode: znode to begin searching from
+ */
+static struct ubifs_znode *find_next_dirty(struct ubifs_znode *znode)
+{
+ int n = znode->iip + 1;
+
+ znode = znode->parent;
+ if (!znode)
+ return NULL;
+ for (; n < znode->child_cnt; n++) {
+ struct ubifs_zbranch *zbr = &znode->zbranch[n];
+
+ if (zbr->znode && ubifs_zn_dirty(zbr->znode))
+ return find_first_dirty(zbr->znode);
+ }
+ return znode;
+}
+
+/**
+ * get_znodes_to_commit - create list of dirty znodes to commit.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the number of znodes to commit.
+ */
+static int get_znodes_to_commit(struct ubifs_info *c)
+{
+ struct ubifs_znode *znode, *cnext;
+ int cnt = 0;
+
+ c->cnext = find_first_dirty(c->zroot.znode);
+ znode = c->enext = c->cnext;
+ if (!znode) {
+ dbg_cmt("no znodes to commit");
+ return 0;
+ }
+ cnt += 1;
+ while (1) {
+ ubifs_assert(c, !ubifs_zn_cow(znode));
+ __set_bit(COW_ZNODE, &znode->flags);
+ znode->alt = 0;
+ cnext = find_next_dirty(znode);
+ if (!cnext) {
+ znode->cnext = c->cnext;
+ break;
+ }
+ znode->cparent = znode->parent;
+ znode->ciip = znode->iip;
+ znode->cnext = cnext;
+ znode = cnext;
+ cnt += 1;
+ }
+ dbg_cmt("committing %d znodes", cnt);
+ ubifs_assert(c, cnt == atomic_long_read(&c->dirty_zn_cnt));
+ return cnt;
+}
+
+/**
+ * alloc_idx_lebs - allocate empty LEBs to be used to commit.
+ * @c: UBIFS file-system description object
+ * @cnt: number of znodes to commit
+ *
+ * This function returns %-ENOSPC if it cannot allocate a sufficient number of
+ * empty LEBs. %0 is returned on success, otherwise a negative error code
+ * is returned.
+ */
+static int alloc_idx_lebs(struct ubifs_info *c, int cnt)
+{
+ int i, leb_cnt, lnum;
+
+ c->ileb_cnt = 0;
+ c->ileb_nxt = 0;
+ leb_cnt = get_leb_cnt(c, cnt);
+ dbg_cmt("need about %d empty LEBS for TNC commit", leb_cnt);
+ if (!leb_cnt)
+ return 0;
+ c->ilebs = kmalloc_array(leb_cnt, sizeof(int), GFP_NOFS);
+ if (!c->ilebs)
+ return -ENOMEM;
+ for (i = 0; i < leb_cnt; i++) {
+ lnum = ubifs_find_free_leb_for_idx(c);
+ if (lnum < 0)
+ return lnum;
+ c->ilebs[c->ileb_cnt++] = lnum;
+ dbg_cmt("LEB %d", lnum);
+ }
+ if (dbg_is_chk_index(c) && !get_random_u32_below(8))
+ return -ENOSPC;
+ return 0;
+}
+
+/**
+ * free_unused_idx_lebs - free unused LEBs that were allocated for the commit.
+ * @c: UBIFS file-system description object
+ *
+ * It is possible that we allocate more empty LEBs for the commit than we need.
+ * This functions frees the surplus.
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int free_unused_idx_lebs(struct ubifs_info *c)
+{
+ int i, err = 0, lnum, er;
+
+ for (i = c->ileb_nxt; i < c->ileb_cnt; i++) {
+ lnum = c->ilebs[i];
+ dbg_cmt("LEB %d", lnum);
+ er = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_INDEX | LPROPS_TAKEN, 0);
+ if (!err)
+ err = er;
+ }
+ return err;
+}
+
+/**
+ * free_idx_lebs - free unused LEBs after commit end.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+static int free_idx_lebs(struct ubifs_info *c)
+{
+ int err;
+
+ err = free_unused_idx_lebs(c);
+ kfree(c->ilebs);
+ c->ilebs = NULL;
+ return err;
+}
+
+/**
+ * ubifs_tnc_start_commit - start TNC commit.
+ * @c: UBIFS file-system description object
+ * @zroot: new index root position is returned here
+ *
+ * This function prepares the list of indexing nodes to commit and lays out
+ * their positions on flash. If there is not enough free space it uses the
+ * in-gap commit method. Returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot)
+{
+ int err = 0, cnt;
+
+ mutex_lock(&c->tnc_mutex);
+ err = dbg_check_tnc(c, 1);
+ if (err)
+ goto out;
+ cnt = get_znodes_to_commit(c);
+ if (cnt != 0) {
+ int no_space = 0;
+
+ err = alloc_idx_lebs(c, cnt);
+ if (err == -ENOSPC)
+ no_space = 1;
+ else if (err)
+ goto out_free;
+ err = layout_commit(c, no_space, cnt);
+ if (err)
+ goto out_free;
+ ubifs_assert(c, atomic_long_read(&c->dirty_zn_cnt) == 0);
+ err = free_unused_idx_lebs(c);
+ if (err)
+ goto out;
+ }
+ destroy_old_idx(c);
+ memcpy(zroot, &c->zroot, sizeof(struct ubifs_zbranch));
+
+ err = ubifs_save_dirty_idx_lnums(c);
+ if (err)
+ goto out;
+
+ spin_lock(&c->space_lock);
+ /*
+ * Although we have not finished committing yet, update size of the
+ * committed index ('c->bi.old_idx_sz') and zero out the index growth
+ * budget. It is OK to do this now, because we've reserved all the
+ * space which is needed to commit the index, and it is save for the
+ * budgeting subsystem to assume the index is already committed,
+ * even though it is not.
+ */
+ ubifs_assert(c, c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
+ c->bi.old_idx_sz = c->calc_idx_sz;
+ c->bi.uncommitted_idx = 0;
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+ spin_unlock(&c->space_lock);
+ mutex_unlock(&c->tnc_mutex);
+
+ dbg_cmt("number of index LEBs %d", c->lst.idx_lebs);
+ dbg_cmt("size of index %llu", c->calc_idx_sz);
+ return err;
+
+out_free:
+ free_idx_lebs(c);
+out:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * write_index - write index nodes.
+ * @c: UBIFS file-system description object
+ *
+ * This function writes the index nodes whose positions were laid out in the
+ * layout_in_empty_space function.
+ */
+static int write_index(struct ubifs_info *c)
+{
+ struct ubifs_idx_node *idx;
+ struct ubifs_znode *znode, *cnext;
+ int i, lnum, offs, len, next_len, buf_len, buf_offs, used;
+ int avail, wlen, err, lnum_pos = 0, blen, nxt_offs;
+
+ cnext = c->enext;
+ if (!cnext)
+ return 0;
+
+ /*
+ * Always write index nodes to the index head so that index nodes and
+ * other types of nodes are never mixed in the same erase block.
+ */
+ lnum = c->ihead_lnum;
+ buf_offs = c->ihead_offs;
+
+ /* Allocate commit buffer */
+ buf_len = ALIGN(c->max_idx_node_sz, c->min_io_size);
+ used = 0;
+ avail = buf_len;
+
+ /* Ensure there is enough room for first write */
+ next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
+ if (buf_offs + next_len > c->leb_size) {
+ err = ubifs_update_one_lp(c, lnum, LPROPS_NC, 0, 0,
+ LPROPS_TAKEN);
+ if (err)
+ return err;
+ lnum = -1;
+ }
+
+ while (1) {
+ u8 hash[UBIFS_HASH_ARR_SZ];
+
+ cond_resched();
+
+ znode = cnext;
+ idx = c->cbuf + used;
+
+ /* Make index node */
+ idx->ch.node_type = UBIFS_IDX_NODE;
+ idx->child_cnt = cpu_to_le16(znode->child_cnt);
+ idx->level = cpu_to_le16(znode->level);
+ for (i = 0; i < znode->child_cnt; i++) {
+ struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
+ struct ubifs_zbranch *zbr = &znode->zbranch[i];
+
+ key_write_idx(c, &zbr->key, &br->key);
+ br->lnum = cpu_to_le32(zbr->lnum);
+ br->offs = cpu_to_le32(zbr->offs);
+ br->len = cpu_to_le32(zbr->len);
+ ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
+ if (!zbr->lnum || !zbr->len) {
+ ubifs_err(c, "bad ref in znode");
+ ubifs_dump_znode(c, znode);
+ if (zbr->znode)
+ ubifs_dump_znode(c, zbr->znode);
+
+ return -EINVAL;
+ }
+ }
+ len = ubifs_idx_node_sz(c, znode->child_cnt);
+ ubifs_prepare_node(c, idx, len, 0);
+ ubifs_node_calc_hash(c, idx, hash);
+
+ mutex_lock(&c->tnc_mutex);
+
+ if (znode->cparent)
+ ubifs_copy_hash(c, hash,
+ znode->cparent->zbranch[znode->ciip].hash);
+
+ if (znode->parent) {
+ if (!ubifs_zn_obsolete(znode))
+ ubifs_copy_hash(c, hash,
+ znode->parent->zbranch[znode->iip].hash);
+ } else {
+ ubifs_copy_hash(c, hash, c->zroot.hash);
+ }
+
+ mutex_unlock(&c->tnc_mutex);
+
+ /* Determine the index node position */
+ if (lnum == -1) {
+ lnum = c->ilebs[lnum_pos++];
+ buf_offs = 0;
+ used = 0;
+ avail = buf_len;
+ }
+ offs = buf_offs + used;
+
+ if (lnum != znode->lnum || offs != znode->offs ||
+ len != znode->len) {
+ ubifs_err(c, "inconsistent znode posn");
+ return -EINVAL;
+ }
+
+ /* Grab some stuff from znode while we still can */
+ cnext = znode->cnext;
+
+ ubifs_assert(c, ubifs_zn_dirty(znode));
+ ubifs_assert(c, ubifs_zn_cow(znode));
+
+ /*
+ * It is important that other threads should see %DIRTY_ZNODE
+ * flag cleared before %COW_ZNODE. Specifically, it matters in
+ * the 'dirty_cow_znode()' function. This is the reason for the
+ * first barrier. Also, we want the bit changes to be seen to
+ * other threads ASAP, to avoid unnecessary copying, which is
+ * the reason for the second barrier.
+ */
+ clear_bit(DIRTY_ZNODE, &znode->flags);
+ smp_mb__before_atomic();
+ clear_bit(COW_ZNODE, &znode->flags);
+ smp_mb__after_atomic();
+
+ /*
+ * We have marked the znode as clean but have not updated the
+ * @c->clean_zn_cnt counter. If this znode becomes dirty again
+ * before 'free_obsolete_znodes()' is called, then
+ * @c->clean_zn_cnt will be decremented before it gets
+ * incremented (resulting in 2 decrements for the same znode).
+ * This means that @c->clean_zn_cnt may become negative for a
+ * while.
+ *
+ * Q: why we cannot increment @c->clean_zn_cnt?
+ * A: because we do not have the @c->tnc_mutex locked, and the
+ * following code would be racy and buggy:
+ *
+ * if (!ubifs_zn_obsolete(znode)) {
+ * atomic_long_inc(&c->clean_zn_cnt);
+ * atomic_long_inc(&ubifs_clean_zn_cnt);
+ * }
+ *
+ * Thus, we just delay the @c->clean_zn_cnt update until we
+ * have the mutex locked.
+ */
+
+ /* Do not access znode from this point on */
+
+ /* Update buffer positions */
+ wlen = used + len;
+ used += ALIGN(len, 8);
+ avail -= ALIGN(len, 8);
+
+ /*
+ * Calculate the next index node length to see if there is
+ * enough room for it
+ */
+ if (cnext == c->cnext)
+ next_len = 0;
+ else
+ next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
+
+ nxt_offs = buf_offs + used + next_len;
+ if (next_len && nxt_offs <= c->leb_size) {
+ if (avail > 0)
+ continue;
+ else
+ blen = buf_len;
+ } else {
+ wlen = ALIGN(wlen, 8);
+ blen = ALIGN(wlen, c->min_io_size);
+ ubifs_pad(c, c->cbuf + wlen, blen - wlen);
+ }
+
+ /* The buffer is full or there are no more znodes to do */
+ err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs, blen);
+ if (err)
+ return err;
+ buf_offs += blen;
+ if (next_len) {
+ if (nxt_offs > c->leb_size) {
+ err = ubifs_update_one_lp(c, lnum, LPROPS_NC, 0,
+ 0, LPROPS_TAKEN);
+ if (err)
+ return err;
+ lnum = -1;
+ }
+ used -= blen;
+ if (used < 0)
+ used = 0;
+ avail = buf_len - used;
+ memmove(c->cbuf, c->cbuf + blen, used);
+ continue;
+ }
+ break;
+ }
+
+ if (lnum != c->dbg->new_ihead_lnum ||
+ buf_offs != c->dbg->new_ihead_offs) {
+ ubifs_err(c, "inconsistent ihead");
+ return -EINVAL;
+ }
+
+ c->ihead_lnum = lnum;
+ c->ihead_offs = buf_offs;
+
+ return 0;
+}
+
+/**
+ * free_obsolete_znodes - free obsolete znodes.
+ * @c: UBIFS file-system description object
+ *
+ * At the end of commit end, obsolete znodes are freed.
+ */
+static void free_obsolete_znodes(struct ubifs_info *c)
+{
+ struct ubifs_znode *znode, *cnext;
+
+ cnext = c->cnext;
+ do {
+ znode = cnext;
+ cnext = znode->cnext;
+ if (ubifs_zn_obsolete(znode))
+ kfree(znode);
+ else {
+ znode->cnext = NULL;
+ atomic_long_inc(&c->clean_zn_cnt);
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+ }
+ } while (cnext != c->cnext);
+}
+
+/**
+ * return_gap_lebs - return LEBs used by the in-gap commit method.
+ * @c: UBIFS file-system description object
+ *
+ * This function clears the "taken" flag for the LEBs which were used by the
+ * "commit in-the-gaps" method.
+ */
+static int return_gap_lebs(struct ubifs_info *c)
+{
+ int *p, err;
+
+ if (!c->gap_lebs)
+ return 0;
+
+ dbg_cmt("");
+ for (p = c->gap_lebs; *p != -1; p++) {
+ err = ubifs_change_one_lp(c, *p, LPROPS_NC, LPROPS_NC, 0,
+ LPROPS_TAKEN, 0);
+ if (err)
+ return err;
+ }
+
+ kfree(c->gap_lebs);
+ c->gap_lebs = NULL;
+ return 0;
+}
+
+/**
+ * ubifs_tnc_end_commit - update the TNC for commit end.
+ * @c: UBIFS file-system description object
+ *
+ * Write the dirty znodes.
+ */
+int ubifs_tnc_end_commit(struct ubifs_info *c)
+{
+ int err;
+
+ if (!c->cnext)
+ return 0;
+
+ err = return_gap_lebs(c);
+ if (err)
+ return err;
+
+ err = write_index(c);
+ if (err)
+ return err;
+
+ mutex_lock(&c->tnc_mutex);
+
+ dbg_cmt("TNC height is %d", c->zroot.znode->level + 1);
+
+ free_obsolete_znodes(c);
+
+ c->cnext = NULL;
+ kfree(c->ilebs);
+ c->ilebs = NULL;
+
+ mutex_unlock(&c->tnc_mutex);
+
+ return 0;
+}
diff --git a/ubifs-utils/libubifs/tnc_misc.c b/ubifs-utils/libubifs/tnc_misc.c
new file mode 100644
index 00000000..d3f8a6aa
--- /dev/null
+++ b/ubifs-utils/libubifs/tnc_misc.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ *
+ * Authors: Adrian Hunter
+ * Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains miscelanious TNC-related functions shared betweend
+ * different files. This file does not form any logically separate TNC
+ * sub-system. The file was created because there is a lot of TNC code and
+ * putting it all in one file would make that file too big and unreadable.
+ */
+
+#include "ubifs.h"
+
+/**
+ * ubifs_tnc_levelorder_next - next TNC tree element in levelorder traversal.
+ * @c: UBIFS file-system description object
+ * @zr: root of the subtree to traverse
+ * @znode: previous znode
+ *
+ * This function implements levelorder TNC traversal. The LNC is ignored.
+ * Returns the next element or %NULL if @znode is already the last one.
+ */
+struct ubifs_znode *ubifs_tnc_levelorder_next(const struct ubifs_info *c,
+ struct ubifs_znode *zr,
+ struct ubifs_znode *znode)
+{
+ int level, iip, level_search = 0;
+ struct ubifs_znode *zn;
+
+ ubifs_assert(c, zr);
+
+ if (unlikely(!znode))
+ return zr;
+
+ if (unlikely(znode == zr)) {
+ if (znode->level == 0)
+ return NULL;
+ return ubifs_tnc_find_child(zr, 0);
+ }
+
+ level = znode->level;
+
+ iip = znode->iip;
+ while (1) {
+ ubifs_assert(c, znode->level <= zr->level);
+
+ /*
+ * First walk up until there is a znode with next branch to
+ * look at.
+ */
+ while (znode->parent != zr && iip >= znode->parent->child_cnt) {
+ znode = znode->parent;
+ iip = znode->iip;
+ }
+
+ if (unlikely(znode->parent == zr &&
+ iip >= znode->parent->child_cnt)) {
+ /* This level is done, switch to the lower one */
+ level -= 1;
+ if (level_search || level < 0)
+ /*
+ * We were already looking for znode at lower
+ * level ('level_search'). As we are here
+ * again, it just does not exist. Or all levels
+ * were finished ('level < 0').
+ */
+ return NULL;
+
+ level_search = 1;
+ iip = -1;
+ znode = ubifs_tnc_find_child(zr, 0);
+ ubifs_assert(c, znode);
+ }
+
+ /* Switch to the next index */
+ zn = ubifs_tnc_find_child(znode->parent, iip + 1);
+ if (!zn) {
+ /* No more children to look at, we have walk up */
+ iip = znode->parent->child_cnt;
+ continue;
+ }
+
+ /* Walk back down to the level we came from ('level') */
+ while (zn->level != level) {
+ znode = zn;
+ zn = ubifs_tnc_find_child(zn, 0);
+ if (!zn) {
+ /*
+ * This path is not too deep so it does not
+ * reach 'level'. Try next path.
+ */
+ iip = znode->iip;
+ break;
+ }
+ }
+
+ if (zn) {
+ ubifs_assert(c, zn->level >= 0);
+ return zn;
+ }
+ }
+}
+
+/**
+ * ubifs_search_zbranch - search znode branch.
+ * @c: UBIFS file-system description object
+ * @znode: znode to search in
+ * @key: key to search for
+ * @n: znode branch slot number is returned here
+ *
+ * This is a helper function which search branch with key @key in @znode using
+ * binary search. The result of the search may be:
+ * o exact match, then %1 is returned, and the slot number of the branch is
+ * stored in @n;
+ * o no exact match, then %0 is returned and the slot number of the left
+ * closest branch is returned in @n; the slot if all keys in this znode are
+ * greater than @key, then %-1 is returned in @n.
+ */
+int ubifs_search_zbranch(const struct ubifs_info *c,
+ const struct ubifs_znode *znode,
+ const union ubifs_key *key, int *n)
+{
+ int beg = 0, end = znode->child_cnt, mid;
+ int cmp;
+ const struct ubifs_zbranch *zbr = &znode->zbranch[0];
+
+ ubifs_assert(c, end > beg);
+
+ while (end > beg) {
+ mid = (beg + end) >> 1;
+ cmp = keys_cmp(c, key, &zbr[mid].key);
+ if (cmp > 0)
+ beg = mid + 1;
+ else if (cmp < 0)
+ end = mid;
+ else {
+ *n = mid;
+ return 1;
+ }
+ }
+
+ *n = end - 1;
+
+ /* The insert point is after *n */
+ ubifs_assert(c, *n >= -1 && *n < znode->child_cnt);
+ if (*n == -1)
+ ubifs_assert(c, keys_cmp(c, key, &zbr[0].key) < 0);
+ else
+ ubifs_assert(c, keys_cmp(c, key, &zbr[*n].key) > 0);
+ if (*n + 1 < znode->child_cnt)
+ ubifs_assert(c, keys_cmp(c, key, &zbr[*n + 1].key) < 0);
+
+ return 0;
+}
+
+/**
+ * ubifs_tnc_postorder_first - find first znode to do postorder tree traversal.
+ * @znode: znode to start at (root of the sub-tree to traverse)
+ *
+ * Find the lowest leftmost znode in a subtree of the TNC tree. The LNC is
+ * ignored.
+ */
+struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode)
+{
+ if (unlikely(!znode))
+ return NULL;
+
+ while (znode->level > 0) {
+ struct ubifs_znode *child;
+
+ child = ubifs_tnc_find_child(znode, 0);
+ if (!child)
+ return znode;
+ znode = child;
+ }
+
+ return znode;
+}
+
+/**
+ * ubifs_tnc_postorder_next - next TNC tree element in postorder traversal.
+ * @c: UBIFS file-system description object
+ * @znode: previous znode
+ *
+ * This function implements postorder TNC traversal. The LNC is ignored.
+ * Returns the next element or %NULL if @znode is already the last one.
+ */
+struct ubifs_znode *ubifs_tnc_postorder_next(const struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn;
+
+ ubifs_assert(c, znode);
+ if (unlikely(!znode->parent))
+ return NULL;
+
+ /* Switch to the next index in the parent */
+ zn = ubifs_tnc_find_child(znode->parent, znode->iip + 1);
+ if (!zn)
+ /* This is in fact the last child, return parent */
+ return znode->parent;
+
+ /* Go to the first znode in this new subtree */
+ return ubifs_tnc_postorder_first(zn);
+}
+
+/**
+ * ubifs_destroy_tnc_subtree - destroy all znodes connected to a subtree.
+ * @c: UBIFS file-system description object
+ * @znode: znode defining subtree to destroy
+ *
+ * This function destroys subtree of the TNC tree. Returns number of clean
+ * znodes in the subtree.
+ */
+long ubifs_destroy_tnc_subtree(const struct ubifs_info *c,
+ struct ubifs_znode *znode)
+{
+ struct ubifs_znode *zn = ubifs_tnc_postorder_first(znode);
+ long clean_freed = 0;
+ int n;
+
+ ubifs_assert(c, zn);
+ while (1) {
+ for (n = 0; n < zn->child_cnt; n++) {
+ if (!zn->zbranch[n].znode)
+ continue;
+
+ if (zn->level > 0 &&
+ !ubifs_zn_dirty(zn->zbranch[n].znode))
+ clean_freed += 1;
+
+ cond_resched();
+ kfree(zn->zbranch[n].znode);
+ }
+
+ if (zn == znode) {
+ if (!ubifs_zn_dirty(zn))
+ clean_freed += 1;
+ kfree(zn);
+ return clean_freed;
+ }
+
+ zn = ubifs_tnc_postorder_next(c, zn);
+ }
+}
+
+/**
+ * ubifs_destroy_tnc_tree - destroy all znodes connected to the TNC tree.
+ * @c: UBIFS file-system description object
+ *
+ * This function destroys the whole TNC tree and updates clean global znode
+ * count.
+ */
+void ubifs_destroy_tnc_tree(struct ubifs_info *c)
+{
+ long n, freed;
+
+ if (!c->zroot.znode)
+ return;
+
+ n = atomic_long_read(&c->clean_zn_cnt);
+ freed = ubifs_destroy_tnc_subtree(c, c->zroot.znode);
+ ubifs_assert(c, freed == n);
+ atomic_long_sub(n, &ubifs_clean_zn_cnt);
+
+ c->zroot.znode = NULL;
+}
+
+/**
+ * read_znode - read an indexing node from flash and fill znode.
+ * @c: UBIFS file-system description object
+ * @zzbr: the zbranch describing the node to read
+ * @znode: znode to read to
+ *
+ * This function reads an indexing node from the flash media and fills znode
+ * with the read data. Returns zero in case of success and a negative error
+ * code in case of failure. The read indexing node is validated and if anything
+ * is wrong with it, this function prints complaint messages and returns
+ * %-EINVAL.
+ */
+static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
+ struct ubifs_znode *znode)
+{
+ int lnum = zzbr->lnum;
+ int offs = zzbr->offs;
+ int len = zzbr->len;
+ int i, err, type, cmp;
+ struct ubifs_idx_node *idx;
+
+ idx = kmalloc(c->max_idx_node_sz, GFP_NOFS);
+ if (!idx)
+ return -ENOMEM;
+
+ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
+ if (err < 0) {
+ kfree(idx);
+ return err;
+ }
+
+ err = ubifs_node_check_hash(c, idx, zzbr->hash);
+ if (err) {
+ ubifs_bad_hash(c, idx, zzbr->hash, lnum, offs);
+ kfree(idx);
+ return err;
+ }
+
+ znode->child_cnt = le16_to_cpu(idx->child_cnt);
+ znode->level = le16_to_cpu(idx->level);
+
+ dbg_tnc("LEB %d:%d, level %d, %d branch",
+ lnum, offs, znode->level, znode->child_cnt);
+
+ if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) {
+ ubifs_err(c, "current fanout %d, branch count %d",
+ c->fanout, znode->child_cnt);
+ ubifs_err(c, "max levels %d, znode level %d",
+ UBIFS_MAX_LEVELS, znode->level);
+ err = 1;
+ goto out_dump;
+ }
+
+ for (i = 0; i < znode->child_cnt; i++) {
+ struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
+ struct ubifs_zbranch *zbr = &znode->zbranch[i];
+
+ key_read(c, &br->key, &zbr->key);
+ zbr->lnum = le32_to_cpu(br->lnum);
+ zbr->offs = le32_to_cpu(br->offs);
+ zbr->len = le32_to_cpu(br->len);
+ ubifs_copy_hash(c, ubifs_branch_hash(c, br), zbr->hash);
+ zbr->znode = NULL;
+
+ /* Validate branch */
+
+ if (zbr->lnum < c->main_first ||
+ zbr->lnum >= c->leb_cnt || zbr->offs < 0 ||
+ zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) {
+ ubifs_err(c, "bad branch %d", i);
+ err = 2;
+ goto out_dump;
+ }
+
+ switch (key_type(c, &zbr->key)) {
+ case UBIFS_INO_KEY:
+ case UBIFS_DATA_KEY:
+ case UBIFS_DENT_KEY:
+ case UBIFS_XENT_KEY:
+ break;
+ default:
+ ubifs_err(c, "bad key type at slot %d: %d",
+ i, key_type(c, &zbr->key));
+ err = 3;
+ goto out_dump;
+ }
+
+ if (znode->level)
+ continue;
+
+ type = key_type(c, &zbr->key);
+ if (c->ranges[type].max_len == 0) {
+ if (zbr->len != c->ranges[type].len) {
+ ubifs_err(c, "bad target node (type %d) length (%d)",
+ type, zbr->len);
+ ubifs_err(c, "have to be %d", c->ranges[type].len);
+ err = 4;
+ goto out_dump;
+ }
+ } else if (zbr->len < c->ranges[type].min_len ||
+ zbr->len > c->ranges[type].max_len) {
+ ubifs_err(c, "bad target node (type %d) length (%d)",
+ type, zbr->len);
+ ubifs_err(c, "have to be in range of %d-%d",
+ c->ranges[type].min_len,
+ c->ranges[type].max_len);
+ err = 5;
+ goto out_dump;
+ }
+ }
+
+ /*
+ * Ensure that the next key is greater or equivalent to the
+ * previous one.
+ */
+ for (i = 0; i < znode->child_cnt - 1; i++) {
+ const union ubifs_key *key1, *key2;
+
+ key1 = &znode->zbranch[i].key;
+ key2 = &znode->zbranch[i + 1].key;
+
+ cmp = keys_cmp(c, key1, key2);
+ if (cmp > 0) {
+ ubifs_err(c, "bad key order (keys %d and %d)", i, i + 1);
+ err = 6;
+ goto out_dump;
+ } else if (cmp == 0 && !is_hash_key(c, key1)) {
+ /* These can only be keys with colliding hash */
+ ubifs_err(c, "keys %d and %d are not hashed but equivalent",
+ i, i + 1);
+ err = 7;
+ goto out_dump;
+ }
+ }
+
+ kfree(idx);
+ return 0;
+
+out_dump:
+ ubifs_err(c, "bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
+ ubifs_dump_node(c, idx, c->max_idx_node_sz);
+ kfree(idx);
+ return -EINVAL;
+}
+
+/**
+ * ubifs_load_znode - load znode to TNC cache.
+ * @c: UBIFS file-system description object
+ * @zbr: znode branch
+ * @parent: znode's parent
+ * @iip: index in parent
+ *
+ * This function loads znode pointed to by @zbr into the TNC cache and
+ * returns pointer to it in case of success and a negative error code in case
+ * of failure.
+ */
+struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ struct ubifs_znode *parent, int iip)
+{
+ int err;
+ struct ubifs_znode *znode;
+
+ ubifs_assert(c, !zbr->znode);
+ /*
+ * A slab cache is not presently used for znodes because the znode size
+ * depends on the fanout which is stored in the superblock.
+ */
+ znode = kzalloc(c->max_znode_sz, GFP_NOFS);
+ if (!znode)
+ return ERR_PTR(-ENOMEM);
+
+ err = read_znode(c, zbr, znode);
+ if (err)
+ goto out;
+
+ atomic_long_inc(&c->clean_zn_cnt);
+
+ /*
+ * Increment the global clean znode counter as well. It is OK that
+ * global and per-FS clean znode counters may be inconsistent for some
+ * short time (because we might be preempted at this point), the global
+ * one is only used in shrinker.
+ */
+ atomic_long_inc(&ubifs_clean_zn_cnt);
+
+ zbr->znode = znode;
+ znode->parent = parent;
+ znode->time = ktime_get_seconds();
+ znode->iip = iip;
+
+ return znode;
+
+out:
+ kfree(znode);
+ return ERR_PTR(err);
+}
+
+/**
+ * ubifs_tnc_read_node - read a leaf node from the flash media.
+ * @c: UBIFS file-system description object
+ * @zbr: key and position of the node
+ * @node: node is returned here
+ *
+ * This function reads a node defined by @zbr from the flash media. Returns
+ * zero in case of success or a negative error code in case of failure.
+ */
+int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
+{
+ union ubifs_key key1, *key = &zbr->key;
+ int err, type = key_type(c, key);
+ struct ubifs_wbuf *wbuf;
+
+ /*
+ * 'zbr' has to point to on-flash node. The node may sit in a bud and
+ * may even be in a write buffer, so we have to take care about this.
+ */
+ wbuf = ubifs_get_wbuf(c, zbr->lnum);
+ if (wbuf)
+ err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len,
+ zbr->lnum, zbr->offs);
+ else
+ err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum,
+ zbr->offs);
+
+ if (err) {
+ dbg_tnck(key, "key ");
+ return err;
+ }
+
+ /* Make sure the key of the read node is correct */
+ key_read(c, node + UBIFS_KEY_OFFSET, &key1);
+ if (!keys_eq(c, key, &key1)) {
+ ubifs_err(c, "bad key in node at LEB %d:%d",
+ zbr->lnum, zbr->offs);
+ dbg_tnck(key, "looked for key ");
+ dbg_tnck(&key1, "but found node's key ");
+ ubifs_dump_node(c, node, zbr->len);
+ return -EINVAL;
+ }
+
+ err = ubifs_node_check_hash(c, node, zbr->hash);
+ if (err) {
+ ubifs_bad_hash(c, node, zbr->hash, zbr->lnum, zbr->offs);
+ return err;
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
new file mode 100644
index 00000000..4226b21e
--- /dev/null
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -0,0 +1,2164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Adrian Hunter
+ */
+
+#ifndef __UBIFS_H__
+#define __UBIFS_H__
+
+#include <asm/div64.h>
+#include <linux/statfs.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/mtd/ubi.h>
+#include <linux/pagemap.h>
+#include <linux/backing-dev.h>
+#include <linux/security.h>
+#include <linux/xattr.h>
+#include <linux/random.h>
+#include <linux/sysfs.h>
+#include <linux/completion.h>
+#include <crypto/hash_info.h>
+#include <crypto/hash.h>
+#include <crypto/utils.h>
+
+#include <linux/fscrypt.h>
+
+#include "ubifs-media.h"
+
+/* Version of this UBIFS implementation */
+#define UBIFS_VERSION 1
+
+/* UBIFS file system VFS magic number */
+#define UBIFS_SUPER_MAGIC 0x24051905
+
+/* Number of UBIFS blocks per VFS page */
+#define UBIFS_BLOCKS_PER_PAGE (PAGE_SIZE / UBIFS_BLOCK_SIZE)
+#define UBIFS_BLOCKS_PER_PAGE_SHIFT (PAGE_SHIFT - UBIFS_BLOCK_SHIFT)
+
+/* "File system end of life" sequence number watermark */
+#define SQNUM_WARN_WATERMARK 0xFFFFFFFF00000000ULL
+#define SQNUM_WATERMARK 0xFFFFFFFFFF000000ULL
+
+/*
+ * Minimum amount of LEBs reserved for the index. At present the index needs at
+ * least 2 LEBs: one for the index head and one for in-the-gaps method (which
+ * currently does not cater for the index head and so excludes it from
+ * consideration).
+ */
+#define MIN_INDEX_LEBS 2
+
+/* Minimum amount of data UBIFS writes to the flash */
+#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8)
+
+/*
+ * Currently we do not support inode number overlapping and re-using, so this
+ * watermark defines dangerous inode number level. This should be fixed later,
+ * although it is difficult to exceed current limit. Another option is to use
+ * 64-bit inode numbers, but this means more overhead.
+ */
+#define INUM_WARN_WATERMARK 0xFFF00000
+#define INUM_WATERMARK 0xFFFFFF00
+
+/* Maximum number of entries in each LPT (LEB category) heap */
+#define LPT_HEAP_SZ 256
+
+/*
+ * Background thread name pattern. The numbers are UBI device and volume
+ * numbers.
+ */
+#define BGT_NAME_PATTERN "ubifs_bgt%d_%d"
+
+/* Maximum possible inode number (only 32-bit inodes are supported now) */
+#define MAX_INUM 0xFFFFFFFF
+
+/* Number of non-data journal heads */
+#define NONDATA_JHEADS_CNT 2
+
+/* Shorter names for journal head numbers for internal usage */
+#define GCHD UBIFS_GC_HEAD
+#define BASEHD UBIFS_BASE_HEAD
+#define DATAHD UBIFS_DATA_HEAD
+
+/* 'No change' value for 'ubifs_change_lp()' */
+#define LPROPS_NC 0x80000001
+
+/*
+ * There is no notion of truncation key because truncation nodes do not exist
+ * in TNC. However, when replaying, it is handy to introduce fake "truncation"
+ * keys for truncation nodes because the code becomes simpler. So we define
+ * %UBIFS_TRUN_KEY type.
+ *
+ * But otherwise, out of the journal reply scope, the truncation keys are
+ * invalid.
+ */
+#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT
+#define UBIFS_INVALID_KEY UBIFS_KEY_TYPES_CNT
+
+/*
+ * How much a directory entry/extended attribute entry adds to the parent/host
+ * inode.
+ */
+#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1, 8)
+
+/* How much an extended attribute adds to the host inode */
+#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8)
+
+/*
+ * Znodes which were not touched for 'OLD_ZNODE_AGE' seconds are considered
+ * "old", and znode which were touched last 'YOUNG_ZNODE_AGE' seconds ago are
+ * considered "young". This is used by shrinker when selecting znode to trim
+ * off.
+ */
+#define OLD_ZNODE_AGE 20
+#define YOUNG_ZNODE_AGE 5
+
+/*
+ * Some compressors, like LZO, may end up with more data then the input buffer.
+ * So UBIFS always allocates larger output buffer, to be sure the compressor
+ * will not corrupt memory in case of worst case compression.
+ */
+#define WORST_COMPR_FACTOR 2
+
+#ifdef CONFIG_FS_ENCRYPTION
+#define UBIFS_CIPHER_BLOCK_SIZE FSCRYPT_CONTENTS_ALIGNMENT
+#else
+#define UBIFS_CIPHER_BLOCK_SIZE 0
+#endif
+
+/*
+ * How much memory is needed for a buffer where we compress a data node.
+ */
+#define COMPRESSED_DATA_NODE_BUF_SZ \
+ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR)
+
+/* Maximum expected tree height for use by bottom_up_buf */
+#define BOTTOM_UP_HEIGHT 64
+
+/* Maximum number of data nodes to bulk-read */
+#define UBIFS_MAX_BULK_READ 32
+
+#ifdef CONFIG_UBIFS_FS_AUTHENTICATION
+#define UBIFS_HASH_ARR_SZ UBIFS_MAX_HASH_LEN
+#define UBIFS_HMAC_ARR_SZ UBIFS_MAX_HMAC_LEN
+#else
+#define UBIFS_HASH_ARR_SZ 0
+#define UBIFS_HMAC_ARR_SZ 0
+#endif
+
+/*
+ * The UBIFS sysfs directory name pattern and maximum name length (3 for "ubi"
+ * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
+ */
+#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
+#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
+
+/*
+ * Lockdep classes for UBIFS inode @ui_mutex.
+ */
+enum {
+ WB_MUTEX_1 = 0,
+ WB_MUTEX_2 = 1,
+ WB_MUTEX_3 = 2,
+ WB_MUTEX_4 = 3,
+};
+
+/*
+ * Znode flags (actually, bit numbers which store the flags).
+ *
+ * DIRTY_ZNODE: znode is dirty
+ * COW_ZNODE: znode is being committed and a new instance of this znode has to
+ * be created before changing this znode
+ * OBSOLETE_ZNODE: znode is obsolete, which means it was deleted, but it is
+ * still in the commit list and the ongoing commit operation
+ * will commit it, and delete this znode after it is done
+ */
+enum {
+ DIRTY_ZNODE = 0,
+ COW_ZNODE = 1,
+ OBSOLETE_ZNODE = 2,
+};
+
+/*
+ * Commit states.
+ *
+ * COMMIT_RESTING: commit is not wanted
+ * COMMIT_BACKGROUND: background commit has been requested
+ * COMMIT_REQUIRED: commit is required
+ * COMMIT_RUNNING_BACKGROUND: background commit is running
+ * COMMIT_RUNNING_REQUIRED: commit is running and it is required
+ * COMMIT_BROKEN: commit failed
+ */
+enum {
+ COMMIT_RESTING = 0,
+ COMMIT_BACKGROUND,
+ COMMIT_REQUIRED,
+ COMMIT_RUNNING_BACKGROUND,
+ COMMIT_RUNNING_REQUIRED,
+ COMMIT_BROKEN,
+};
+
+/*
+ * 'ubifs_scan_a_node()' return values.
+ *
+ * SCANNED_GARBAGE: scanned garbage
+ * SCANNED_EMPTY_SPACE: scanned empty space
+ * SCANNED_A_NODE: scanned a valid node
+ * SCANNED_A_CORRUPT_NODE: scanned a corrupted node
+ * SCANNED_A_BAD_PAD_NODE: scanned a padding node with invalid pad length
+ *
+ * Greater than zero means: 'scanned that number of padding bytes'
+ */
+enum {
+ SCANNED_GARBAGE = 0,
+ SCANNED_EMPTY_SPACE = -1,
+ SCANNED_A_NODE = -2,
+ SCANNED_A_CORRUPT_NODE = -3,
+ SCANNED_A_BAD_PAD_NODE = -4,
+};
+
+/*
+ * LPT cnode flag bits.
+ *
+ * DIRTY_CNODE: cnode is dirty
+ * OBSOLETE_CNODE: cnode is being committed and has been copied (or deleted),
+ * so it can (and must) be freed when the commit is finished
+ * COW_CNODE: cnode is being committed and must be copied before writing
+ */
+enum {
+ DIRTY_CNODE = 0,
+ OBSOLETE_CNODE = 1,
+ COW_CNODE = 2,
+};
+
+/*
+ * Dirty flag bits (lpt_drty_flgs) for LPT special nodes.
+ *
+ * LTAB_DIRTY: ltab node is dirty
+ * LSAVE_DIRTY: lsave node is dirty
+ */
+enum {
+ LTAB_DIRTY = 1,
+ LSAVE_DIRTY = 2,
+};
+
+/*
+ * Return codes used by the garbage collector.
+ * @LEB_FREED: the logical eraseblock was freed and is ready to use
+ * @LEB_FREED_IDX: indexing LEB was freed and can be used only after the commit
+ * @LEB_RETAINED: the logical eraseblock was freed and retained for GC purposes
+ */
+enum {
+ LEB_FREED,
+ LEB_FREED_IDX,
+ LEB_RETAINED,
+};
+
+/*
+ * Action taken upon a failed ubifs_assert().
+ * @ASSACT_REPORT: just report the failed assertion
+ * @ASSACT_RO: switch to read-only mode
+ * @ASSACT_PANIC: call BUG() and possible panic the kernel
+ */
+enum {
+ ASSACT_REPORT = 0,
+ ASSACT_RO,
+ ASSACT_PANIC,
+};
+
+/**
+ * struct ubifs_old_idx - index node obsoleted since last commit start.
+ * @rb: rb-tree node
+ * @lnum: LEB number of obsoleted index node
+ * @offs: offset of obsoleted index node
+ */
+struct ubifs_old_idx {
+ struct rb_node rb;
+ int lnum;
+ int offs;
+};
+
+/* The below union makes it easier to deal with keys */
+union ubifs_key {
+ uint8_t u8[UBIFS_SK_LEN];
+ uint32_t u32[UBIFS_SK_LEN/4];
+ uint64_t u64[UBIFS_SK_LEN/8];
+ __le32 j32[UBIFS_SK_LEN/4];
+};
+
+/**
+ * struct ubifs_scan_node - UBIFS scanned node information.
+ * @list: list of scanned nodes
+ * @key: key of node scanned (if it has one)
+ * @sqnum: sequence number
+ * @type: type of node scanned
+ * @offs: offset with LEB of node scanned
+ * @len: length of node scanned
+ * @node: raw node
+ */
+struct ubifs_scan_node {
+ struct list_head list;
+ union ubifs_key key;
+ unsigned long long sqnum;
+ int type;
+ int offs;
+ int len;
+ void *node;
+};
+
+/**
+ * struct ubifs_scan_leb - UBIFS scanned LEB information.
+ * @lnum: logical eraseblock number
+ * @nodes_cnt: number of nodes scanned
+ * @nodes: list of struct ubifs_scan_node
+ * @endpt: end point (and therefore the start of empty space)
+ * @buf: buffer containing entire LEB scanned
+ */
+struct ubifs_scan_leb {
+ int lnum;
+ int nodes_cnt;
+ struct list_head nodes;
+ int endpt;
+ void *buf;
+};
+
+/**
+ * struct ubifs_gced_idx_leb - garbage-collected indexing LEB.
+ * @list: list
+ * @lnum: LEB number
+ * @unmap: OK to unmap this LEB
+ *
+ * This data structure is used to temporary store garbage-collected indexing
+ * LEBs - they are not released immediately, but only after the next commit.
+ * This is needed to guarantee recoverability.
+ */
+struct ubifs_gced_idx_leb {
+ struct list_head list;
+ int lnum;
+ int unmap;
+};
+
+/**
+ * struct ubifs_inode - UBIFS in-memory inode description.
+ * @vfs_inode: VFS inode description object
+ * @creat_sqnum: sequence number at time of creation
+ * @del_cmtno: commit number corresponding to the time the inode was deleted,
+ * protected by @c->commit_sem;
+ * @xattr_size: summarized size of all extended attributes in bytes
+ * @xattr_cnt: count of extended attributes this inode has
+ * @xattr_names: sum of lengths of all extended attribute names belonging to
+ * this inode
+ * @dirty: non-zero if the inode is dirty
+ * @xattr: non-zero if this is an extended attribute inode
+ * @bulk_read: non-zero if bulk-read should be used
+ * @ui_mutex: serializes inode write-back with the rest of VFS operations,
+ * serializes "clean <-> dirty" state changes, serializes bulk-read,
+ * protects @dirty, @bulk_read, @ui_size, and @xattr_size
+ * @xattr_sem: serilizes write operations (remove|set|create) on xattr
+ * @ui_lock: protects @synced_i_size
+ * @synced_i_size: synchronized size of inode, i.e. the value of inode size
+ * currently stored on the flash; used only for regular file
+ * inodes
+ * @ui_size: inode size used by UBIFS when writing to flash
+ * @flags: inode flags (@UBIFS_COMPR_FL, etc)
+ * @compr_type: default compression type used for this inode
+ * @last_page_read: page number of last page read (for bulk read)
+ * @read_in_a_row: number of consecutive pages read in a row (for bulk read)
+ * @data_len: length of the data attached to the inode
+ * @data: inode's data
+ *
+ * @ui_mutex exists for two main reasons. At first it prevents inodes from
+ * being written back while UBIFS changing them, being in the middle of an VFS
+ * operation. This way UBIFS makes sure the inode fields are consistent. For
+ * example, in 'ubifs_rename()' we change 4 inodes simultaneously, and
+ * write-back must not write any of them before we have finished.
+ *
+ * The second reason is budgeting - UBIFS has to budget all operations. If an
+ * operation is going to mark an inode dirty, it has to allocate budget for
+ * this. It cannot just mark it dirty because there is no guarantee there will
+ * be enough flash space to write the inode back later. This means UBIFS has
+ * to have full control over inode "clean <-> dirty" transitions (and pages
+ * actually). But unfortunately, VFS marks inodes dirty in many places, and it
+ * does not ask the file-system if it is allowed to do so (there is a notifier,
+ * but it is not enough), i.e., there is no mechanism to synchronize with this.
+ * So UBIFS has its own inode dirty flag and its own mutex to serialize
+ * "clean <-> dirty" transitions.
+ *
+ * The @synced_i_size field is used to make sure we never write pages which are
+ * beyond last synchronized inode size. See 'ubifs_writepage()' for more
+ * information.
+ *
+ * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
+ * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
+ * make sure @inode->i_size is always changed under @ui_mutex, because it
+ * cannot call 'truncate_setsize()' with @ui_mutex locked, because it would
+ * deadlock with 'ubifs_writepage()' (see file.c). All the other inode fields
+ * are changed under @ui_mutex, so they do not need "shadow" fields. Note, one
+ * could consider to rework locking and base it on "shadow" fields.
+ */
+struct ubifs_inode {
+ struct inode vfs_inode;
+ unsigned long long creat_sqnum;
+ unsigned long long del_cmtno;
+ unsigned int xattr_size;
+ unsigned int xattr_cnt;
+ unsigned int xattr_names;
+ unsigned int dirty:1;
+ unsigned int xattr:1;
+ unsigned int bulk_read:1;
+ unsigned int compr_type:2;
+ struct mutex ui_mutex;
+ struct rw_semaphore xattr_sem;
+ spinlock_t ui_lock;
+ loff_t synced_i_size;
+ loff_t ui_size;
+ int flags;
+ pgoff_t last_page_read;
+ pgoff_t read_in_a_row;
+ int data_len;
+ void *data;
+};
+
+/**
+ * struct ubifs_unclean_leb - records a LEB recovered under read-only mode.
+ * @list: list
+ * @lnum: LEB number of recovered LEB
+ * @endpt: offset where recovery ended
+ *
+ * This structure records a LEB identified during recovery that needs to be
+ * cleaned but was not because UBIFS was mounted read-only. The information
+ * is used to clean the LEB when remounting to read-write mode.
+ */
+struct ubifs_unclean_leb {
+ struct list_head list;
+ int lnum;
+ int endpt;
+};
+
+/*
+ * LEB properties flags.
+ *
+ * LPROPS_UNCAT: not categorized
+ * LPROPS_DIRTY: dirty > free, dirty >= @c->dead_wm, not index
+ * LPROPS_DIRTY_IDX: dirty + free > @c->min_idx_node_sze and index
+ * LPROPS_FREE: free > 0, dirty < @c->dead_wm, not empty, not index
+ * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs
+ * LPROPS_EMPTY: LEB is empty, not taken
+ * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken
+ * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken
+ * LPROPS_CAT_MASK: mask for the LEB categories above
+ * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media)
+ * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash)
+ */
+enum {
+ LPROPS_UNCAT = 0,
+ LPROPS_DIRTY = 1,
+ LPROPS_DIRTY_IDX = 2,
+ LPROPS_FREE = 3,
+ LPROPS_HEAP_CNT = 3,
+ LPROPS_EMPTY = 4,
+ LPROPS_FREEABLE = 5,
+ LPROPS_FRDI_IDX = 6,
+ LPROPS_CAT_MASK = 15,
+ LPROPS_TAKEN = 16,
+ LPROPS_INDEX = 32,
+};
+
+/**
+ * struct ubifs_lprops - logical eraseblock properties.
+ * @free: amount of free space in bytes
+ * @dirty: amount of dirty space in bytes
+ * @flags: LEB properties flags (see above)
+ * @lnum: LEB number
+ * @list: list of same-category lprops (for LPROPS_EMPTY and LPROPS_FREEABLE)
+ * @hpos: heap position in heap of same-category lprops (other categories)
+ */
+struct ubifs_lprops {
+ int free;
+ int dirty;
+ int flags;
+ int lnum;
+ union {
+ struct list_head list;
+ int hpos;
+ };
+};
+
+/**
+ * struct ubifs_lpt_lprops - LPT logical eraseblock properties.
+ * @free: amount of free space in bytes
+ * @dirty: amount of dirty space in bytes
+ * @tgc: trivial GC flag (1 => unmap after commit end)
+ * @cmt: commit flag (1 => reserved for commit)
+ */
+struct ubifs_lpt_lprops {
+ int free;
+ int dirty;
+ unsigned tgc:1;
+ unsigned cmt:1;
+};
+
+/**
+ * struct ubifs_lp_stats - statistics of eraseblocks in the main area.
+ * @empty_lebs: number of empty LEBs
+ * @taken_empty_lebs: number of taken LEBs
+ * @idx_lebs: number of indexing LEBs
+ * @total_free: total free space in bytes (includes all LEBs)
+ * @total_dirty: total dirty space in bytes (includes all LEBs)
+ * @total_used: total used space in bytes (does not include index LEBs)
+ * @total_dead: total dead space in bytes (does not include index LEBs)
+ * @total_dark: total dark space in bytes (does not include index LEBs)
+ *
+ * The @taken_empty_lebs field counts the LEBs that are in the transient state
+ * of having been "taken" for use but not yet written to. @taken_empty_lebs is
+ * needed to account correctly for @gc_lnum, otherwise @empty_lebs could be
+ * used by itself (in which case 'unused_lebs' would be a better name). In the
+ * case of @gc_lnum, it is "taken" at mount time or whenever a LEB is retained
+ * by GC, but unlike other empty LEBs that are "taken", it may not be written
+ * straight away (i.e. before the next commit start or unmount), so either
+ * @gc_lnum must be specially accounted for, or the current approach followed
+ * i.e. count it under @taken_empty_lebs.
+ *
+ * @empty_lebs includes @taken_empty_lebs.
+ *
+ * @total_used, @total_dead and @total_dark fields do not account indexing
+ * LEBs.
+ */
+struct ubifs_lp_stats {
+ int empty_lebs;
+ int taken_empty_lebs;
+ int idx_lebs;
+ long long total_free;
+ long long total_dirty;
+ long long total_used;
+ long long total_dead;
+ long long total_dark;
+};
+
+struct ubifs_nnode;
+
+/**
+ * struct ubifs_cnode - LEB Properties Tree common node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (zero for pnodes, greater than zero for nnodes)
+ * @num: node number
+ */
+struct ubifs_cnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+};
+
+/**
+ * struct ubifs_pnode - LEB Properties Tree leaf node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (always zero for pnodes)
+ * @num: node number
+ * @lprops: LEB properties array
+ */
+struct ubifs_pnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+ struct ubifs_lprops lprops[UBIFS_LPT_FANOUT];
+};
+
+/**
+ * struct ubifs_nbranch - LEB Properties Tree internal node branch.
+ * @lnum: LEB number of child
+ * @offs: offset of child
+ * @nnode: nnode child
+ * @pnode: pnode child
+ * @cnode: cnode child
+ */
+struct ubifs_nbranch {
+ int lnum;
+ int offs;
+ union {
+ struct ubifs_nnode *nnode;
+ struct ubifs_pnode *pnode;
+ struct ubifs_cnode *cnode;
+ };
+};
+
+/**
+ * struct ubifs_nnode - LEB Properties Tree internal node.
+ * @parent: parent nnode
+ * @cnext: next cnode to commit
+ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE)
+ * @iip: index in parent
+ * @level: level in the tree (always greater than zero for nnodes)
+ * @num: node number
+ * @nbranch: branches to child nodes
+ */
+struct ubifs_nnode {
+ struct ubifs_nnode *parent;
+ struct ubifs_cnode *cnext;
+ unsigned long flags;
+ int iip;
+ int level;
+ int num;
+ struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT];
+};
+
+/**
+ * struct ubifs_lpt_heap - heap of categorized lprops.
+ * @arr: heap array
+ * @cnt: number in heap
+ * @max_cnt: maximum number allowed in heap
+ *
+ * There are %LPROPS_HEAP_CNT heaps.
+ */
+struct ubifs_lpt_heap {
+ struct ubifs_lprops **arr;
+ int cnt;
+ int max_cnt;
+};
+
+/*
+ * Return codes for LPT scan callback function.
+ *
+ * LPT_SCAN_CONTINUE: continue scanning
+ * LPT_SCAN_ADD: add the LEB properties scanned to the tree in memory
+ * LPT_SCAN_STOP: stop scanning
+ */
+enum {
+ LPT_SCAN_CONTINUE = 0,
+ LPT_SCAN_ADD = 1,
+ LPT_SCAN_STOP = 2,
+};
+
+struct ubifs_info;
+
+/* Callback used by the 'ubifs_lpt_scan_nolock()' function */
+typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c,
+ const struct ubifs_lprops *lprops,
+ int in_tree, void *data);
+
+/**
+ * struct ubifs_wbuf - UBIFS write-buffer.
+ * @c: UBIFS file-system description object
+ * @buf: write-buffer (of min. flash I/O unit size)
+ * @lnum: logical eraseblock number the write-buffer points to
+ * @offs: write-buffer offset in this logical eraseblock
+ * @avail: number of bytes available in the write-buffer
+ * @used: number of used bytes in the write-buffer
+ * @size: write-buffer size (in [@c->min_io_size, @c->max_write_size] range)
+ * @jhead: journal head the mutex belongs to (note, needed only to shut lockdep
+ * up by 'mutex_lock_nested()).
+ * @sync_callback: write-buffer synchronization callback
+ * @io_mutex: serializes write-buffer I/O
+ * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes
+ * fields
+ * @timer: write-buffer timer
+ * @no_timer: non-zero if this write-buffer does not have a timer
+ * @need_sync: non-zero if the timer expired and the wbuf needs sync'ing
+ * @next_ino: points to the next position of the following inode number
+ * @inodes: stores the inode numbers of the nodes which are in wbuf
+ *
+ * The write-buffer synchronization callback is called when the write-buffer is
+ * synchronized in order to notify how much space was wasted due to
+ * write-buffer padding and how much free space is left in the LEB.
+ *
+ * Note: the fields @buf, @lnum, @offs, @avail and @used can be read under
+ * spin-lock or mutex because they are written under both mutex and spin-lock.
+ * @buf is appended to under mutex but overwritten under both mutex and
+ * spin-lock. Thus the data between @buf and @buf + @used can be read under
+ * spinlock.
+ */
+struct ubifs_wbuf {
+ struct ubifs_info *c;
+ void *buf;
+ int lnum;
+ int offs;
+ int avail;
+ int used;
+ int size;
+ int jhead;
+ int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad);
+ struct mutex io_mutex;
+ spinlock_t lock;
+ struct hrtimer timer;
+ unsigned int no_timer:1;
+ unsigned int need_sync:1;
+ int next_ino;
+ ino_t *inodes;
+};
+
+/**
+ * struct ubifs_bud - bud logical eraseblock.
+ * @lnum: logical eraseblock number
+ * @start: where the (uncommitted) bud data starts
+ * @jhead: journal head number this bud belongs to
+ * @list: link in the list buds belonging to the same journal head
+ * @rb: link in the tree of all buds
+ * @log_hash: the log hash from the commit start node up to this bud
+ */
+struct ubifs_bud {
+ int lnum;
+ int start;
+ int jhead;
+ struct list_head list;
+ struct rb_node rb;
+ struct shash_desc *log_hash;
+};
+
+/**
+ * struct ubifs_jhead - journal head.
+ * @wbuf: head's write-buffer
+ * @buds_list: list of bud LEBs belonging to this journal head
+ * @grouped: non-zero if UBIFS groups nodes when writing to this journal head
+ * @log_hash: the log hash from the commit start node up to this journal head
+ *
+ * Note, the @buds list is protected by the @c->buds_lock.
+ */
+struct ubifs_jhead {
+ struct ubifs_wbuf wbuf;
+ struct list_head buds_list;
+ unsigned int grouped:1;
+ struct shash_desc *log_hash;
+};
+
+/**
+ * struct ubifs_zbranch - key/coordinate/length branch stored in znodes.
+ * @key: key
+ * @znode: znode address in memory
+ * @lnum: LEB number of the target node (indexing node or data node)
+ * @offs: target node offset within @lnum
+ * @len: target node length
+ * @hash: the hash of the target node
+ */
+struct ubifs_zbranch {
+ union ubifs_key key;
+ union {
+ struct ubifs_znode *znode;
+ void *leaf;
+ };
+ int lnum;
+ int offs;
+ int len;
+ u8 hash[UBIFS_HASH_ARR_SZ];
+};
+
+/**
+ * struct ubifs_znode - in-memory representation of an indexing node.
+ * @parent: parent znode or NULL if it is the root
+ * @cnext: next znode to commit
+ * @cparent: parent node for this commit
+ * @ciip: index in cparent's zbranch array
+ * @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE)
+ * @time: last access time (seconds)
+ * @level: level of the entry in the TNC tree
+ * @child_cnt: count of child znodes
+ * @iip: index in parent's zbranch array
+ * @alt: lower bound of key range has altered i.e. child inserted at slot 0
+ * @lnum: LEB number of the corresponding indexing node
+ * @offs: offset of the corresponding indexing node
+ * @len: length of the corresponding indexing node
+ * @zbranch: array of znode branches (@c->fanout elements)
+ *
+ * Note! The @lnum, @offs, and @len fields are not really needed - we have them
+ * only for internal consistency check. They could be removed to save some RAM.
+ */
+struct ubifs_znode {
+ struct ubifs_znode *parent;
+ struct ubifs_znode *cnext;
+ struct ubifs_znode *cparent;
+ int ciip;
+ unsigned long flags;
+ time64_t time;
+ int level;
+ int child_cnt;
+ int iip;
+ int alt;
+ int lnum;
+ int offs;
+ int len;
+ struct ubifs_zbranch zbranch[];
+};
+
+/**
+ * struct bu_info - bulk-read information.
+ * @key: first data node key
+ * @zbranch: zbranches of data nodes to bulk read
+ * @buf: buffer to read into
+ * @buf_len: buffer length
+ * @gc_seq: GC sequence number to detect races with GC
+ * @cnt: number of data nodes for bulk read
+ * @blk_cnt: number of data blocks including holes
+ * @oef: end of file reached
+ */
+struct bu_info {
+ union ubifs_key key;
+ struct ubifs_zbranch zbranch[UBIFS_MAX_BULK_READ];
+ void *buf;
+ int buf_len;
+ int gc_seq;
+ int cnt;
+ int blk_cnt;
+ int eof;
+};
+
+/**
+ * struct ubifs_node_range - node length range description data structure.
+ * @len: fixed node length
+ * @min_len: minimum possible node length
+ * @max_len: maximum possible node length
+ *
+ * If @max_len is %0, the node has fixed length @len.
+ */
+struct ubifs_node_range {
+ union {
+ int len;
+ int min_len;
+ };
+ int max_len;
+};
+
+/**
+ * struct ubifs_compressor - UBIFS compressor description structure.
+ * @compr_type: compressor type (%UBIFS_COMPR_LZO, etc)
+ * @cc: cryptoapi compressor handle
+ * @comp_mutex: mutex used during compression
+ * @decomp_mutex: mutex used during decompression
+ * @name: compressor name
+ * @capi_name: cryptoapi compressor name
+ */
+struct ubifs_compressor {
+ int compr_type;
+ struct crypto_comp *cc;
+ struct mutex *comp_mutex;
+ struct mutex *decomp_mutex;
+ const char *name;
+ const char *capi_name;
+};
+
+/**
+ * struct ubifs_budget_req - budget requirements of an operation.
+ *
+ * @fast: non-zero if the budgeting should try to acquire budget quickly and
+ * should not try to call write-back
+ * @recalculate: non-zero if @idx_growth, @data_growth, and @dd_growth fields
+ * have to be re-calculated
+ * @new_page: non-zero if the operation adds a new page
+ * @dirtied_page: non-zero if the operation makes a page dirty
+ * @new_dent: non-zero if the operation adds a new directory entry
+ * @mod_dent: non-zero if the operation removes or modifies an existing
+ * directory entry
+ * @new_ino: non-zero if the operation adds a new inode
+ * @new_ino_d: how much data newly created inode contains
+ * @dirtied_ino: how many inodes the operation makes dirty
+ * @dirtied_ino_d: how much data dirtied inode contains
+ * @idx_growth: how much the index will supposedly grow
+ * @data_growth: how much new data the operation will supposedly add
+ * @dd_growth: how much data that makes other data dirty the operation will
+ * supposedly add
+ *
+ * @idx_growth, @data_growth and @dd_growth are not used in budget request. The
+ * budgeting subsystem caches index and data growth values there to avoid
+ * re-calculating them when the budget is released. However, if @idx_growth is
+ * %-1, it is calculated by the release function using other fields.
+ *
+ * An inode may contain 4KiB of data at max., thus the widths of @new_ino_d
+ * is 13 bits, and @dirtied_ino_d - 15, because up to 4 inodes may be made
+ * dirty by the re-name operation.
+ *
+ * Note, UBIFS aligns node lengths to 8-bytes boundary, so the requester has to
+ * make sure the amount of inode data which contribute to @new_ino_d and
+ * @dirtied_ino_d fields are aligned.
+ */
+struct ubifs_budget_req {
+ unsigned int fast:1;
+ unsigned int recalculate:1;
+#ifndef UBIFS_DEBUG
+ unsigned int new_page:1;
+ unsigned int dirtied_page:1;
+ unsigned int new_dent:1;
+ unsigned int mod_dent:1;
+ unsigned int new_ino:1;
+ unsigned int new_ino_d:13;
+ unsigned int dirtied_ino:4;
+ unsigned int dirtied_ino_d:15;
+#else
+ /* Not bit-fields to check for overflows */
+ unsigned int new_page;
+ unsigned int dirtied_page;
+ unsigned int new_dent;
+ unsigned int mod_dent;
+ unsigned int new_ino;
+ unsigned int new_ino_d;
+ unsigned int dirtied_ino;
+ unsigned int dirtied_ino_d;
+#endif
+ int idx_growth;
+ int data_growth;
+ int dd_growth;
+};
+
+/**
+ * struct ubifs_orphan - stores the inode number of an orphan.
+ * @rb: rb-tree node of rb-tree of orphans sorted by inode number
+ * @list: list head of list of orphans in order added
+ * @new_list: list head of list of orphans added since the last commit
+ * @cnext: next orphan to commit
+ * @dnext: next orphan to delete
+ * @inum: inode number
+ * @new: %1 => added since the last commit, otherwise %0
+ * @cmt: %1 => commit pending, otherwise %0
+ * @del: %1 => delete pending, otherwise %0
+ */
+struct ubifs_orphan {
+ struct rb_node rb;
+ struct list_head list;
+ struct list_head new_list;
+ struct ubifs_orphan *cnext;
+ struct ubifs_orphan *dnext;
+ ino_t inum;
+ unsigned new:1;
+ unsigned cmt:1;
+ unsigned del:1;
+};
+
+/**
+ * struct ubifs_mount_opts - UBIFS-specific mount options information.
+ * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast)
+ * @bulk_read: enable/disable bulk-reads (%0 default, %1 disable, %2 enable)
+ * @chk_data_crc: enable/disable CRC data checking when reading data nodes
+ * (%0 default, %1 disable, %2 enable)
+ * @override_compr: override default compressor (%0 - do not override and use
+ * superblock compressor, %1 - override and use compressor
+ * specified in @compr_type)
+ * @compr_type: compressor type to override the superblock compressor with
+ * (%UBIFS_COMPR_NONE, etc)
+ */
+struct ubifs_mount_opts {
+ unsigned int unmount_mode:2;
+ unsigned int bulk_read:2;
+ unsigned int chk_data_crc:2;
+ unsigned int override_compr:1;
+ unsigned int compr_type:2;
+};
+
+/**
+ * struct ubifs_budg_info - UBIFS budgeting information.
+ * @idx_growth: amount of bytes budgeted for index growth
+ * @data_growth: amount of bytes budgeted for cached data
+ * @dd_growth: amount of bytes budgeted for cached data that will make
+ * other data dirty
+ * @uncommitted_idx: amount of bytes were budgeted for growth of the index, but
+ * which still have to be taken into account because the index
+ * has not been committed so far
+ * @old_idx_sz: size of index on flash
+ * @min_idx_lebs: minimum number of LEBs required for the index
+ * @nospace: non-zero if the file-system does not have flash space (used as
+ * optimization)
+ * @nospace_rp: the same as @nospace, but additionally means that even reserved
+ * pool is full
+ * @page_budget: budget for a page (constant, never changed after mount)
+ * @inode_budget: budget for an inode (constant, never changed after mount)
+ * @dent_budget: budget for a directory entry (constant, never changed after
+ * mount)
+ */
+struct ubifs_budg_info {
+ long long idx_growth;
+ long long data_growth;
+ long long dd_growth;
+ long long uncommitted_idx;
+ unsigned long long old_idx_sz;
+ int min_idx_lebs;
+ unsigned int nospace:1;
+ unsigned int nospace_rp:1;
+ int page_budget;
+ int inode_budget;
+ int dent_budget;
+};
+
+/**
+ * ubifs_stats_info - per-FS statistics information.
+ * @magic_errors: number of bad magic numbers (will be reset with a new mount).
+ * @node_errors: number of bad nodes (will be reset with a new mount).
+ * @crc_errors: number of bad crcs (will be reset with a new mount).
+ */
+struct ubifs_stats_info {
+ unsigned int magic_errors;
+ unsigned int node_errors;
+ unsigned int crc_errors;
+};
+
+struct ubifs_debug_info;
+
+/**
+ * struct ubifs_info - UBIFS file-system description data structure
+ * (per-superblock).
+ * @vfs_sb: VFS @struct super_block object
+ * @sup_node: The super block node as read from the device
+ *
+ * @highest_inum: highest used inode number
+ * @max_sqnum: current global sequence number
+ * @cmt_no: commit number of the last successfully completed commit, protected
+ * by @commit_sem
+ * @cnt_lock: protects @highest_inum and @max_sqnum counters
+ * @fmt_version: UBIFS on-flash format version
+ * @ro_compat_version: R/O compatibility version
+ * @uuid: UUID from super block
+ *
+ * @lhead_lnum: log head logical eraseblock number
+ * @lhead_offs: log head offset
+ * @ltail_lnum: log tail logical eraseblock number (offset is always 0)
+ * @log_mutex: protects the log, @lhead_lnum, @lhead_offs, @ltail_lnum, and
+ * @bud_bytes
+ * @min_log_bytes: minimum required number of bytes in the log
+ * @cmt_bud_bytes: used during commit to temporarily amount of bytes in
+ * committed buds
+ *
+ * @buds: tree of all buds indexed by bud LEB number
+ * @bud_bytes: how many bytes of flash is used by buds
+ * @buds_lock: protects the @buds tree, @bud_bytes, and per-journal head bud
+ * lists
+ * @jhead_cnt: count of journal heads
+ * @jheads: journal heads (head zero is base head)
+ * @max_bud_bytes: maximum number of bytes allowed in buds
+ * @bg_bud_bytes: number of bud bytes when background commit is initiated
+ * @old_buds: buds to be released after commit ends
+ * @max_bud_cnt: maximum number of buds
+ *
+ * @commit_sem: synchronizes committer with other processes
+ * @cmt_state: commit state
+ * @cs_lock: commit state lock
+ * @cmt_wq: wait queue to sleep on if the log is full and a commit is running
+ *
+ * @big_lpt: flag that LPT is too big to write whole during commit
+ * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
+ * @double_hash: flag indicating that we can do lookups by hash
+ * @encrypted: flag indicating that this file system contains encrypted files
+ * @no_chk_data_crc: do not check CRCs when reading data nodes (except during
+ * recovery)
+ * @bulk_read: enable bulk-reads
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @rw_incompat: the media is not R/W compatible
+ * @assert_action: action to take when a ubifs_assert() fails
+ * @authenticated: flag indigating the FS is mounted in authenticated mode
+ *
+ * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
+ * @calc_idx_sz
+ * @zroot: zbranch which points to the root index node and znode
+ * @cnext: next znode to commit
+ * @enext: next znode to commit to empty space
+ * @gap_lebs: array of LEBs used by the in-gaps commit method
+ * @cbuf: commit buffer
+ * @ileb_buf: buffer for commit in-the-gaps method
+ * @ileb_len: length of data in ileb_buf
+ * @ihead_lnum: LEB number of index head
+ * @ihead_offs: offset of index head
+ * @ilebs: pre-allocated index LEBs
+ * @ileb_cnt: number of pre-allocated index LEBs
+ * @ileb_nxt: next pre-allocated index LEBs
+ * @old_idx: tree of index nodes obsoleted since the last commit start
+ * @bottom_up_buf: a buffer which is used by 'dirty_cow_bottom_up()' in tnc.c
+ *
+ * @mst_node: master node
+ * @mst_offs: offset of valid master node
+ *
+ * @max_bu_buf_len: maximum bulk-read buffer length
+ * @bu_mutex: protects the pre-allocated bulk-read buffer and @c->bu
+ * @bu: pre-allocated bulk-read information
+ *
+ * @write_reserve_mutex: protects @write_reserve_buf
+ * @write_reserve_buf: on the write path we allocate memory, which might
+ * sometimes be unavailable, in which case we use this
+ * write reserve buffer
+ *
+ * @log_lebs: number of logical eraseblocks in the log
+ * @log_bytes: log size in bytes
+ * @log_last: last LEB of the log
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @lpt_first: first LEB of the lprops table area
+ * @lpt_last: last LEB of the lprops table area
+ * @orph_lebs: number of LEBs used for the orphan area
+ * @orph_first: first LEB of the orphan area
+ * @orph_last: last LEB of the orphan area
+ * @main_lebs: count of LEBs in the main area
+ * @main_first: first LEB of the main area
+ * @main_bytes: main area size in bytes
+ *
+ * @key_hash_type: type of the key hash
+ * @key_hash: direntry key hash function
+ * @key_fmt: key format
+ * @key_len: key length
+ * @hash_len: The length of the index node hashes
+ * @fanout: fanout of the index tree (number of links per indexing node)
+ *
+ * @min_io_size: minimal input/output unit size
+ * @min_io_shift: number of bits in @min_io_size minus one
+ * @max_write_size: maximum amount of bytes the underlying flash can write at a
+ * time (MTD write buffer size)
+ * @max_write_shift: number of bits in @max_write_size minus one
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_start: starting offset of logical eraseblocks within physical
+ * eraseblocks
+ * @half_leb_size: half LEB size
+ * @idx_leb_size: how many bytes of an LEB are effectively available when it is
+ * used to store indexing nodes (@leb_size - @max_idx_node_sz)
+ * @leb_cnt: count of logical eraseblocks
+ * @max_leb_cnt: maximum count of logical eraseblocks
+ * @ro_media: the underlying UBI volume is read-only
+ * @ro_mount: the file-system was mounted as read-only
+ * @ro_error: UBIFS switched to R/O mode because an error happened
+ *
+ * @dirty_pg_cnt: number of dirty pages (not used)
+ * @dirty_zn_cnt: number of dirty znodes
+ * @clean_zn_cnt: number of clean znodes
+ *
+ * @space_lock: protects @bi and @lst
+ * @lst: lprops statistics
+ * @bi: budgeting information
+ * @calc_idx_sz: temporary variable which is used to calculate new index size
+ * (contains accurate new index size at end of TNC commit start)
+ *
+ * @ref_node_alsz: size of the LEB reference node aligned to the min. flash
+ * I/O unit
+ * @mst_node_alsz: master node aligned size
+ * @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary
+ * @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary
+ * @max_inode_sz: maximum possible inode size in bytes
+ * @max_znode_sz: size of znode in bytes
+ *
+ * @leb_overhead: how many bytes are wasted in an LEB when it is filled with
+ * data nodes of maximum size - used in free space reporting
+ * @dead_wm: LEB dead space watermark
+ * @dark_wm: LEB dark space watermark
+ * @block_cnt: count of 4KiB blocks on the FS
+ *
+ * @ranges: UBIFS node length ranges
+ * @ubi: UBI volume descriptor
+ * @di: UBI device information
+ * @vi: UBI volume information
+ *
+ * @orph_tree: rb-tree of orphan inode numbers
+ * @orph_list: list of orphan inode numbers in order added
+ * @orph_new: list of orphan inode numbers added since last commit
+ * @orph_cnext: next orphan to commit
+ * @orph_dnext: next orphan to delete
+ * @orphan_lock: lock for orph_tree and orph_new
+ * @orph_buf: buffer for orphan nodes
+ * @new_orphans: number of orphans since last commit
+ * @cmt_orphans: number of orphans being committed
+ * @tot_orphans: number of orphans in the rb_tree
+ * @max_orphans: maximum number of orphans allowed
+ * @ohead_lnum: orphan head LEB number
+ * @ohead_offs: orphan head offset
+ * @no_orphs: non-zero if there are no orphans
+ *
+ * @bgt: UBIFS background thread
+ * @bgt_name: background thread name
+ * @need_bgt: if background thread should run
+ * @need_wbuf_sync: if write-buffers have to be synchronized
+ *
+ * @gc_lnum: LEB number used for garbage collection
+ * @sbuf: a buffer of LEB size used by GC and replay for scanning
+ * @idx_gc: list of index LEBs that have been garbage collected
+ * @idx_gc_cnt: number of elements on the idx_gc list
+ * @gc_seq: incremented for every non-index LEB garbage collected
+ * @gced_lnum: last non-index LEB that was garbage collected
+ *
+ * @infos_list: links all 'ubifs_info' objects
+ * @umount_mutex: serializes shrinker and un-mount
+ * @shrinker_run_no: shrinker run number
+ *
+ * @space_bits: number of bits needed to record free or dirty space
+ * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT
+ * @lpt_offs_bits: number of bits needed to record an offset in the LPT
+ * @lpt_spc_bits: number of bits needed to space in the LPT
+ * @pcnt_bits: number of bits needed to record pnode or nnode number
+ * @lnum_bits: number of bits needed to record LEB number
+ * @nnode_sz: size of on-flash nnode
+ * @pnode_sz: size of on-flash pnode
+ * @ltab_sz: size of on-flash LPT lprops table
+ * @lsave_sz: size of on-flash LPT save table
+ * @pnode_cnt: number of pnodes
+ * @nnode_cnt: number of nnodes
+ * @lpt_hght: height of the LPT
+ * @pnodes_have: number of pnodes in memory
+ *
+ * @lp_mutex: protects lprops table and all the other lprops-related fields
+ * @lpt_lnum: LEB number of the root nnode of the LPT
+ * @lpt_offs: offset of the root nnode of the LPT
+ * @nhead_lnum: LEB number of LPT head
+ * @nhead_offs: offset of LPT head
+ * @lpt_drty_flgs: dirty flags for LPT special nodes e.g. ltab
+ * @dirty_nn_cnt: number of dirty nnodes
+ * @dirty_pn_cnt: number of dirty pnodes
+ * @check_lpt_free: flag that indicates LPT GC may be needed
+ * @lpt_sz: LPT size
+ * @lpt_nod_buf: buffer for an on-flash nnode or pnode
+ * @lpt_buf: buffer of LEB size used by LPT
+ * @nroot: address in memory of the root nnode of the LPT
+ * @lpt_cnext: next LPT node to commit
+ * @lpt_heap: array of heaps of categorized lprops
+ * @dirty_idx: a (reverse sorted) copy of the LPROPS_DIRTY_IDX heap as at
+ * previous commit start
+ * @uncat_list: list of un-categorized LEBs
+ * @empty_list: list of empty LEBs
+ * @freeable_list: list of freeable non-index LEBs (free + dirty == @leb_size)
+ * @frdi_idx_list: list of freeable index LEBs (free + dirty == @leb_size)
+ * @freeable_cnt: number of freeable LEBs in @freeable_list
+ * @in_a_category_cnt: count of lprops which are in a certain category, which
+ * basically meants that they were loaded from the flash
+ *
+ * @ltab_lnum: LEB number of LPT's own lprops table
+ * @ltab_offs: offset of LPT's own lprops table
+ * @ltab: LPT's own lprops table
+ * @ltab_cmt: LPT's own lprops table (commit copy)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @lsave_lnum: LEB number of LPT's save table
+ * @lsave_offs: offset of LPT's save table
+ * @lsave: LPT's save table
+ * @lscan_lnum: LEB number of last LPT scan
+ *
+ * @rp_size: size of the reserved pool in bytes
+ * @report_rp_size: size of the reserved pool reported to user-space
+ * @rp_uid: reserved pool user ID
+ * @rp_gid: reserved pool group ID
+ *
+ * @hash_tfm: the hash transformation used for hashing nodes
+ * @hmac_tfm: the HMAC transformation for this filesystem
+ * @hmac_desc_len: length of the HMAC used for authentication
+ * @auth_key_name: the authentication key name
+ * @auth_hash_name: the name of the hash algorithm used for authentication
+ * @auth_hash_algo: the authentication hash used for this fs
+ * @log_hash: the log hash from the commit start node up to the latest reference
+ * node.
+ *
+ * @empty: %1 if the UBI device is empty
+ * @need_recovery: %1 if the file-system needs recovery
+ * @replaying: %1 during journal replay
+ * @mounting: %1 while mounting
+ * @probing: %1 while attempting to mount if SB_SILENT mount flag is set
+ * @remounting_rw: %1 while re-mounting from R/O mode to R/W mode
+ * @replay_list: temporary list used during journal replay
+ * @replay_buds: list of buds to replay
+ * @cs_sqnum: sequence number of first node in the log (commit start node)
+ * @unclean_leb_list: LEBs to recover when re-mounting R/O mounted FS to R/W
+ * mode
+ * @rcvrd_mst_node: recovered master node to write when re-mounting R/O mounted
+ * FS to R/W mode
+ * @size_tree: inode size information for recovery
+ * @mount_opts: UBIFS-specific mount options
+ *
+ * @dbg: debugging-related information
+ * @stats: statistics exported over sysfs
+ *
+ * @kobj: kobject for /sys/fs/ubifs/
+ * @kobj_unregister: completion to unregister sysfs kobject
+ */
+struct ubifs_info {
+ struct super_block *vfs_sb;
+ struct ubifs_sb_node *sup_node;
+
+ ino_t highest_inum;
+ unsigned long long max_sqnum;
+ unsigned long long cmt_no;
+ spinlock_t cnt_lock;
+ int fmt_version;
+ int ro_compat_version;
+ unsigned char uuid[16];
+
+ int lhead_lnum;
+ int lhead_offs;
+ int ltail_lnum;
+ struct mutex log_mutex;
+ int min_log_bytes;
+ long long cmt_bud_bytes;
+
+ struct rb_root buds;
+ long long bud_bytes;
+ spinlock_t buds_lock;
+ int jhead_cnt;
+ struct ubifs_jhead *jheads;
+ long long max_bud_bytes;
+ long long bg_bud_bytes;
+ struct list_head old_buds;
+ int max_bud_cnt;
+
+ struct rw_semaphore commit_sem;
+ int cmt_state;
+ spinlock_t cs_lock;
+ wait_queue_head_t cmt_wq;
+
+ struct kobject kobj;
+ struct completion kobj_unregister;
+
+ unsigned int big_lpt:1;
+ unsigned int space_fixup:1;
+ unsigned int double_hash:1;
+ unsigned int encrypted:1;
+ unsigned int no_chk_data_crc:1;
+ unsigned int bulk_read:1;
+ unsigned int default_compr:2;
+ unsigned int rw_incompat:1;
+ unsigned int assert_action:2;
+ unsigned int authenticated:1;
+ unsigned int superblock_need_write:1;
+
+ struct mutex tnc_mutex;
+ struct ubifs_zbranch zroot;
+ struct ubifs_znode *cnext;
+ struct ubifs_znode *enext;
+ int *gap_lebs;
+ void *cbuf;
+ void *ileb_buf;
+ int ileb_len;
+ int ihead_lnum;
+ int ihead_offs;
+ int *ilebs;
+ int ileb_cnt;
+ int ileb_nxt;
+ struct rb_root old_idx;
+ int *bottom_up_buf;
+
+ struct ubifs_mst_node *mst_node;
+ int mst_offs;
+
+ int max_bu_buf_len;
+ struct mutex bu_mutex;
+ struct bu_info bu;
+
+ struct mutex write_reserve_mutex;
+ void *write_reserve_buf;
+
+ int log_lebs;
+ long long log_bytes;
+ int log_last;
+ int lpt_lebs;
+ int lpt_first;
+ int lpt_last;
+ int orph_lebs;
+ int orph_first;
+ int orph_last;
+ int main_lebs;
+ int main_first;
+ long long main_bytes;
+
+ uint8_t key_hash_type;
+ uint32_t (*key_hash)(const char *str, int len);
+ int key_fmt;
+ int key_len;
+ int hash_len;
+ int fanout;
+
+ int min_io_size;
+ int min_io_shift;
+ int max_write_size;
+ int max_write_shift;
+ int leb_size;
+ int leb_start;
+ int half_leb_size;
+ int idx_leb_size;
+ int leb_cnt;
+ int max_leb_cnt;
+ unsigned int ro_media:1;
+ unsigned int ro_mount:1;
+ unsigned int ro_error:1;
+
+ atomic_long_t dirty_pg_cnt;
+ atomic_long_t dirty_zn_cnt;
+ atomic_long_t clean_zn_cnt;
+
+ spinlock_t space_lock;
+ struct ubifs_lp_stats lst;
+ struct ubifs_budg_info bi;
+ unsigned long long calc_idx_sz;
+
+ int ref_node_alsz;
+ int mst_node_alsz;
+ int min_idx_node_sz;
+ int max_idx_node_sz;
+ long long max_inode_sz;
+ int max_znode_sz;
+
+ int leb_overhead;
+ int dead_wm;
+ int dark_wm;
+ int block_cnt;
+
+ struct ubifs_node_range ranges[UBIFS_NODE_TYPES_CNT];
+ struct ubi_volume_desc *ubi;
+ struct ubi_device_info di;
+ struct ubi_volume_info vi;
+
+ struct rb_root orph_tree;
+ struct list_head orph_list;
+ struct list_head orph_new;
+ struct ubifs_orphan *orph_cnext;
+ struct ubifs_orphan *orph_dnext;
+ spinlock_t orphan_lock;
+ void *orph_buf;
+ int new_orphans;
+ int cmt_orphans;
+ int tot_orphans;
+ int max_orphans;
+ int ohead_lnum;
+ int ohead_offs;
+ int no_orphs;
+
+ struct task_struct *bgt;
+ char bgt_name[sizeof(BGT_NAME_PATTERN) + 9];
+ int need_bgt;
+ int need_wbuf_sync;
+
+ int gc_lnum;
+ void *sbuf;
+ struct list_head idx_gc;
+ int idx_gc_cnt;
+ int gc_seq;
+ int gced_lnum;
+
+ struct list_head infos_list;
+ struct mutex umount_mutex;
+ unsigned int shrinker_run_no;
+
+ int space_bits;
+ int lpt_lnum_bits;
+ int lpt_offs_bits;
+ int lpt_spc_bits;
+ int pcnt_bits;
+ int lnum_bits;
+ int nnode_sz;
+ int pnode_sz;
+ int ltab_sz;
+ int lsave_sz;
+ int pnode_cnt;
+ int nnode_cnt;
+ int lpt_hght;
+ int pnodes_have;
+
+ struct mutex lp_mutex;
+ int lpt_lnum;
+ int lpt_offs;
+ int nhead_lnum;
+ int nhead_offs;
+ int lpt_drty_flgs;
+ int dirty_nn_cnt;
+ int dirty_pn_cnt;
+ int check_lpt_free;
+ long long lpt_sz;
+ void *lpt_nod_buf;
+ void *lpt_buf;
+ struct ubifs_nnode *nroot;
+ struct ubifs_cnode *lpt_cnext;
+ struct ubifs_lpt_heap lpt_heap[LPROPS_HEAP_CNT];
+ struct ubifs_lpt_heap dirty_idx;
+ struct list_head uncat_list;
+ struct list_head empty_list;
+ struct list_head freeable_list;
+ struct list_head frdi_idx_list;
+ int freeable_cnt;
+ int in_a_category_cnt;
+
+ int ltab_lnum;
+ int ltab_offs;
+ struct ubifs_lpt_lprops *ltab;
+ struct ubifs_lpt_lprops *ltab_cmt;
+ int lsave_cnt;
+ int lsave_lnum;
+ int lsave_offs;
+ int *lsave;
+ int lscan_lnum;
+
+ long long rp_size;
+ long long report_rp_size;
+ kuid_t rp_uid;
+ kgid_t rp_gid;
+
+ struct crypto_shash *hash_tfm;
+ struct crypto_shash *hmac_tfm;
+ int hmac_desc_len;
+ char *auth_key_name;
+ char *auth_hash_name;
+ enum hash_algo auth_hash_algo;
+
+ struct shash_desc *log_hash;
+
+ /* The below fields are used only during mounting and re-mounting */
+ unsigned int empty:1;
+ unsigned int need_recovery:1;
+ unsigned int replaying:1;
+ unsigned int mounting:1;
+ unsigned int remounting_rw:1;
+ unsigned int probing:1;
+ struct list_head replay_list;
+ struct list_head replay_buds;
+ unsigned long long cs_sqnum;
+ struct list_head unclean_leb_list;
+ struct ubifs_mst_node *rcvrd_mst_node;
+ struct rb_root size_tree;
+ struct ubifs_mount_opts mount_opts;
+
+ struct ubifs_debug_info *dbg;
+ struct ubifs_stats_info *stats;
+};
+
+extern struct list_head ubifs_infos;
+extern spinlock_t ubifs_infos_lock;
+extern atomic_long_t ubifs_clean_zn_cnt;
+extern const struct super_operations ubifs_super_operations;
+extern const struct address_space_operations ubifs_file_address_operations;
+extern const struct file_operations ubifs_file_operations;
+extern const struct inode_operations ubifs_file_inode_operations;
+extern const struct file_operations ubifs_dir_operations;
+extern const struct inode_operations ubifs_dir_inode_operations;
+extern const struct inode_operations ubifs_symlink_inode_operations;
+extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
+extern int ubifs_default_version;
+
+/* auth.c */
+static inline int ubifs_authenticated(const struct ubifs_info *c)
+{
+ return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated;
+}
+
+struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c);
+static inline struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
+{
+ return ubifs_authenticated(c) ? __ubifs_hash_get_desc(c) : NULL;
+}
+
+static inline int ubifs_shash_init(const struct ubifs_info *c,
+ struct shash_desc *desc)
+{
+ if (ubifs_authenticated(c))
+ return crypto_shash_init(desc);
+ else
+ return 0;
+}
+
+static inline int ubifs_shash_update(const struct ubifs_info *c,
+ struct shash_desc *desc, const void *buf,
+ unsigned int len)
+{
+ int err = 0;
+
+ if (ubifs_authenticated(c)) {
+ err = crypto_shash_update(desc, buf, len);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static inline int ubifs_shash_final(const struct ubifs_info *c,
+ struct shash_desc *desc, u8 *out)
+{
+ return ubifs_authenticated(c) ? crypto_shash_final(desc, out) : 0;
+}
+
+int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
+ u8 *hash);
+static inline int ubifs_node_calc_hash(const struct ubifs_info *c,
+ const void *buf, u8 *hash)
+{
+ if (ubifs_authenticated(c))
+ return __ubifs_node_calc_hash(c, buf, hash);
+ else
+ return 0;
+}
+
+int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
+ struct shash_desc *inhash);
+
+/**
+ * ubifs_check_hash - compare two hashes
+ * @c: UBIFS file-system description object
+ * @expected: first hash
+ * @got: second hash
+ *
+ * Compare two hashes @expected and @got. Returns 0 when they are equal, a
+ * negative error code otherwise.
+ */
+static inline int ubifs_check_hash(const struct ubifs_info *c,
+ const u8 *expected, const u8 *got)
+{
+ return crypto_memneq(expected, got, c->hash_len);
+}
+
+/**
+ * ubifs_check_hmac - compare two HMACs
+ * @c: UBIFS file-system description object
+ * @expected: first HMAC
+ * @got: second HMAC
+ *
+ * Compare two hashes @expected and @got. Returns 0 when they are equal, a
+ * negative error code otherwise.
+ */
+static inline int ubifs_check_hmac(const struct ubifs_info *c,
+ const u8 *expected, const u8 *got)
+{
+ return crypto_memneq(expected, got, c->hmac_desc_len);
+}
+
+#ifdef CONFIG_UBIFS_FS_AUTHENTICATION
+void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
+ const u8 *hash, int lnum, int offs);
+#else
+static inline void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
+ const u8 *hash, int lnum, int offs) {};
+#endif
+
+int __ubifs_node_check_hash(const struct ubifs_info *c, const void *buf,
+ const u8 *expected);
+static inline int ubifs_node_check_hash(const struct ubifs_info *c,
+ const void *buf, const u8 *expected)
+{
+ if (ubifs_authenticated(c))
+ return __ubifs_node_check_hash(c, buf, expected);
+ else
+ return 0;
+}
+
+int ubifs_init_authentication(struct ubifs_info *c);
+void __ubifs_exit_authentication(struct ubifs_info *c);
+static inline void ubifs_exit_authentication(struct ubifs_info *c)
+{
+ if (ubifs_authenticated(c))
+ __ubifs_exit_authentication(c);
+}
+
+/**
+ * ubifs_branch_hash - returns a pointer to the hash of a branch
+ * @c: UBIFS file-system description object
+ * @br: branch to get the hash from
+ *
+ * This returns a pointer to the hash of a branch. Since the key already is a
+ * dynamically sized object we cannot use a struct member here.
+ */
+static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
+ struct ubifs_branch *br)
+{
+ return (void *)br + sizeof(*br) + c->key_len;
+}
+
+/**
+ * ubifs_copy_hash - copy a hash
+ * @c: UBIFS file-system description object
+ * @from: source hash
+ * @to: destination hash
+ *
+ * With authentication this copies a hash, otherwise does nothing.
+ */
+static inline void ubifs_copy_hash(const struct ubifs_info *c, const u8 *from,
+ u8 *to)
+{
+ if (ubifs_authenticated(c))
+ memcpy(to, from, c->hash_len);
+}
+
+int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
+ int len, int ofs_hmac);
+static inline int ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
+ int len, int ofs_hmac)
+{
+ if (ubifs_authenticated(c))
+ return __ubifs_node_insert_hmac(c, buf, len, ofs_hmac);
+ else
+ return 0;
+}
+
+int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *buf,
+ int len, int ofs_hmac);
+static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
+ const void *buf, int len, int ofs_hmac)
+{
+ if (ubifs_authenticated(c))
+ return __ubifs_node_verify_hmac(c, buf, len, ofs_hmac);
+ else
+ return 0;
+}
+
+/**
+ * ubifs_auth_node_sz - returns the size of an authentication node
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the size of an authentication node which can
+ * be 0 for unauthenticated filesystems or the real size of an auth node
+ * authentication is enabled.
+ */
+static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
+{
+ if (ubifs_authenticated(c))
+ return sizeof(struct ubifs_auth_node) + c->hmac_desc_len;
+ else
+ return 0;
+}
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+ const struct ubifs_sb_node *sup);
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac);
+
+int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
+
+int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
+ struct shash_desc *target);
+static inline int ubifs_shash_copy_state(const struct ubifs_info *c,
+ struct shash_desc *src,
+ struct shash_desc *target)
+{
+ if (ubifs_authenticated(c))
+ return __ubifs_shash_copy_state(c, src, target);
+ else
+ return 0;
+}
+
+/* io.c */
+void ubifs_ro_mode(struct ubifs_info *c, int err);
+int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
+ int len, int even_ebadmsg);
+int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+ int len);
+int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len);
+int ubifs_leb_unmap(struct ubifs_info *c, int lnum);
+int ubifs_leb_map(struct ubifs_info *c, int lnum);
+int ubifs_is_mapped(const struct ubifs_info *c, int lnum);
+int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len);
+int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs);
+int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf);
+int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
+ int lnum, int offs);
+int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
+ int lnum, int offs);
+int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
+ int offs);
+int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int hmac_offs);
+int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
+ int lnum, int offs, int quiet, int must_chk_crc);
+void ubifs_init_node(struct ubifs_info *c, void *buf, int len, int pad);
+void ubifs_crc_node(struct ubifs_info *c, void *buf, int len);
+void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
+int ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len,
+ int hmac_offs, int pad);
+void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
+int ubifs_io_init(struct ubifs_info *c);
+void ubifs_pad(const struct ubifs_info *c, void *buf, int pad);
+int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf);
+int ubifs_bg_wbufs_sync(struct ubifs_info *c);
+void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum);
+int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode);
+
+/* scan.c */
+struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int quiet);
+void ubifs_scan_destroy(struct ubifs_scan_leb *sleb);
+int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
+ int offs, int quiet);
+struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
+ int offs, void *sbuf);
+void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ int lnum, int offs);
+int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+ void *buf, int offs);
+void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
+ void *buf);
+
+/* log.c */
+void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud);
+void ubifs_create_buds_lists(struct ubifs_info *c);
+int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs);
+struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum);
+struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum);
+int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum);
+int ubifs_log_end_commit(struct ubifs_info *c, int new_ltail_lnum);
+int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum);
+int ubifs_consolidate_log(struct ubifs_info *c);
+
+/* journal.c */
+int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
+ const struct fscrypt_name *nm, const struct inode *inode,
+ int deletion, int xent);
+int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
+ const union ubifs_key *key, const void *buf, int len);
+int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
+int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
+int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
+ const struct inode *fst_inode,
+ const struct fscrypt_name *fst_nm,
+ const struct inode *snd_dir,
+ const struct inode *snd_inode,
+ const struct fscrypt_name *snd_nm, int sync);
+int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
+ const struct inode *old_inode,
+ const struct fscrypt_name *old_nm,
+ const struct inode *new_dir,
+ const struct inode *new_inode,
+ const struct fscrypt_name *new_nm,
+ const struct inode *whiteout, int sync);
+int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
+ loff_t old_size, loff_t new_size);
+int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
+ const struct inode *inode, const struct fscrypt_name *nm);
+int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode1,
+ const struct inode *inode2);
+
+/* budget.c */
+int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req);
+void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req);
+void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
+ struct ubifs_inode *ui);
+int ubifs_budget_inode_op(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+void ubifs_release_ino_dirty(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+void ubifs_cancel_ino_op(struct ubifs_info *c, struct inode *inode,
+ struct ubifs_budget_req *req);
+long long ubifs_get_free_space(struct ubifs_info *c);
+long long ubifs_get_free_space_nolock(struct ubifs_info *c);
+int ubifs_calc_min_idx_lebs(struct ubifs_info *c);
+void ubifs_convert_page_budget(struct ubifs_info *c);
+long long ubifs_reported_space(const struct ubifs_info *c, long long free);
+long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs);
+
+/* find.c */
+int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *offs,
+ int squeeze);
+int ubifs_find_free_leb_for_idx(struct ubifs_info *c);
+int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
+ int min_space, int pick_free);
+int ubifs_find_dirty_idx_leb(struct ubifs_info *c);
+int ubifs_save_dirty_idx_lnums(struct ubifs_info *c);
+
+/* tnc.c */
+int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_znode **zn, int *n);
+int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, const struct fscrypt_name *nm);
+int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, uint32_t secondary_hash);
+int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, int *lnum, int *offs);
+int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
+ int offs, int len, const u8 *hash);
+int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
+ int old_lnum, int old_offs, int lnum, int offs, int len);
+int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs, int len, const u8 *hash,
+ const struct fscrypt_name *nm);
+int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
+int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
+ const struct fscrypt_name *nm);
+int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
+ uint32_t cookie);
+int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
+ union ubifs_key *to_key);
+int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
+struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
+ union ubifs_key *key,
+ const struct fscrypt_name *nm);
+void ubifs_tnc_close(struct ubifs_info *c);
+int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs, int is_idx);
+int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs);
+/* Shared by tnc.c for tnc_commit.c */
+void destroy_old_idx(struct ubifs_info *c);
+int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
+ int lnum, int offs);
+int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode);
+int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu);
+int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu);
+
+/* tnc_misc.c */
+struct ubifs_znode *ubifs_tnc_levelorder_next(const struct ubifs_info *c,
+ struct ubifs_znode *zr,
+ struct ubifs_znode *znode);
+int ubifs_search_zbranch(const struct ubifs_info *c,
+ const struct ubifs_znode *znode,
+ const union ubifs_key *key, int *n);
+struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode);
+struct ubifs_znode *ubifs_tnc_postorder_next(const struct ubifs_info *c,
+ struct ubifs_znode *znode);
+long ubifs_destroy_tnc_subtree(const struct ubifs_info *c,
+ struct ubifs_znode *zr);
+void ubifs_destroy_tnc_tree(struct ubifs_info *c);
+struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr,
+ struct ubifs_znode *parent, int iip);
+int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node);
+
+/* tnc_commit.c */
+int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot);
+int ubifs_tnc_end_commit(struct ubifs_info *c);
+
+/* shrinker.c */
+unsigned long ubifs_shrink_scan(struct shrinker *shrink,
+ struct shrink_control *sc);
+unsigned long ubifs_shrink_count(struct shrinker *shrink,
+ struct shrink_control *sc);
+
+/* commit.c */
+int ubifs_bg_thread(void *info);
+void ubifs_commit_required(struct ubifs_info *c);
+void ubifs_request_bg_commit(struct ubifs_info *c);
+int ubifs_run_commit(struct ubifs_info *c);
+void ubifs_recovery_commit(struct ubifs_info *c);
+int ubifs_gc_should_commit(struct ubifs_info *c);
+void ubifs_wait_for_commit(struct ubifs_info *c);
+
+/* master.c */
+int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2);
+int ubifs_read_master(struct ubifs_info *c);
+int ubifs_write_master(struct ubifs_info *c);
+
+/* sb.c */
+int ubifs_read_superblock(struct ubifs_info *c);
+int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
+int ubifs_fixup_free_space(struct ubifs_info *c);
+int ubifs_enable_encryption(struct ubifs_info *c);
+
+/* replay.c */
+int ubifs_validate_entry(struct ubifs_info *c,
+ const struct ubifs_dent_node *dent);
+int ubifs_replay_journal(struct ubifs_info *c);
+
+/* gc.c */
+int ubifs_garbage_collect(struct ubifs_info *c, int anyway);
+int ubifs_gc_start_commit(struct ubifs_info *c);
+int ubifs_gc_end_commit(struct ubifs_info *c);
+void ubifs_destroy_idx_gc(struct ubifs_info *c);
+int ubifs_get_idx_gc_leb(struct ubifs_info *c);
+int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp);
+
+/* orphan.c */
+int ubifs_add_orphan(struct ubifs_info *c, ino_t inum);
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum);
+int ubifs_orphan_start_commit(struct ubifs_info *c);
+int ubifs_orphan_end_commit(struct ubifs_info *c);
+int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only);
+int ubifs_clear_orphans(struct ubifs_info *c);
+
+/* lpt.c */
+int ubifs_calc_lpt_geom(struct ubifs_info *c);
+int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
+ u8 *hash);
+int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
+ int *lpt_lebs, int *big_lpt, u8 *hash);
+int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
+struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
+struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
+int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
+ ubifs_lpt_scan_callback scan_cb, void *data);
+
+/* Shared by lpt.c for lpt_commit.c */
+void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave);
+void ubifs_pack_ltab(struct ubifs_info *c, void *buf,
+ struct ubifs_lpt_lprops *ltab);
+void ubifs_pack_pnode(struct ubifs_info *c, void *buf,
+ struct ubifs_pnode *pnode);
+void ubifs_pack_nnode(struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode);
+struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip);
+struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *parent, int iip);
+struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i);
+int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip);
+void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty);
+void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode);
+uint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, int nrbits);
+struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght);
+/* Needed only in debugging code in lpt_commit.c */
+int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
+ struct ubifs_nnode *nnode);
+int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash);
+
+/* lpt_commit.c */
+int ubifs_lpt_start_commit(struct ubifs_info *c);
+int ubifs_lpt_end_commit(struct ubifs_info *c);
+int ubifs_lpt_post_commit(struct ubifs_info *c);
+void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
+
+/* lprops.c */
+const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
+ const struct ubifs_lprops *lp,
+ int free, int dirty, int flags,
+ int idx_gc_cnt);
+void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst);
+void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
+ int cat);
+void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
+ struct ubifs_lprops *new_lprops);
+void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops);
+int ubifs_categorize_lprops(const struct ubifs_info *c,
+ const struct ubifs_lprops *lprops);
+int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean, int idx_gc_cnt);
+int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
+ int flags_set, int flags_clean);
+int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp);
+const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c);
+const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);
+int ubifs_calc_dark(const struct ubifs_info *c, int spc);
+
+/* file.c */
+int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+int ubifs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
+int ubifs_update_time(struct inode *inode, int flags);
+
+/* dir.c */
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
+ umode_t mode, bool is_xattr);
+int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,
+ struct kstat *stat, u32 request_mask, unsigned int flags);
+int ubifs_check_dir_empty(struct inode *dir);
+
+/* xattr.c */
+int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
+ size_t size, int flags, bool check_lock);
+ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
+ size_t size);
+
+#ifdef CONFIG_UBIFS_FS_XATTR
+extern const struct xattr_handler * const ubifs_xattr_handlers[];
+ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size);
+void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum);
+int ubifs_purge_xattrs(struct inode *host);
+#else
+#define ubifs_listxattr NULL
+#define ubifs_xattr_handlers NULL
+static inline void ubifs_evict_xattr_inode(struct ubifs_info *c,
+ ino_t xattr_inum) { }
+static inline int ubifs_purge_xattrs(struct inode *host)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_UBIFS_FS_SECURITY
+extern int ubifs_init_security(struct inode *dentry, struct inode *inode,
+ const struct qstr *qstr);
+#else
+static inline int ubifs_init_security(struct inode *dentry,
+ struct inode *inode, const struct qstr *qstr)
+{
+ return 0;
+}
+#endif
+
+
+/* super.c */
+struct inode *ubifs_iget(struct super_block *sb, unsigned long inum);
+
+/* recovery.c */
+int ubifs_recover_master_node(struct ubifs_info *c);
+int ubifs_write_rcvrd_mst_node(struct ubifs_info *c);
+struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf, int jhead);
+struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum,
+ int offs, void *sbuf);
+int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf);
+int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
+int ubifs_rcvry_gc_commit(struct ubifs_info *c);
+int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
+ int deletion, loff_t new_size);
+int ubifs_recover_size(struct ubifs_info *c, bool in_place);
+void ubifs_destroy_size_tree(struct ubifs_info *c);
+
+/* ioctl.c */
+int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int ubifs_fileattr_set(struct mnt_idmap *idmap,
+ struct dentry *dentry, struct fileattr *fa);
+long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+void ubifs_set_inode_flags(struct inode *inode);
+#ifdef CONFIG_COMPAT
+long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#endif
+
+/* compressor.c */
+int __init ubifs_compressors_init(void);
+void ubifs_compressors_exit(void);
+void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
+ void *out_buf, int *out_len, int *compr_type);
+int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
+ void *out, int *out_len, int compr_type);
+
+/* sysfs.c */
+int ubifs_sysfs_init(void);
+void ubifs_sysfs_exit(void);
+int ubifs_sysfs_register(struct ubifs_info *c);
+void ubifs_sysfs_unregister(struct ubifs_info *c);
+
+#include "debug.h"
+#include "misc.h"
+#include "key.h"
+
+#ifndef CONFIG_FS_ENCRYPTION
+static inline int ubifs_encrypt(const struct inode *inode,
+ struct ubifs_data_node *dn,
+ unsigned int in_len, unsigned int *out_len,
+ int block)
+{
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ ubifs_assert(c, 0);
+ return -EOPNOTSUPP;
+}
+static inline int ubifs_decrypt(const struct inode *inode,
+ struct ubifs_data_node *dn,
+ unsigned int *out_len, int block)
+{
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+ ubifs_assert(c, 0);
+ return -EOPNOTSUPP;
+}
+#else
+/* crypto.c */
+int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
+ unsigned int in_len, unsigned int *out_len, int block);
+int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn,
+ unsigned int *out_len, int block);
+#endif
+
+extern const struct fscrypt_operations ubifs_crypt_operations;
+
+/* Normal UBIFS messages */
+__printf(2, 3)
+void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...);
+__printf(2, 3)
+void ubifs_err(const struct ubifs_info *c, const char *fmt, ...);
+__printf(2, 3)
+void ubifs_warn(const struct ubifs_info *c, const char *fmt, ...);
+/*
+ * A conditional variant of 'ubifs_err()' which doesn't output anything
+ * if probing (ie. SB_SILENT set).
+ */
+#define ubifs_errc(c, fmt, ...) \
+do { \
+ if (!(c)->probing) \
+ ubifs_err(c, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#endif /* !__UBIFS_H__ */
--
2.13.6
Add documents to describe fsck, which includes introductions, designment,
advantage and limitations.
Signed-off-by: Zhihao Cheng <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
---
ubifs-utils/fsck.ubifs/README.txt | 388 ++++++++++++++++++++++++++++++++++++++
1 file changed, 388 insertions(+)
create mode 100644 ubifs-utils/fsck.ubifs/README.txt
diff --git a/ubifs-utils/fsck.ubifs/README.txt b/ubifs-utils/fsck.ubifs/README.txt
new file mode 100644
index 00000000..99ad7c7c
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/README.txt
@@ -0,0 +1,388 @@
+ fsck.ubifs
+ ==========
+ The fsck.ubifs can check and repair the UBIFS image on a given UBI volume, it
+ could fix inconsistent UBIFS image(which is corrupted by hardware exceptions
+ or UBIFS realization bugs) and makes filesystem become consistent.
+
+
+ Manuals
+ -------
+ There are four modes for fsck.ubifs:
+ 1. normal mode(no options): Check the filesystem, ask user whether to fix the
+ problem as long as inconsistent data is found during fs checking.
+ 2. safe mode(-a option): Check and automatic safely repair the filesystem, if
+ there are any data dropping operations needed by fixing, fsck will fail.
+ 3. danger mode(-y option): Answer 'yes' to all questions. There are two sub
+ modes:
+ a) Default submode(no options): Check and automatic repair the filesystem
+ according to TNC, data dropping will be reported. If TNC/master/log is
+ corrupted, fsck will fail.
+ b) rebuild submode(-b option): Check and automatic forcibly repair the
+ filesystem, turns to rebuild filesystem if TNC/master/log is corrupted.
+ Always make fsck successful.
+ 4. check mode(-n option): Make no changes to the filesystem, only check the
+ filesystem. This mode doesn't check space, because unclean LEBs cannot be
+ rewritten in read-only mode.
+
+ The exit code returned by fsck.ubifs is compatible with FSCK(8), which is the
+ sum of the following conditions:
+ 0 - No errors
+ 1 - File system errors corrected
+ 2 - System should be rebooted
+ 4 - File system errors left uncorrected
+ 8 - Operational error
+ 16 - Usage or syntax error
+ 32 - Fsck canceled by user request
+ 128 - Shared library error
+
+
+ Designment
+ ----------
+ There are 2 working modes for fsck: rebuild mode and non-rebuild mode. The main
+ idea is that construct all files by scanning the entire filesystem, then check
+ the consistency of metadata(file meta information, space statistics, etc.)
+ according to the files. The file(xattr is treated as a file) is organized as:
+ file tree(rbtree, inum indexed)
+ / \
+ file1 file2
+ / \
+ file3 file4
+ file {
+ inode node // each file has 1 inode node
+ dentry (sub rb_tree, sqnum indexed)
+ // '/' has no dentries, otherwise at least 1 dentry is required.
+ trun node // the newest one truncation node
+ data (sub rb_tree, block number indexed)
+ // Each file may have 0 or many data nodes
+ xattrs (sub rb_tree, inum indexed)
+ // Each file may have 0 or many xattr files
+ }
+
+ Step 0. Both two modes need to read the superblock firstly, fsck fails if
+ superblock is corrupted, because fsck has no idea about the location
+ of each area(master, log, main, etc.) when the layout is lost.
+
+ A. Rebuild mode:
+ Step 1. Scan nodes(inode node/dentry node/data node/truncation node) from all
+ LEBs.
+ a) Corrupted LEBs(eg. garbage data, corrupted empty space) are dropped
+ during scanning.
+ b) Corrupted nodes(eg. incorrect crc, bad inode size, bad dentry name
+ length, etc.) are dropped during scanning.
+ c) Valid inode nodes(nlink > 0) and dentry nodes(inum != 0) are put
+ into two valid trees(valid_inos & valid_dents) separately.
+ d) Deleted inode nodes (nlink is 0) and deleted dentry nodes(inum is 0)
+ are put into two deleted trees(del_inos & del_dents) separately.
+ e) Other nodes(data nodes/truncation node) are put into corresponding
+ file, if the file doesn't exist, insert a new file into the file
+ tree.
+ Step 2. Traverse nodes from deleted trees, remove inode nodes and dentry nodes
+ with smaller sqnum from valid trees. valid_inos - del_inos = left_inos,
+ valid_dents - del_dents = left_dents.
+ This step handles the deleting case, for example, file A is deleted,
+ deleted inode node and deleted dentry node are written, if we ignore
+ the deleted nodes, file A can be recovered after rebuilding because
+ undeleted inode node and undeleted dentry node can be scanned. There's
+ an exception, if deleted inode node and deleted dentry node are
+ reclaimed(by gc) after deletion, file A is recovered. So deleted data
+ or files could be recovered by rebuild mode.
+ Step 3. Traverse left_inos and left_dents, insert inode node and dentry nodes
+ into the corresponding file.
+ Step 4. Traverse all files, drop invalid files, move xattr files into the
+ corresponding host file's subtree. Invalid files such as:
+ a) File has no inode node or inode nlink is zero
+ b) Non-consistent file types between inode node and dentry nodes
+ c) File has no dentry nodes(excepts '/')
+ d) Encrypted file has no xattr information
+ e) Non regular file has data nodes
+ f) Directory/xattr file has more than one dentries
+ g) Xattr file has no host inode, or the host inode is a xattr
+ h) Non-xattr file's parent is not a directory
+ i) etc.
+ Step 5. Extract reachable directory entries tree. Make sure that all files can
+ be searched from '/', unreachable file is deleted. Since all xattr
+ files are attached to the corresponding host file, only non-xattr
+ files should be checked. Luckily, directory file only has one dentry,
+ the reachable checking of a dentry becomes easy. Traverse all
+ dentries for each file, check whether the dentry is reachable, if not,
+ remove dentry from the file. If the file has no dentries, the file is
+ unreachable.
+ Step 6. Correct the file information. Traverse all files and calculate
+ information(nlink, size, xattr_cnt, etc.) for each file just like
+ check_leaf(in linux kernel) does, correct the inode node based on the
+ calculated information.
+ Step 7. Record used LEBs. Traverse all files'(including effective nodes from
+ deletion trees in step 2) position, after this step fsck knows which
+ LEB is empty.
+ Step 8. Re-write data. Read data from LEB and write back data, make sure that
+ all LEB is ended with empty data(0xFF). It will prevent failed gc
+ scanning in the next mounting.
+ Step 9. Build TNC. Construct TNC according to all files' nodes, just like mkfs
+ does(refer to add_to_index in mkfs), then write TNC(refer to
+ write_index in mkfs) on flash. (If there are no files, create a new
+ root dir file.)
+ Step 10.Build LPT. Construct LPT according to all nodes' position and length,
+ just like mkfs does, then write LPT(refer to write_lpt) on flash.
+ Step 11.Clean up log area and orphan area. Log area and orphan area can be
+ erased.
+ Step 12.Write master node. Since all meta areas are ready, master node can be
+ updated.
+
+ B. Non-rebuild mode:
+ Step 1. Read master & init lpt.
+ a) Scan master nodes failed or master node is invalid (which is not
+ caused by invalid space statistics), danger mode with rebuild_fs and
+ normal mode with 'yes' answer will turn to rebuild mode, other modes
+ will exit. Fsck cannot find the right TNC/LPT if the master node is
+ invalid, which affects subsequent steps, so this problem must be
+ fixed.
+ b) Invalid space statistics in master node, set %FR_LPT_INCORRECT for
+ for lpt status and ignore the error.
+ c) LPT node is corrupted, set %FR_LPT_CORRUPTED for lpt status and
+ ignore the error.
+ Step 2. Replay journal.
+ I. Scan log LEBs to get all buds.
+ a) Nodes in log LEBs are invalid/corrupted, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to rebuild
+ mode, other modes will exit. Corrupted log LEB could fail
+ ubifs_consolidate_log, which may lead to commit failure by out of
+ space in the log area, so this problem must be fixed.
+ II. Scan bud LEBs to get all nodes.
+ a) Nodes in bud LEBs are invalid/corrupted, danger mode and normal
+ mode with 'yes' answer will drop bud LEB and set
+ %FR_LPT_INCORRECT for lpt status, other modes will exit.
+ Corrupted LEB will make gc failed, so this problem must be
+ fixed.
+ III. Record isize into size tree according to data/truncation/inode
+ nodes.
+ IV. Apply nodes to TNC & LPT, update property for bud LEBs.
+ a) Corrupted/Invalid node searched from TNC, skip node and set
+ %FR_LPT_INCORRECT in lpt status for danger mode and normal mode
+ with 'yes' answer, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ b) Corrupted/Invalid index node read from TNC, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ c) Corrupted/Invalid lpt node, Set %FR_LPT_CORRUPTED for lpt status
+ and ignore the error.
+ d) Incorrect LEB property: Set %FR_LPT_INCORRECT for lpt status and
+ ignore the error.
+ e) If lpt status is not empty, skip updating lpt, because incorrect
+ LEB property could trigger assertion failure in ubifs_change_lp.
+ Step 3. Handle orphan nodes.
+ I. Scan orphan LEB to get all orphan nodes.
+ a) Corrupted/Invalid orphan node: danger mode and normal mode with
+ 'yes' answer will drop orphan LEB, other modes will exit.
+ Corrupted orphan area could lead to mounting/committing failure,
+ so this problem must be fixed.
+ II. Parse orphan node, find the original inode for each inum.
+ a) Corrupted/Invalid node searched from TNC, skip node for danger
+ mode and normal mode with 'yes' answer, other modes will exit.
+ b) Corrupted/Invalid index node read from TNC, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ III. Remove inode for each inum, update TNC & LPT.
+ a) Corrupted/Invalid node searched from TNC, skip node for danger
+ mode and normal mode with 'yes' answer, other modes will exit.
+ b) Corrupted/Invalid index node read from TNC, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ c) Corrupted/Invalid lpt node, Set %FR_LPT_CORRUPTED for lpt
+ status and ignore the error.
+ d) Incorrect LEB property: Set %FR_LPT_INCORRECT for lpt status
+ and ignore the error.
+ e) If lpt status is not empty, skip updating lpt, because
+ incorrect LEB property could trigger assertion failure in
+ ubifs_change_lp.
+ Step 4. Consolidate log area.
+ a) Corrupted data in log LEBs, danger mode with rebuild_fs and normal
+ mode with 'yes' answer will turn to rebuild filesystem, other modes
+ will exit. It could make commit failed by out of space in log area,
+ so this problem must be fixed.
+ Step 5. Recover isize.
+ I. Traverse size tree, lookup corresponding inode from TNC.
+ a) Corrupted/Invalid node searched from TNC, skip node for danger
+ mode and normal mode with 'yes' answer, other modes will exit.
+ b) Corrupted/Invalid index node read from TNC, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ II. Update isize for inode. Keep <inum, isize> in size tree for check
+ mode, remove <inum, isize> from the size tree and update inode
+ node in place for other modes.
+ Step 6. Traverse TNC and construct files.
+ I. Traverse TNC, check whether the leaf node is valid, remove invalid
+ nodes, construct file for valid node and insert the file into the
+ file tree.
+ a) Corrupted/Invalid node searched from TNC, remove corresponding
+ TNC branch for danger mode and normal mode with 'yes' answer,
+ other modes will exit. The space statistics depend on a valid
+ TNC, so this problem must be fixed.
+ b) Corrupted/Invalid index node read from TNC, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. The space statistics
+ depend on a valid TNC, so this problem must be fixed.
+ II. Scan all LEBs(contain TNC) for non check mode(unclean LEBs cannot
+ be fixed in read-only mode, so scanning may fail in check mode,
+ then space statistics won't be checked in check mode), remove TNC
+ branch which points to corrupted LEB.
+ a) Corrupted data is found by scanning. If the current node is
+ index node, danger mode with rebuild_fs and normal mode with
+ 'yes' answer will turn to rebuild filesystem, other modes will
+ exit; If the current node is non-index node, danger mode and
+ normal mode with 'yes' answer will remove all TNC branches which
+ point to the corrupted LEB, other modes will exit. The space
+ statistics depend on valid LEB scanning, so this problem must
+ be fixed.
+ b) LEB contains both index and non-index nodes, danger mode with
+ rebuild_fs and normal mode with 'yes' answer will turn to
+ rebuild filesystem, other modes will exit. Invalid LEB will make
+ gc failed, so this problem must be fixed.
+ Step 7. Update files' size for check mode. Update files' size according to the
+ size tree for check mode.
+ Step 8. Check and handle invalid files. Similar to rebuild mode, but the
+ methods of handling are different:
+ a) Move unattached(file has no dentries) regular file into disconnected
+ list for safe mode, danger mode and normal mode with 'yes' answer,
+ let subsequent steps to handle them with lost+found. Other modes
+ will exit. Disconnected file affects the result of calculated
+ information(which will be used in subsequent steps) for its' parent
+ file(eg. nlink, size), so this problem must be fixed.
+ b) Make file type be consistent between inode, detries and data nodes
+ by deleting dentries or data nodes, for danger mode and normal mode
+ with 'yes' answer, other modes will exit.
+ c) Delete file for other invalid cases(eg. file has no inode) in
+ danger mode and normal mode with 'yes' answer, other modes will
+ exit.
+ Step 9. Extract reachable directory entries tree. Similar to rebuild mode, but
+ the methods of handling are different:
+ a) Remove unreachable dentry for danger mode and normal mode with 'yes'
+ answer, other modes will exit. Unreachable dentry affects the
+ calculated information(which will be used in subsequent steps) for
+ its' file(eg. nlink), so this problem must be fixed.
+ b) Delete unreachable non-regular file for danger mode and normal mode
+ with 'yes' answer, other modes will exit. Unreachable file affects
+ the calculated information(which will be used in subsequent steps)
+ for its' parent file(eg. nlink, size), so this problem must be
+ fixed.
+ c) Move unreachable regular file into disconnected list for safe mode,
+ danger mode and normal mode with 'yes' answer, let subsequent steps
+ to handle them with lost+found. Other modes will exit. Disconnected
+ file affects the calculated information(which will be used in
+ subsequent steps) for its' parent file(eg. nlink, size), so this
+ problem must be fixed.
+ Step 10.Correct the file information. Similar to rebuild mode, but the methods
+ of handling are different:
+ a) Correct the file information for safe mode, danger mode and normal
+ mode with 'yes' answer, other modes will exit. Incorrect file
+ information affects the new creations(which will be used in handling
+ lost+found), so this problem must be fixed.
+ Step 11.Check whether the TNC is empty. Empty TNC is equal to corrupted TNC,
+ which means that zero child count for root znode. If TNC is empty(All
+ nodes are invalid and are deleted from TNC), turn to rebuild mode for
+ danger mode with rebuild_fs and normal mode with 'yes' answer, other
+ modes will exit.
+ Step 12.Check and correct the space statistics.
+ I. Exit for check mode, if %FR_LPT_CORRUPTED or %FR_LPT_INCORRECT is
+ set in lpt status, the exit code should have %FSCK_UNCORRECTED.
+ II. Check lpt status, if %FR_LPT_CORRUPTED is set in lpt status, normal
+ mode with 'no' answer will exit, other modes will rebuild lpt. New
+ creations could be done in subsequent steps, which depends on
+ correct space statistics, so this problem must be fixed.
+ III. Traverse LPT nodes, check the correctness of nnode and pnode,
+ compare LEB scanning result with LEB properties.
+ a) LPT node is corrupted, normal mode with 'no' answer will exit,
+ rebuild lpt for other modes. New creations could be done in
+ subsequent steps, which depends on the correct space
+ statistics, so this problem must be fixed.
+ b) Incorrect nnode/pnode, normal mode with 'no' answer will exit,
+ other other modes will correct the nnode/pnode. New creations
+ could be done in subsequent steps, which depends on correct
+ space statistics, so this problem must be fixed.
+ c) Inconsistent comparing result, normal mode with 'no' answer
+ will exit, other modes will correct the space statistics. New
+ creations could be done in subsequent steps, which depends on
+ correct space statistics, so this problem must be fixed.
+ IV. Compare LPT area scanning result with lprops table information.
+ a) LPT area is corrupted, normal mode with 'no' answer will exit,
+ rebuild lpt for other modes. Commit could fail in doing LPT gc
+ caused by scanning corrupted data, so this problem must be
+ fixed.
+ b) Inconsistent comparing result, normal mode with 'no' answer
+ will exit, other modes will correct the lprops table
+ information. Commit could fail in writing LPT with %ENOSPC
+ return code caused by incorrect space statistics in the LPT
+ area, so this problem must be fixed.
+ Step 13.Do commit, commit problem fixing modifications to disk. The index size
+ checking depends on this step.
+ Step 14.Check and correct the index size. Check and correct the index size by
+ traversing TNC just like dbg_check_idx_size does. This step should be
+ executed after first committing, because 'c->calc_idx_sz' can be
+ changed in 'ubifs_tnc_start_commit' and the initial value of
+ 'c->calc_idx_sz' read from the disk is untrusted. Correct the index
+ size for safe mode, danger mode and normal mode with 'yes' answer,
+ other modes will exit. New creations could be done in subsequent steps,
+ which depends on the correct index size, so this problem must be fixed.
+ Step 15.Check and create the root dir. Check whether the root dir exists,
+ create a new one if it is not found, for safe mode, danger mode and
+ normal mode with 'yes' answer, other modes will exit. Mounting depends
+ on the root dir, so this problem must be fixed.
+ Step 16.Check and create the lost+found.
+ I. If the root dir is encrypted, set lost+found as invalid. Because it
+ is impossible to check whether the lost+found exists in an encrypted
+ directory.
+ II. Search the lost+found under root dir.
+ a) Found a lost+found, lost+found is a non-encrypted directory, set
+ lost+found as valid, otherwise set lost+found as invalid.
+ b) Not found the lost+found, create a new one. If creation is
+ failed by %ENOSPC, set lost+found as invalid.
+ Step 17.Handle each file from the disconnected list.
+ I. If lost+found is invalid, delete file for danger mode and normal
+ mode with 'yes' answer, other modes will skip and set the exit code
+ with %FSCK_UNCORRECTED.
+ II. If lost+found is valid, link disconnected file under lost+found
+ directory with the name of the corresponding inode number
+ (INO_<inum>_<index>, index(starts from 0) is used to handle the
+ conflicted names).
+ a) Fails in handling conflicted file names, delete file for danger
+ mode and normal mode with 'yes' answer, other modes will skip
+ and set the exit code with %FSCK_UNCORRECTED.
+ b) Fails in linking caused by %ENOSPC, delete file for danger mode
+ and normal mode with 'yes' answer, other modes will skip and set
+ the exit code with %FSCK_UNCORRECTED.
+ Step 18.Do final commit, commit problem fixing modifications to disk and clear
+ %UBIFS_MST_DIRTY flag for master node.
+
+
+ Advantage
+ ---------
+ 1. Can be used for any UBIFS image, fsck has nothing to do with kernel version.
+ 2. Fsck is tolerant with power-cut, fsck will always succeed in a certain mode
+ without changing mode even power-cut happens in checking and repairing. In
+ other words, fsck won't let UBIFS image become worse in abnormal situations.
+ 3. It is compatible with FSCK(8), the exit code returned by fsck.ubifs is same
+ as FSCK, the command options used by fsck are supported in fsck.ubifs too.
+ 4. The UBIFS image can be fixed as long as the super block is not corrupted.
+ 5. Encrypted UBIFS image is supported, because dentry name and data content of
+ file are not necessary for fsck.
+
+
+ Limitations
+ -----------
+ 1. UBIFS image file is not supported(Not like ext4). The UBIFS image file is
+ not equal to UBI volume, empty LEBs are not included in image file, so UBIFS
+ cannot allocate empty space when file recovering is needed. Another reason
+ is that atomic LEB changing is not supported by image file.
+ 2. Authenticated UBIFS image is not supported, UBIFS metadata(TNC/LPT) parsing
+ depends on the authentication key which is not supported in fsck options.
+
+
+ Authors
+ -------
+ Zhihao Cheng <[email protected]>
+ Zhang Yi <[email protected]>
+ Xiang Yang <[email protected]>
+ Huang Xiaojia <[email protected]>
--
2.13.6
This is the 8/12 step of rebuilding. Re-write data. Read data from
LEB and write back data, make sure that all LEB is ended with empty
data(0xFF). It will prevent failed gc scanning in next mounting.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 107 ++++++++++++++++++++++++++++--------
ubifs-utils/libubifs/ubifs.h | 2 +
3 files changed, 89 insertions(+), 22 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 0b065935..f027ec3f 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -181,10 +181,12 @@ struct scanned_file {
/**
* ubifs_rebuild_info - UBIFS rebuilding information.
* @used_lebs: a bitmap used for recording used lebs
+ * @lpts: lprops table
* @scanned_files: tree of all scanned files
*/
struct ubifs_rebuild_info {
unsigned long *used_lebs;
+ struct ubifs_lprops *lpts;
struct rb_root scanned_files;
};
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index f5412f08..1b03d6c0 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -58,9 +58,18 @@ static int init_rebuild_info(struct ubifs_info *c)
log_err(c, errno, "can not allocate bitmap of used lebs");
goto free_rebuild;
}
+ FSCK(c)->rebuild->lpts = kzalloc(sizeof(struct ubifs_lprops) * c->main_lebs,
+ GFP_KERNEL);
+ if (!FSCK(c)->rebuild->lpts) {
+ err = -ENOMEM;
+ log_err(c, errno, "can not allocate lpts");
+ goto free_used_lebs;
+ }
return 0;
+free_used_lebs:
+ kfree(FSCK(c)->rebuild->used_lebs);
free_rebuild:
kfree(FSCK(c)->rebuild);
free_sbuf:
@@ -70,6 +79,7 @@ free_sbuf:
static void destroy_rebuild_info(struct ubifs_info *c)
{
+ kfree(FSCK(c)->rebuild->lpts);
kfree(FSCK(c)->rebuild->used_lebs);
kfree(FSCK(c)->rebuild);
vfree(c->sbuf);
@@ -461,9 +471,13 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_ino_node = lookup_valid_ino_node(c, si, del_ino_node);
if (valid_ino_node) {
- int lnum = del_ino_node->header.lnum;
+ int lnum = del_ino_node->header.lnum - c->main_first;
+ int pos = del_ino_node->header.offs +
+ ALIGN(del_ino_node->header.len, 8);
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ set_bit(lnum, FSCK(c)->rebuild->used_lebs);
+ FSCK(c)->rebuild->lpts[lnum].end =
+ max_t(int, FSCK(c)->rebuild->lpts[lnum].end, pos);
rb_erase(&valid_ino_node->rb, &si->valid_inos);
kfree(valid_ino_node);
}
@@ -479,9 +493,13 @@ static void remove_del_nodes(struct ubifs_info *c, struct scanned_info *si)
valid_dent_node = lookup_valid_dent_node(c, si, del_dent_node);
if (valid_dent_node) {
- int lnum = del_dent_node->header.lnum;
+ int lnum = del_dent_node->header.lnum - c->main_first;
+ int pos = del_dent_node->header.offs +
+ ALIGN(del_dent_node->header.len, 8);
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ set_bit(lnum, FSCK(c)->rebuild->used_lebs);
+ FSCK(c)->rebuild->lpts[lnum].end =
+ max_t(int, FSCK(c)->rebuild->lpts[lnum].end, pos);
rb_erase(&valid_dent_node->rb, &si->valid_dents);
kfree(valid_dent_node);
}
@@ -667,10 +685,21 @@ static const char *get_file_name(struct ubifs_info *c, struct scanned_file *file
return name;
}
+static void parse_node_location(struct ubifs_info *c, struct scanned_node *sn)
+{
+ int lnum, pos;
+
+ lnum = sn->lnum - c->main_first;
+ pos = sn->offs + ALIGN(sn->len, 8);
+
+ set_bit(lnum, FSCK(c)->rebuild->used_lebs);
+ FSCK(c)->rebuild->lpts[lnum].end = max_t(int,
+ FSCK(c)->rebuild->lpts[lnum].end, pos);
+}
+
static void record_file_used_lebs(struct ubifs_info *c,
struct scanned_file *file)
{
- int lnum;
struct rb_node *node;
struct scanned_file *xattr_file;
struct scanned_dent_node *dent_node;
@@ -682,26 +711,21 @@ static void record_file_used_lebs(struct ubifs_info *c,
ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
c->dev_name);
- lnum = file->ino.header.lnum;
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ parse_node_location(c, &file->ino.header);
- if (file->trun.header.exist) {
- lnum = file->trun.header.lnum;
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
- }
+ if (file->trun.header.exist)
+ parse_node_location(c, &file->trun.header);
for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) {
data_node = rb_entry(node, struct scanned_data_node, rb);
- lnum = data_node->header.lnum;
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ parse_node_location(c, &data_node->header);
}
for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
dent_node = rb_entry(node, struct scanned_dent_node, rb);
- lnum = dent_node->header.lnum;
- set_bit(lnum - c->main_first, FSCK(c)->rebuild->used_lebs);
+ parse_node_location(c, &dent_node->header);
}
for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
@@ -712,22 +736,57 @@ static void record_file_used_lebs(struct ubifs_info *c,
}
/**
- * record_used_lebs - record used LEBs.
+ * traverse_files_and_nodes - traverse all nodes from valid files.
* @c: UBIFS file-system description object
*
- * This function records all used LEBs which may hold useful nodes, then left
- * unused LEBs could be taken for storing new index tree.
+ * This function traverses all nodes from valid files and does following
+ * things:
+ * 1. Record all used LEBs which may hold useful nodes, then left unused
+ * LEBs could be taken for storing new index tree.
+ * 2. Re-write data to prevent failed gc scanning in the subsequent mounting
+ * process caused by corrupted data.
*/
-static void record_used_lebs(struct ubifs_info *c)
+static int traverse_files_and_nodes(struct ubifs_info *c)
{
+ int i, err = 0;
+ struct rb_node *node;
struct scanned_file *file;
struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ log_out(c, "Record used LEBs");
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
record_file_used_lebs(c, file);
}
+
+ /* Re-write data. */
+ log_out(c, "Re-write data");
+ for (i = 0; i < c->main_lebs; ++i) {
+ int lnum, len, end;
+
+ if (!test_bit(i, FSCK(c)->rebuild->used_lebs))
+ continue;
+
+ lnum = i + c->main_first;
+ dbg_fsck("re-write LEB %d, in %s", lnum, c->dev_name);
+
+ end = FSCK(c)->rebuild->lpts[i].end;
+ len = ALIGN(end, c->min_io_size);
+
+ err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 0);
+ if (err && err != -EBADMSG)
+ return err;
+
+ if (len > end)
+ ubifs_pad(c, c->sbuf + end, len - end);
+
+ err = ubifs_leb_change(c, lnum, c->sbuf, len);
+ if (err)
+ return err;
+ }
+
+ return err;
}
/**
@@ -788,9 +847,13 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
goto out;
}
- /* Step 7: Record used LEBs. */
- log_out(c, "Record used LEBs");
- record_used_lebs(c);
+ /*
+ * Step 7: Record used LEBs.
+ * Step 8: Re-write data to clean corrupted data.
+ */
+ err = traverse_files_and_nodes(c);
+ if (err)
+ exit_code |= FSCK_ERROR;
out:
destroy_scanned_info(c, &si);
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index e6de7cea..c9d582da 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -337,6 +337,7 @@ enum {
* @dirty: amount of dirty space in bytes
* @flags: LEB properties flags (see above)
* @lnum: LEB number
+ * @end: the end postition of LEB calculated by the last node
* @list: list of same-category lprops (for LPROPS_EMPTY and LPROPS_FREEABLE)
* @hpos: heap position in heap of same-category lprops (other categories)
*/
@@ -345,6 +346,7 @@ struct ubifs_lprops {
int dirty;
int flags;
int lnum;
+ int end;
union {
struct list_head list;
int hpos;
--
2.13.6
In order to check the consistency of each file and the reachability of
the whole dentry tree, fsck orginizes all nodes into files. And the
final recovered file(xattr is treated as a file) is organized as:
(rbtree, inum indexed)
/ \
file1 file2
/ \
file3 file4
file {
inode node // each file has 1 inode node
dentry (sub rb_tree, sqnum indexed) // '/' has no dentries,
// otherwise at least 1
// dentry is required.
trun node // the newest one truncation node
data (sub rb_tree, block number indexed) // Each file may have 0
// or many data nodes
xattrs (sub rb_tree, inum indexed) // Each file may have 0 or
// many xattr files
}
Each file from file rb_tree is constructed by scanning nodes(eg. inode,
dentry, etc.) from the TNC or the UBI volume. File's xattrs will be
initialized in subsequent steps.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/extract_files.c | 275 +++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 43 ++++++
2 files changed, 318 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index dd52ef84..58aa9db8 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -403,3 +403,278 @@ bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
out:
return valid;
}
+
+/**
+ * insert_file_dentry - insert dentry according to scanned dent node.
+ * @file: file object
+ * @n_dent: scanned dent node
+ *
+ * Insert file dentry information. Returns zero in case of success, a
+ * negative error code in case of failure.
+ */
+static int insert_file_dentry(struct scanned_file *file,
+ struct scanned_dent_node *n_dent)
+{
+ struct scanned_dent_node *dent;
+ struct rb_node **p, *parent = NULL;
+
+ p = &file->dent_nodes.rb_node;
+ while (*p) {
+ parent = *p;
+ dent = rb_entry(parent, struct scanned_dent_node, rb);
+ if (n_dent->header.sqnum < dent->header.sqnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ dent = kmalloc(sizeof(struct scanned_dent_node), GFP_KERNEL);
+ if (!dent)
+ return -ENOMEM;
+
+ *dent = *n_dent;
+ rb_link_node(&dent->rb, parent, p);
+ rb_insert_color(&dent->rb, &file->dent_nodes);
+
+ return 0;
+}
+
+/**
+ * update_file_data - insert/update data according to scanned data node.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @n_dn: scanned data node
+ *
+ * Insert or update file data information. Returns zero in case of success,
+ * a negative error code in case of failure.
+ */
+static int update_file_data(struct ubifs_info *c, struct scanned_file *file,
+ struct scanned_data_node *n_dn)
+{
+ int cmp;
+ struct scanned_data_node *dn, *o_dn = NULL;
+ struct rb_node **p, *parent = NULL;
+
+ p = &file->data_nodes.rb_node;
+ while (*p) {
+ parent = *p;
+ dn = rb_entry(parent, struct scanned_data_node, rb);
+ cmp = keys_cmp(c, &n_dn->key, &dn->key);
+ if (cmp < 0) {
+ p = &(*p)->rb_left;
+ } else if (cmp > 0) {
+ p = &(*p)->rb_right;
+ } else {
+ o_dn = dn;
+ break;
+ }
+ }
+
+ if (o_dn) {
+ /* found data node with same block no. */
+ if (o_dn->header.sqnum < n_dn->header.sqnum) {
+ o_dn->header = n_dn->header;
+ o_dn->size = n_dn->size;
+ }
+
+ return 0;
+ }
+
+ dn = kmalloc(sizeof(struct scanned_data_node), GFP_KERNEL);
+ if (!dn)
+ return -ENOMEM;
+
+ *dn = *n_dn;
+ INIT_LIST_HEAD(&dn->list);
+ rb_link_node(&dn->rb, parent, p);
+ rb_insert_color(&dn->rb, &file->data_nodes);
+
+ return 0;
+}
+
+/**
+ * update_file - update file information.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @sn: scanned node
+ * @key_type: type of @sn
+ *
+ * Update inode/dent/truncation/data node information of @file. Returns
+ * zero in case of success, a negative error code in case of failure.
+ */
+static int update_file(struct ubifs_info *c, struct scanned_file *file,
+ struct scanned_node *sn, int key_type)
+{
+ int err = 0;
+
+ switch (key_type) {
+ case UBIFS_INO_KEY:
+ {
+ struct scanned_ino_node *o_ino, *n_ino;
+
+ o_ino = &file->ino;
+ n_ino = (struct scanned_ino_node *)sn;
+ if (o_ino->header.exist && o_ino->header.sqnum > sn->sqnum)
+ goto out;
+
+ *o_ino = *n_ino;
+ break;
+ }
+ case UBIFS_DENT_KEY:
+ case UBIFS_XENT_KEY:
+ {
+ struct scanned_dent_node *dent = (struct scanned_dent_node *)sn;
+
+ err = insert_file_dentry(file, dent);
+ break;
+ }
+ case UBIFS_DATA_KEY:
+ {
+ struct scanned_data_node *dn = (struct scanned_data_node *)sn;
+
+ err = update_file_data(c, file, dn);
+ break;
+ }
+ case UBIFS_TRUN_KEY:
+ {
+ struct scanned_trun_node *o_trun, *n_trun;
+
+ o_trun = &file->trun;
+ n_trun = (struct scanned_trun_node *)sn;
+ if (o_trun->header.exist && o_trun->header.sqnum > sn->sqnum)
+ goto out;
+
+ *o_trun = *n_trun;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ log_err(c, 0, "unknown key type %d", key_type);
+ }
+
+out:
+ return err;
+}
+
+/**
+ * insert_or_update_file - insert or update file according to scanned node.
+ * @c: UBIFS file-system description object
+ * @file_tree: tree of all scanned files
+ * @sn: scanned node
+ * @key_type: key type of @sn
+ * @inum: inode number
+ *
+ * According to @sn, this function inserts file into the tree, or updates
+ * file information if it already exists in the tree. Returns zero in case
+ * of success, a negative error code in case of failure.
+ */
+int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
+ struct scanned_node *sn, int key_type, ino_t inum)
+{
+ int err;
+ struct scanned_file *file, *old_file = NULL;
+ struct rb_node **p, *parent = NULL;
+
+ p = &file_tree->rb_node;
+ while (*p) {
+ parent = *p;
+ file = rb_entry(parent, struct scanned_file, rb);
+ if (inum < file->inum) {
+ p = &(*p)->rb_left;
+ } else if (inum > file->inum) {
+ p = &(*p)->rb_right;
+ } else {
+ old_file = file;
+ break;
+ }
+ }
+ if (old_file)
+ return update_file(c, old_file, sn, key_type);
+
+ file = kzalloc(sizeof(struct scanned_file), GFP_KERNEL);
+ if (!file)
+ return -ENOMEM;
+
+ file->inum = inum;
+ file->dent_nodes = RB_ROOT;
+ file->data_nodes = RB_ROOT;
+ file->xattr_files = RB_ROOT;
+ INIT_LIST_HEAD(&file->list);
+ err = update_file(c, file, sn, key_type);
+ if (err) {
+ kfree(file);
+ return err;
+ }
+ rb_link_node(&file->rb, parent, p);
+ rb_insert_color(&file->rb, file_tree);
+
+ return 0;
+}
+
+/**
+ * destroy_file_content - destroy scanned data/dentry nodes in give file.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ *
+ * Destroy all data/dentry nodes and xattrs attached to @file.
+ */
+void destroy_file_content(struct ubifs_info *c, struct scanned_file *file)
+{
+ struct scanned_data_node *data_node;
+ struct scanned_dent_node *dent_node;
+ struct scanned_file *xattr_file;
+ struct rb_node *this;
+
+ this = rb_first(&file->data_nodes);
+ while (this) {
+ data_node = rb_entry(this, struct scanned_data_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&data_node->rb, &file->data_nodes);
+ kfree(data_node);
+ }
+
+ this = rb_first(&file->dent_nodes);
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&dent_node->rb, &file->dent_nodes);
+ kfree(dent_node);
+ }
+
+ this = rb_first(&file->xattr_files);
+ while (this) {
+ xattr_file = rb_entry(this, struct scanned_file, rb);
+ this = rb_next(this);
+
+ ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
+ destroy_file_content(c, xattr_file);
+ rb_erase(&xattr_file->rb, &file->xattr_files);
+ kfree(xattr_file);
+ }
+}
+
+/**
+ * destroy_file_tree - destroy files from a given tree.
+ * @c: UBIFS file-system description object
+ * @file_tree: tree of all scanned files
+ *
+ * Destroy scanned files from a given tree.
+ */
+void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
+{
+ struct scanned_file *file;
+ struct rb_node *this;
+
+ this = rb_first(file_tree);
+ while (this) {
+ file = rb_entry(this, struct scanned_file, rb);
+ this = rb_next(this);
+
+ destroy_file_content(c, file);
+
+ rb_erase(&file->rb, file_tree);
+ kfree(file);
+ }
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 3511d90e..80d3af84 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -136,6 +136,45 @@ struct scanned_trun_node {
};
/**
+ * scanned_file - file info scanned from UBIFS volume.
+ *
+ * @calc_nlink: calculated count of directory entries refer this inode
+ * @calc_xcnt: calculated count of extended attributes
+ * @calc_xsz: calculated summary size of all extended attributes
+ * @calc_xnms: calculated sum of lengths of all extended attribute names
+ * @calc_size: calculated file size
+ * @has_encrypted_info: whether the file has encryption related xattrs
+ *
+ * @inum: inode number
+ * @ino: inode node
+ * @trun: truncation node
+ *
+ * @rb: link in the tree of all scanned files
+ * @list: link in the list files for kinds of processing
+ * @dent_nodes: tree of all scanned dentry nodes
+ * @data_nodes: tree of all scanned data nodes
+ * @xattr_files: tree of all scanned xattr files
+ */
+struct scanned_file {
+ unsigned int calc_nlink;
+ unsigned int calc_xcnt;
+ unsigned int calc_xsz;
+ unsigned int calc_xnms;
+ unsigned long long calc_size;
+ bool has_encrypted_info;
+
+ ino_t inum;
+ struct scanned_ino_node ino;
+ struct scanned_trun_node trun;
+
+ struct rb_node rb;
+ struct list_head list;
+ struct rb_root dent_nodes;
+ struct rb_root data_nodes;
+ struct rb_root xattr_files;
+};
+
+/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
* @failure_reason: reasons for failed operations
@@ -208,5 +247,9 @@ bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
union ubifs_key *key, struct scanned_data_node *data_node);
bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
union ubifs_key *key, struct scanned_trun_node *trun_node);
+int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
+ struct scanned_node *sn, int key_type, ino_t inum);
+void destroy_file_content(struct ubifs_info *c, struct scanned_file *file);
+void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
#endif
--
2.13.6
This is the 11/12 step of rebuilding. Clean up log and orphan area, all
nodes have been recovered, these two areas should be cleared, otherwise
old content in journal/orphan could be replayed in next mounting.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/rebuild_fs.c | 46 +++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 085df2b9..9ea4c224 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -1312,6 +1312,39 @@ static int build_lpt(struct ubifs_info *c)
}
/**
+ * clean_log - clean up log area.
+ * @c: UBIFS file-system description object
+ *
+ * This function cleans up log area, since there is no need to do recovery
+ * in next mounting.
+ */
+static int clean_log(struct ubifs_info *c)
+{
+ int lnum, err;
+ struct ubifs_cs_node *cs;
+
+ for (lnum = UBIFS_LOG_LNUM; lnum <= c->log_last; lnum++) {
+ err = ubifs_leb_unmap(c, lnum);
+ if (err)
+ return err;
+ }
+
+ cs = kzalloc(ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size), GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+
+ cs->ch.node_type = UBIFS_CS_NODE;
+ cs->cmt_no = cpu_to_le64(0);
+
+ err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
+ kfree(cs);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/**
* ubifs_rebuild_filesystem - Rebuild filesystem.
* @c: UBIFS file-system description object
*
@@ -1383,6 +1416,19 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 10. Build LPT. */
log_out(c, "Build LPT");
err = build_lpt(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out;
+ }
+
+ /* Step 11. Clean up log & orphan. */
+ log_out(c, "Clean up log & orphan");
+ err = clean_log(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out;
+ }
+ err = ubifs_clear_orphans(c);
if (err)
exit_code |= FSCK_ERROR;
--
2.13.6
Do fsstress and fsck, check whether there are any files(and their data)
are lost after fsck. This testcase mainly checks whether fsck.ubifs could
corrupt the filesystem content in common case.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/cycle_mount_fsck_check.sh.in | 144 +++++++++++++++++++++
4 files changed, 149 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh.in
diff --git a/.gitignore b/.gitignore
index 799290a4..7de38e86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -115,6 +115,7 @@ tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
+tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 160fa812..9d17b9ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -291,7 +291,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
- tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh])
+ tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 6b533982..68c77a62 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -1,3 +1,4 @@
test_SCRIPTS += \
tests/ubifs_tools-tests/lib/common.sh \
- tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
+ tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh \
+ tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh.in b/tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh.in
new file mode 100755
index 00000000..06e480b8
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh.in
@@ -0,0 +1,144 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# Do many cycles of mount/fsstress/umount/fsck/mount, check whether the
+# filesystem content before fsck and after fsck are consistent.
+# Running time: 10h
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+ID="0xec,0xa1,0x00,0x15" # 128M 128KB 2KB 512-sub-page
+
+function run_test()
+{
+ encryption=$1
+ modprobe nandsim id_bytes=$ID
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ flash_eraseall /dev/mtd$mtdnum
+
+ dmesg -c > /dev/null
+
+ modprobe ubi mtd="$mtdnum,2048" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+
+ echo "Do cycle mount+umount+fsck+check_fs_content test ($encryption)"
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ fi
+
+ round=0
+ while [[ $round -lt 20 ]]
+ do
+ echo "---------------------- ROUND $round ----------------------"
+ let round=$round+1
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime" || fatal "mount ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_set_key $MNT
+ fi
+
+ per=`df -Th | grep ubifs | awk '{print $6}'`;
+ if [[ ${per%?} -gt 95 ]]; then
+ # Used > 95%
+ echo "Clean files"
+ rm -rf $MNT/*
+ check_err_msg
+ fi
+
+ fsstress -d $MNT -l0 -p4 -n10000 &
+
+ sleep $((RANDOM % 30))
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ per=`df -Th | grep ubifs | awk '{print $6}'`;
+ if [[ ${per%?} -gt 95 ]]; then
+ dmesg -c > /dev/null # The ENOSPC error messages may exist
+ else
+ check_err_msg # Make sure new operations are okay after fsck
+ fi
+ sync
+
+ # Record filesystem information
+ rm -f $TMP_FILE 2>/dev/null
+ read_dir $MNT "md5sum"
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ fsck.ubifs -a $DEV # 'fsck.ubifs $DEV' is fine too.
+ res=$?
+ if [[ $res != $FSCK_OK ]]
+ then
+ fatal "fsck expects result $FSCK_OK, but $res is returned"
+ fi
+
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_set_key $MNT
+ fi
+
+ du -sh $MNT > /dev/null # Ensure that all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+ check_err_msg
+
+ # Check filesystem information
+ parse_dir "md5sum"
+ rm -f $TMP_FILE 2>/dev/null
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+
+ check_err_msg
+
+ disable_chkfs
+ done
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r nandsim
+}
+
+check_fsstress
+start_t=$(date +%s)
+for encryption in "encrypted" "noencrypted"; do
+ run_test $encryption
+done
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
Add rebuilding filesystem support. This is the 1/12 step of rebuilding.
Collect files, valid inode nodes, deleted inode nodes, valid dentry
nodes and deleted dentry nodes in kinds of trees by scanning nodes from
flash. Corrupted nodes(eg. incorrect crc, bad inode size, bad dentry
name length, etc.) are dropped during scanning. Larger sqnum node is
picked when more than 1 nodes with same index.
In this step, trun node and data nodes are put into corresponding file,
inode/dentry nodes are put into four trees: valid_inos(nlink != 0),
del_inos(nlink is 0), valid_dents(inum != 0), del_dents(inum is 0).
Next step will process above four trees to deal deletion situations.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 12 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 15 ++
ubifs-utils/fsck.ubifs/rebuild_fs.c | 399 ++++++++++++++++++++++++++++++++++++
4 files changed, 426 insertions(+), 3 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/rebuild_fs.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index a0676a70..b7ee3de4 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -82,7 +82,8 @@ fsck_ubifs_SOURCES = \
ubifs-utils/fsck.ubifs/fsck.ubifs.c \
ubifs-utils/fsck.ubifs/problem.c \
ubifs-utils/fsck.ubifs/load_fs.c \
- ubifs-utils/fsck.ubifs/extract_files.c
+ ubifs-utils/fsck.ubifs/extract_files.c \
+ ubifs-utils/fsck.ubifs/rebuild_fs.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 9bc9c259..683d7c27 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -425,12 +425,20 @@ int main(int argc, char *argv[])
/* Init: Read superblock */
err = ubifs_load_filesystem(c);
- if (err)
+ if (err) {
+ if (FSCK(c)->try_rebuild)
+ ubifs_rebuild_filesystem(c);
goto out_close;
+ }
err = do_fsck();
+ if (err && FSCK(c)->try_rebuild) {
+ ubifs_destroy_filesystem(c);
+ ubifs_rebuild_filesystem(c);
+ } else {
+ ubifs_destroy_filesystem(c);
+ }
- ubifs_destroy_filesystem(c);
out_close:
ubifs_close_volume(c);
out_destroy_fsck:
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 80d3af84..c6c21e99 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -175,16 +175,28 @@ struct scanned_file {
};
/**
+ * ubifs_rebuild_info - UBIFS rebuilding information.
+ * @scanned_files: tree of all scanned files
+ */
+struct ubifs_rebuild_info {
+ struct rb_root scanned_files;
+};
+
+/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
* @failure_reason: reasons for failed operations
* @lpt_status: the status of lpt, could be: %0(OK), %FR_LPT_CORRUPTED or
* %FR_LPT_INCORRECT
+ * @try_rebuild: %true means that try to rebuild fs when fsck failed
+ * @rebuild: rebuilding-related information
*/
struct ubifs_fsck_info {
int mode;
unsigned int failure_reason;
unsigned int lpt_status;
+ bool try_rebuild;
+ struct ubifs_rebuild_info *rebuild;
};
#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
@@ -252,4 +264,7 @@ int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
void destroy_file_content(struct ubifs_info *c, struct scanned_file *file);
void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
+/* rebuild_fs.c */
+int ubifs_rebuild_filesystem(struct ubifs_info *c);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
new file mode 100644
index 00000000..3ca94869
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+/**
+ * scanned_info - nodes and files information from scanning.
+ * @valid_inos: the tree of scanned inode nodes with 'nlink > 0'
+ * @del_inos: the tree of scanned inode nodes with 'nlink = 0'
+ * @valid_dents: the tree of scanned dentry nodes with 'inum > 0'
+ * @del_dents: the tree of scanned dentry nodes with 'inum = 0'
+ */
+struct scanned_info {
+ struct rb_root valid_inos;
+ struct rb_root del_inos;
+ struct rb_root valid_dents;
+ struct rb_root del_dents;
+};
+
+static int init_rebuild_info(struct ubifs_info *c)
+{
+ int err;
+
+ c->sbuf = vmalloc(c->leb_size);
+ if (!c->sbuf) {
+ log_err(c, errno, "can not allocate sbuf");
+ return -ENOMEM;
+ }
+ FSCK(c)->rebuild = kzalloc(sizeof(struct ubifs_rebuild_info),
+ GFP_KERNEL);
+ if (!FSCK(c)->rebuild) {
+ err = -ENOMEM;
+ log_err(c, errno, "can not allocate rebuild info");
+ goto out;
+ }
+ FSCK(c)->rebuild->scanned_files = RB_ROOT;
+
+ return 0;
+
+out:
+ vfree(c->sbuf);
+ return err;
+}
+
+static void destroy_rebuild_info(struct ubifs_info *c)
+{
+ kfree(FSCK(c)->rebuild);
+ vfree(c->sbuf);
+}
+
+/**
+ * insert_or_update_ino_node - insert or update inode node.
+ * @c: UBIFS file-system description object
+ * @new_ino: new inode node
+ * @tree: a tree to record valid/deleted inode node info
+ *
+ * This function inserts @new_ino into the @tree, or updates inode node
+ * if it already exists in the tree. Returns zero in case of success, a
+ * negative error code in case of failure.
+ */
+static int insert_or_update_ino_node(struct ubifs_info *c,
+ struct scanned_ino_node *new_ino,
+ struct rb_root *tree)
+{
+ int cmp;
+ struct scanned_ino_node *ino_node, *old_ino_node = NULL;
+ struct rb_node **p, *parent = NULL;
+
+ p = &tree->rb_node;
+ while (*p) {
+ parent = *p;
+ ino_node = rb_entry(parent, struct scanned_ino_node, rb);
+ cmp = keys_cmp(c, &new_ino->key, &ino_node->key);
+ if (cmp < 0) {
+ p = &(*p)->rb_left;
+ } else if (cmp > 0) {
+ p = &(*p)->rb_right;
+ } else {
+ old_ino_node = ino_node;
+ break;
+ }
+ }
+ if (old_ino_node) {
+ if (old_ino_node->header.sqnum < new_ino->header.sqnum) {
+ size_t len = offsetof(struct scanned_ino_node, rb);
+
+ memcpy(old_ino_node, new_ino, len);
+ }
+ return 0;
+ }
+
+ ino_node = kmalloc(sizeof(struct scanned_ino_node), GFP_KERNEL);
+ if (!ino_node)
+ return -ENOMEM;
+
+ *ino_node = *new_ino;
+ rb_link_node(&ino_node->rb, parent, p);
+ rb_insert_color(&ino_node->rb, tree);
+
+ return 0;
+}
+
+/**
+ * insert_or_update_dent_node - insert or update dentry node.
+ * @c: UBIFS file-system description object
+ * @new_dent: new dentry node
+ * @tree: a tree to record valid/deleted dentry node info
+ *
+ * This function inserts @new_dent into the @tree, or updates dent node
+ * if it already exists in the tree. Returns zero in case of success, a
+ * negative error code in case of failure.
+ */
+static int insert_or_update_dent_node(struct ubifs_info *c,
+ struct scanned_dent_node *new_dent,
+ struct rb_root *tree)
+{
+ int cmp, nlen;
+ struct scanned_dent_node *dent_node, *old_dent_node = NULL;
+ struct rb_node **p, *parent = NULL;
+
+ p = &tree->rb_node;
+ while (*p) {
+ parent = *p;
+ dent_node = rb_entry(parent, struct scanned_dent_node, rb);
+ cmp = keys_cmp(c, &new_dent->key, &dent_node->key);
+ if (cmp < 0) {
+ p = &(*p)->rb_left;
+ } else if (cmp > 0) {
+ p = &(*p)->rb_right;
+ } else {
+ nlen = min(new_dent->nlen, dent_node->nlen);
+ cmp = strncmp(new_dent->name, dent_node->name, nlen) ? :
+ new_dent->nlen - dent_node->nlen;
+ if (cmp < 0) {
+ p = &(*p)->rb_left;
+ } else if (cmp > 0) {
+ p = &(*p)->rb_right;
+ } else {
+ old_dent_node = dent_node;
+ break;
+ }
+ }
+ }
+ if (old_dent_node) {
+ if (old_dent_node->header.sqnum < new_dent->header.sqnum) {
+ size_t len = offsetof(struct scanned_dent_node, rb);
+
+ memcpy(old_dent_node, new_dent, len);
+ }
+ return 0;
+ }
+
+ dent_node = kmalloc(sizeof(struct scanned_dent_node), GFP_KERNEL);
+ if (!dent_node)
+ return -ENOMEM;
+
+ *dent_node = *new_dent;
+ rb_link_node(&dent_node->rb, parent, p);
+ rb_insert_color(&dent_node->rb, tree);
+
+ return 0;
+}
+
+/**
+ * process_scanned_node - process scanned node.
+ * @c: UBIFS file-system description object
+ * @lnum: logical eraseblock number
+ * @snod: scanned node
+ * @si: records nodes and files information during scanning
+ *
+ * This function parses, checks and records scanned node information.
+ * Returns zero in case of success, 1% if the scanned LEB doesn't hold file
+ * data and should be ignored(eg. index LEB), a negative error code in case
+ * of failure.
+ */
+static int process_scanned_node(struct ubifs_info *c, int lnum,
+ struct ubifs_scan_node *snod,
+ struct scanned_info *si)
+{
+ ino_t inum;
+ int offs = snod->offs;
+ void *node = snod->node;
+ union ubifs_key *key = &snod->key;
+ struct rb_root *tree;
+ struct scanned_node *sn;
+ struct scanned_ino_node ino_node;
+ struct scanned_dent_node dent_node;
+ struct scanned_data_node data_node;
+ struct scanned_trun_node trun_node;
+
+ switch (snod->type) {
+ case UBIFS_INO_NODE:
+ {
+ if (!parse_ino_node(c, lnum, offs, node, key, &ino_node))
+ return 0;
+
+ tree = &si->del_inos;
+ if (ino_node.nlink)
+ tree = &si->valid_inos;
+ return insert_or_update_ino_node(c, &ino_node, tree);
+ }
+ case UBIFS_DENT_NODE:
+ case UBIFS_XENT_NODE:
+ {
+ if (!parse_dent_node(c, lnum, offs, node, key, &dent_node))
+ return 0;
+
+ tree = &si->del_dents;
+ if (dent_node.inum)
+ tree = &si->valid_dents;
+ return insert_or_update_dent_node(c, &dent_node, tree);
+ }
+ case UBIFS_DATA_NODE:
+ {
+ if (!parse_data_node(c, lnum, offs, node, key, &data_node))
+ return 0;
+
+ inum = key_inum(c, key);
+ sn = (struct scanned_node *)&data_node;
+ break;
+ }
+ case UBIFS_TRUN_NODE:
+ {
+ if (!parse_trun_node(c, lnum, offs, node, key, &trun_node))
+ return 0;
+
+ inum = le32_to_cpu(((struct ubifs_trun_node *)node)->inum);
+ sn = (struct scanned_node *)&trun_node;
+ break;
+ }
+ default:
+ dbg_fsck("skip node type %d, at %d:%d, in %s",
+ snod->type, lnum, offs, c->dev_name);
+ return 1;
+ }
+
+ tree = &FSCK(c)->rebuild->scanned_files;
+ return insert_or_update_file(c, tree, sn, key_type(c, key), inum);
+}
+
+/**
+ * destroy_scanned_info - destroy scanned nodes.
+ * @c: UBIFS file-system description object
+ * @si: records nodes and files information during scanning
+ *
+ * Destroy scanned files and all data/dentry nodes attached to file, destroy
+ * valid/deleted inode/dentry info.
+ */
+static void destroy_scanned_info(struct ubifs_info *c, struct scanned_info *si)
+{
+ struct scanned_ino_node *ino_node;
+ struct scanned_dent_node *dent_node;
+ struct rb_node *this;
+
+ destroy_file_tree(c, &FSCK(c)->rebuild->scanned_files);
+
+ this = rb_first(&si->valid_inos);
+ while (this) {
+ ino_node = rb_entry(this, struct scanned_ino_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&ino_node->rb, &si->valid_inos);
+ kfree(ino_node);
+ }
+
+ this = rb_first(&si->del_inos);
+ while (this) {
+ ino_node = rb_entry(this, struct scanned_ino_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&ino_node->rb, &si->del_inos);
+ kfree(ino_node);
+ }
+
+ this = rb_first(&si->valid_dents);
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&dent_node->rb, &si->valid_dents);
+ kfree(dent_node);
+ }
+
+ this = rb_first(&si->del_dents);
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ rb_erase(&dent_node->rb, &si->del_dents);
+ kfree(dent_node);
+ }
+}
+
+/**
+ * scan_nodes - scan node information from flash.
+ * @c: UBIFS file-system description object
+ * @si: records nodes and files information during scanning
+ *
+ * This function scans nodes from flash, all ino/dent nodes are split
+ * into valid tree and deleted tree, all trun/data nodes are collected
+ * into file, the file is inserted into @FSCK(c)->rebuild->scanned_files.
+ */
+static int scan_nodes(struct ubifs_info *c, struct scanned_info *si)
+{
+ int lnum, err = 0;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+
+ for (lnum = c->main_first; lnum < c->leb_cnt; ++lnum) {
+ dbg_fsck("scan nodes at LEB %d, in %s", lnum, c->dev_name);
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) != -EUCLEAN)
+ return PTR_ERR(sleb);
+
+ sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, -1);
+ if (IS_ERR(sleb)) {
+ if (PTR_ERR(sleb) != -EUCLEAN)
+ return PTR_ERR(sleb);
+
+ /* This LEB holds corrupted data, abandon it. */
+ continue;
+ }
+ }
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ if (snod->sqnum > c->max_sqnum)
+ c->max_sqnum = snod->sqnum;
+
+ err = process_scanned_node(c, lnum, snod, si);
+ if (err < 0) {
+ log_err(c, 0, "process node failed at LEB %d, err %d",
+ lnum, err);
+ ubifs_scan_destroy(sleb);
+ goto out;
+ } else if (err == 1) {
+ err = 0;
+ break;
+ }
+ }
+
+ ubifs_scan_destroy(sleb);
+ }
+
+out:
+ return err;
+}
+
+/**
+ * ubifs_rebuild_filesystem - Rebuild filesystem.
+ * @c: UBIFS file-system description object
+ *
+ * Scanning nodes from UBI volume and rebuild filesystem. Any inconsistent
+ * problems or corrupted data will be fixed.
+ */
+int ubifs_rebuild_filesystem(struct ubifs_info *c)
+{
+ int err = 0;
+ struct scanned_info si;
+
+ si.valid_inos = si.del_inos = si.valid_dents = si.del_dents = RB_ROOT;
+ log_out(c, "Start rebuilding filesystem (Notice: dropping data/recovering deleted data can't be awared)");
+ FSCK(c)->mode = REBUILD_MODE;
+
+ err = init_rebuild_info(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ return err;
+ }
+
+ /* Step 1: Scan valid/deleted nodes from volume. */
+ log_out(c, "Scan nodes");
+ err = scan_nodes(c, &si);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
+ destroy_scanned_info(c, &si);
+ destroy_rebuild_info(c);
+
+ return err;
+}
--
2.13.6
This is a preparation for adding bad images fsck testcase. There is
no debugfs tools (for example: debugfs[ext4], xfs_db) for UBIFS, so
there is no way to inject precise corruption into UBIFS image, we have
to prepare inconsistent UBIFS images in advance like e2fsprogs[1] does.
(Goto [2] to see how to generate inconsistent UBIFS images).
Original UBIFS image content:
/
├── corrupt_file (xattr - user.corrupt:123, 2K data)
├── dir
│ ├── block_dev
│ ├── char_dev
│ ├── dir
│ └── file (content: '123')
├── hardl_corrupt_file => corrupt_file
└── softl_corrupt_file -> corrupt_file
Here's a descriptons of the various testing images:
=========================================================================
image | Description | expectancy
-------------------------------------------------------------------------
good | good image contains | fsck success, fs content is
| kinds of files. | not changed.
-------------------------------------------------------------------------
sb_fanout | invalid fanout in | fsck failed.
| superblock. |
-------------------------------------------------------------------------
sb_fmt_version | invalid fmt_version | fsck failed.
| in superblock. |
-------------------------------------------------------------------------
sb_leb_size | invalid leb_size in | fsck failed.
| superblock. |
-------------------------------------------------------------------------
sb_log_lebs | invalid log lebs in | fsck failed.
| superblock. |
-------------------------------------------------------------------------
sb_min_io_size | invalid min_io_size | fsck failed.
| in superblock. |
-------------------------------------------------------------------------
master_highest_inum | invalid highest_inum| fsck success, fs content is
| in master nodes. | not changed.
-------------------------------------------------------------------------
master_lpt | bad lpt pos in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
master_tnc | bad tnc pos in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
master_total_dead | bad total_dead in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
master_total_dirty | bad total_dirty in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
master_total_free | bad total_free in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
journal_log | corrupted log area. | fsck success, fs content is
| | not changed.
-------------------------------------------------------------------------
journal_bud | corrupted bud area. | fsck success, file data is
| | lost.
-------------------------------------------------------------------------
orphan_node | bad orphan node. | fsck success, file is
| | deleted as expected.
-------------------------------------------------------------------------
lpt_dirty | bad dirty in pnode. | fsck success, fs content is
| | not changed.
-------------------------------------------------------------------------
lpt_flags | bad flags in pnode | fsck success, fs content is
| (eg. index). | not changed.
-------------------------------------------------------------------------
lpt_free | bad free in pnode. | fsck success, fs content is
| | not changed.
-------------------------------------------------------------------------
lpt_pos | bad pos in nnode. | fsck success, fs content is
| | not changed.
-------------------------------------------------------------------------
ltab_dirty | bad dirty in lprops | fsck success, fs content is
| table. | not changed.
-------------------------------------------------------------------------
ltab_free | bad free in lprops | fsck success, fs content is
| table. | not changed.
-------------------------------------------------------------------------
index_size | bad index size in | fsck success, fs content is
| master nodes. | not changed.
-------------------------------------------------------------------------
tnc_lv0_key | bad key in lv0 | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
tnc_lv0_len | bad len in lv0 | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
tnc_lv0_pos | bad pos in lv0 | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
tnc_noleaf_key | bad key in non-leaf | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
tnc_noleaf_len | bad len in non-leaf | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
tnc_noleaf_pos | bad pos in non-leaf | fsck success, fs content is
| znode. | not changed.
-------------------------------------------------------------------------
corrupted_data_leb | corrupted data leb. | fsck success, partial data of
| | file is lost.
-------------------------------------------------------------------------
corrupted_idx_leb | corrupted index leb.| fsck success, fs content is
| | not changed.
-------------------------------------------------------------------------
inode_data | bad data node. | fsck success, file content
| | is changed, other files are
| | not changed.
-------------------------------------------------------------------------
inode_mode | bad inode mode for | fsck success, file is
| file. | dropped, other files are not
| | changed.
-------------------------------------------------------------------------
inode_nlink | wrong nlink for | fsck success, nlink is
| file. | corrected, fs content is not
| | changed.
-------------------------------------------------------------------------
inode_size | wrong inode size | fsck success, inode size is
| for file. | corrected, fs content is not
| | changed.
-------------------------------------------------------------------------
inode_xcnt | wrong inode | fsck success, xattr_cnt is
| xattr_cnt for file. | corrected, fs content is not
| | changed.
-------------------------------------------------------------------------
soft_link_inode_mode| bad inode mode for | fsck success, soft link
| solf link file. | file is dropped, other files
| | are not changed.
-------------------------------------------------------------------------
soft_link_data_len | bad inode data_len | fsck success, soft link
| for solt link file. | file is dropped, other files
| | are not changed.
-------------------------------------------------------------------------
dentry_key | bad dentry key for | fsck success, dentry is
| file. | removed, other files are
| | not changed.
-------------------------------------------------------------------------
dentry_nlen | inconsistent nlen | fsck success, dentry is
| and name in dentry | removed, other files are
| for file. | not changed.
-------------------------------------------------------------------------
dentry_type | inconsistent type | fsck success, dentry is
| between dentry and | removed, other files are
| inode for file. | not changed.
-------------------------------------------------------------------------
xinode_flags | lost UBIFS_XATTR_FL | fsck success, xattr is
| in xattr inode | removed, other files are
| flags for file. | not changed.
-------------------------------------------------------------------------
xinode_key | bad xattr inode key | fsck success, xattr is
| for file. | removed, other files are
| | not changed.
-------------------------------------------------------------------------
xinode_mode | bad xattr inode | fsck success, xattr is
| mode for file. | removed, other files are
| | not changed.
-------------------------------------------------------------------------
xentry_key | bad xattr entry key | fsck success, xattr is
| for file. | removed, other files are
| | not changed.
-------------------------------------------------------------------------
xentry_nlen | inconsistent nlen | fsck success, xattr is
| and name in xattr | removed, other files are
| entry for file. | not changed.
-------------------------------------------------------------------------
xentry_type | inconsistent type | fsck success, xattr is
| between xattr entry | removed, other files are
| and xattr inode for | not changed.
| file. |
-------------------------------------------------------------------------
xent_host | the xattr's host | fsck success, file, hard
| is a xattr too, the | link and soft link are
| flag of corrupt_file| dropped, other files are
| inode is modified. | not changed.
-------------------------------------------------------------------------
dir_many_dentry | dir has too many | fsck success, hard link is
| dentries, the dentry| dropped, other files are not
| of hard link is | changed.
| modified. |
-------------------------------------------------------------------------
dir_lost | bad dentry for dir. | fsck success, the 'file' is
| | recovered under lost+found,
| | left files under dir are
| | removed, other files are not
| | changed.
-------------------------------------------------------------------------
dir_lost_duplicated | bad inode for dir, | fsck success, the 'file' is
| there is a file | recovered with INO_<inum>_1
| under lost+found, | under lost+found, left files
| which named with the| under dir are removed, other
| inum of the 'file'. | files are not changed.
-------------------------------------------------------------------------
dir_lost_not_recover| bad inode for dir, | fsck success, all files
| lost+found is a | under dir are removed,
| regular file and | other files are not changed.
| exists under root |
| dir. |
-------------------------------------------------------------------------
root_dir | bad '/'. | fsck success, create new
| | root dir('/'). All regular
| | files are reocovered under
| | lost+found, other files are
| | removed.
-------------------------------------------------------------------------
empty_tnc | all files have bad | fsck success, fs content
| inode. | becomes empty.
=========================================================================
[1] https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/tests/README
[2] https://bugzilla.kernel.org/show_bug.cgi?id=218924
Signed-off-by: Zhihao Cheng <[email protected]>
---
Makefile.am | 1 +
tests/ubifs_tools-tests/Makemodule.am | 55 +++++++++++++++++++++
.../ubifs_tools-tests/images/corrupted_data_leb.gz | Bin 0 -> 9536 bytes
.../ubifs_tools-tests/images/corrupted_idx_leb.gz | Bin 0 -> 5082 bytes
tests/ubifs_tools-tests/images/dentry_key.gz | Bin 0 -> 5088 bytes
tests/ubifs_tools-tests/images/dentry_nlen.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/dentry_type.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/dir_lost.gz | Bin 0 -> 5088 bytes
.../images/dir_lost_duplicated.gz | Bin 0 -> 5347 bytes
.../images/dir_lost_not_recover.gz | Bin 0 -> 5396 bytes
tests/ubifs_tools-tests/images/dir_many_dentry.gz | Bin 0 -> 5114 bytes
tests/ubifs_tools-tests/images/empty_tnc.gz | Bin 0 -> 4954 bytes
tests/ubifs_tools-tests/images/good.gz | Bin 0 -> 4960 bytes
tests/ubifs_tools-tests/images/index_size.gz | Bin 0 -> 5070 bytes
tests/ubifs_tools-tests/images/inode_data.gz | Bin 0 -> 5015 bytes
tests/ubifs_tools-tests/images/inode_mode.gz | Bin 0 -> 5109 bytes
tests/ubifs_tools-tests/images/inode_nlink.gz | Bin 0 -> 5110 bytes
tests/ubifs_tools-tests/images/inode_size.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/inode_xcnt.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/journal_bud.gz | Bin 0 -> 5015 bytes
tests/ubifs_tools-tests/images/journal_log.gz | Bin 0 -> 4927 bytes
tests/ubifs_tools-tests/images/lpt_dirty.gz | Bin 0 -> 5056 bytes
tests/ubifs_tools-tests/images/lpt_flags.gz | Bin 0 -> 5060 bytes
tests/ubifs_tools-tests/images/lpt_free.gz | Bin 0 -> 5046 bytes
tests/ubifs_tools-tests/images/lpt_pos.gz | Bin 0 -> 5070 bytes
tests/ubifs_tools-tests/images/ltab_dirty.gz | Bin 0 -> 5104 bytes
tests/ubifs_tools-tests/images/ltab_free.gz | Bin 0 -> 5072 bytes
.../images/master_highest_inum.gz | Bin 0 -> 4813 bytes
tests/ubifs_tools-tests/images/master_lpt.gz | Bin 0 -> 4808 bytes
tests/ubifs_tools-tests/images/master_tnc.gz | Bin 0 -> 4805 bytes
.../ubifs_tools-tests/images/master_total_dead.gz | Bin 0 -> 4817 bytes
.../ubifs_tools-tests/images/master_total_dirty.gz | Bin 0 -> 4814 bytes
.../ubifs_tools-tests/images/master_total_free.gz | Bin 0 -> 4813 bytes
tests/ubifs_tools-tests/images/orphan_node.gz | Bin 0 -> 5379 bytes
tests/ubifs_tools-tests/images/root_dir.gz | Bin 0 -> 5058 bytes
tests/ubifs_tools-tests/images/sb_fanout.gz | Bin 0 -> 5031 bytes
tests/ubifs_tools-tests/images/sb_fmt_version.gz | Bin 0 -> 5032 bytes
tests/ubifs_tools-tests/images/sb_leb_size.gz | Bin 0 -> 5033 bytes
tests/ubifs_tools-tests/images/sb_log_lebs.gz | Bin 0 -> 5031 bytes
tests/ubifs_tools-tests/images/sb_min_io_size.gz | Bin 0 -> 5035 bytes
.../ubifs_tools-tests/images/soft_link_data_len.gz | Bin 0 -> 5112 bytes
.../images/soft_link_inode_mode.gz | Bin 0 -> 5121 bytes
tests/ubifs_tools-tests/images/tnc_lv0_key.gz | Bin 0 -> 5118 bytes
tests/ubifs_tools-tests/images/tnc_lv0_len.gz | Bin 0 -> 5130 bytes
tests/ubifs_tools-tests/images/tnc_lv0_pos.gz | Bin 0 -> 5118 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_key.gz | Bin 0 -> 5140 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_len.gz | Bin 0 -> 5145 bytes
tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz | Bin 0 -> 5125 bytes
tests/ubifs_tools-tests/images/xent_host.gz | Bin 0 -> 5108 bytes
tests/ubifs_tools-tests/images/xentry_key.gz | Bin 0 -> 5085 bytes
tests/ubifs_tools-tests/images/xentry_nlen.gz | Bin 0 -> 5115 bytes
tests/ubifs_tools-tests/images/xentry_type.gz | Bin 0 -> 5113 bytes
tests/ubifs_tools-tests/images/xinode_flags.gz | Bin 0 -> 5112 bytes
tests/ubifs_tools-tests/images/xinode_key.gz | Bin 0 -> 5110 bytes
tests/ubifs_tools-tests/images/xinode_mode.gz | Bin 0 -> 5112 bytes
55 files changed, 56 insertions(+)
create mode 100644 tests/ubifs_tools-tests/images/corrupted_data_leb.gz
create mode 100644 tests/ubifs_tools-tests/images/corrupted_idx_leb.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_key.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_nlen.gz
create mode 100644 tests/ubifs_tools-tests/images/dentry_type.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost_duplicated.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_lost_not_recover.gz
create mode 100644 tests/ubifs_tools-tests/images/dir_many_dentry.gz
create mode 100644 tests/ubifs_tools-tests/images/empty_tnc.gz
create mode 100644 tests/ubifs_tools-tests/images/good.gz
create mode 100644 tests/ubifs_tools-tests/images/index_size.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_data.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_mode.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_nlink.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_size.gz
create mode 100644 tests/ubifs_tools-tests/images/inode_xcnt.gz
create mode 100644 tests/ubifs_tools-tests/images/journal_bud.gz
create mode 100644 tests/ubifs_tools-tests/images/journal_log.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_flags.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_free.gz
create mode 100644 tests/ubifs_tools-tests/images/lpt_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/ltab_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/ltab_free.gz
create mode 100644 tests/ubifs_tools-tests/images/master_highest_inum.gz
create mode 100644 tests/ubifs_tools-tests/images/master_lpt.gz
create mode 100644 tests/ubifs_tools-tests/images/master_tnc.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_dead.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_dirty.gz
create mode 100644 tests/ubifs_tools-tests/images/master_total_free.gz
create mode 100644 tests/ubifs_tools-tests/images/orphan_node.gz
create mode 100644 tests/ubifs_tools-tests/images/root_dir.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_fanout.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_fmt_version.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_leb_size.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_log_lebs.gz
create mode 100644 tests/ubifs_tools-tests/images/sb_min_io_size.gz
create mode 100644 tests/ubifs_tools-tests/images/soft_link_data_len.gz
create mode 100644 tests/ubifs_tools-tests/images/soft_link_inode_mode.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_key.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_len.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_lv0_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_key.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_len.gz
create mode 100644 tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz
create mode 100644 tests/ubifs_tools-tests/images/xent_host.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_key.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_nlen.gz
create mode 100644 tests/ubifs_tools-tests/images/xentry_type.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_flags.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_key.gz
create mode 100644 tests/ubifs_tools-tests/images/xinode_mode.gz
diff --git a/Makefile.am b/Makefile.am
index 0ebd45bb..c7561272 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,7 @@ dist_man8_MANS =
testdir = @TESTBINDIR@
test_PROGRAMS =
test_SCRIPTS =
+test_DATA =
TESTS =
EXTRA_DIST = COPYING CHANGELOG.md README.txt
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 9881ebbe..7ef873ce 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -6,3 +6,58 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh \
tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
+
+test_DATA += \
+ tests/ubifs_tools-tests/images/good.gz \
+ tests/ubifs_tools-tests/images/sb_fanout.gz \
+ tests/ubifs_tools-tests/images/sb_fmt_version.gz \
+ tests/ubifs_tools-tests/images/sb_leb_size.gz \
+ tests/ubifs_tools-tests/images/sb_log_lebs.gz \
+ tests/ubifs_tools-tests/images/sb_min_io_size.gz \
+ tests/ubifs_tools-tests/images/master_highest_inum.gz \
+ tests/ubifs_tools-tests/images/master_lpt.gz \
+ tests/ubifs_tools-tests/images/master_tnc.gz \
+ tests/ubifs_tools-tests/images/master_total_dead.gz \
+ tests/ubifs_tools-tests/images/master_total_dirty.gz \
+ tests/ubifs_tools-tests/images/master_total_free.gz \
+ tests/ubifs_tools-tests/images/journal_log.gz \
+ tests/ubifs_tools-tests/images/journal_bud.gz \
+ tests/ubifs_tools-tests/images/orphan_node.gz \
+ tests/ubifs_tools-tests/images/lpt_dirty.gz \
+ tests/ubifs_tools-tests/images/lpt_flags.gz \
+ tests/ubifs_tools-tests/images/lpt_free.gz \
+ tests/ubifs_tools-tests/images/lpt_pos.gz \
+ tests/ubifs_tools-tests/images/ltab_dirty.gz \
+ tests/ubifs_tools-tests/images/ltab_free.gz \
+ tests/ubifs_tools-tests/images/index_size.gz \
+ tests/ubifs_tools-tests/images/tnc_lv0_key.gz \
+ tests/ubifs_tools-tests/images/tnc_lv0_len.gz \
+ tests/ubifs_tools-tests/images/tnc_lv0_pos.gz \
+ tests/ubifs_tools-tests/images/tnc_noleaf_key.gz \
+ tests/ubifs_tools-tests/images/tnc_noleaf_len.gz \
+ tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz \
+ tests/ubifs_tools-tests/images/corrupted_data_leb.gz \
+ tests/ubifs_tools-tests/images/corrupted_idx_leb.gz \
+ tests/ubifs_tools-tests/images/inode_data.gz \
+ tests/ubifs_tools-tests/images/inode_mode.gz \
+ tests/ubifs_tools-tests/images/inode_nlink.gz \
+ tests/ubifs_tools-tests/images/inode_size.gz \
+ tests/ubifs_tools-tests/images/inode_xcnt.gz \
+ tests/ubifs_tools-tests/images/soft_link_inode_mode.gz \
+ tests/ubifs_tools-tests/images/soft_link_data_len.gz \
+ tests/ubifs_tools-tests/images/dentry_key.gz \
+ tests/ubifs_tools-tests/images/dentry_nlen.gz \
+ tests/ubifs_tools-tests/images/dentry_type.gz \
+ tests/ubifs_tools-tests/images/xinode_flags.gz \
+ tests/ubifs_tools-tests/images/xinode_key.gz \
+ tests/ubifs_tools-tests/images/xinode_mode.gz \
+ tests/ubifs_tools-tests/images/xentry_key.gz \
+ tests/ubifs_tools-tests/images/xentry_nlen.gz \
+ tests/ubifs_tools-tests/images/xentry_type.gz \
+ tests/ubifs_tools-tests/images/xent_host.gz \
+ tests/ubifs_tools-tests/images/dir_many_dentry.gz \
+ tests/ubifs_tools-tests/images/dir_lost.gz \
+ tests/ubifs_tools-tests/images/dir_lost_duplicated.gz \
+ tests/ubifs_tools-tests/images/dir_lost_not_recover.gz \
+ tests/ubifs_tools-tests/images/root_dir.gz \
+ tests/ubifs_tools-tests/images/empty_tnc.gz
diff --git a/tests/ubifs_tools-tests/images/corrupted_data_leb.gz b/tests/ubifs_tools-tests/images/corrupted_data_leb.gz
new file mode 100644
index 0000000000000000000000000000000000000000..ebb98a4fefb664a2505fece1c12ab1faf104632d
GIT binary patch
literal 9536
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/corrupted_idx_leb.gz b/tests/ubifs_tools-tests/images/corrupted_idx_leb.gz
new file mode 100644
index 0000000000000000000000000000000000000000..446f8118af32366765ded1f51c6070348171ce64
GIT binary patch
literal 5082
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
B1>XPw
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dentry_key.gz b/tests/ubifs_tools-tests/images/dentry_key.gz
new file mode 100644
index 0000000000000000000000000000000000000000..bc313b5cddc698648b1ad29abc3deb711083c5a0
GIT binary patch
literal 5088
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dentry_nlen.gz b/tests/ubifs_tools-tests/images/dentry_nlen.gz
new file mode 100644
index 0000000000000000000000000000000000000000..4e5c826e78bc9dd526312337f6573badf15d139c
GIT binary patch
literal 5113
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
U|NG{sgaI>HCO+G<=ir`y0q^GjmH+?%
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dentry_type.gz b/tests/ubifs_tools-tests/images/dentry_type.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e67348da01f38aceab7e7ab952a8433a8671b542
GIT binary patch
literal 5115
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dir_lost.gz b/tests/ubifs_tools-tests/images/dir_lost.gz
new file mode 100644
index 0000000000000000000000000000000000000000..fd42040d2e23a4c8f65146ea1b9d81a1eb1732c1
GIT binary patch
literal 5088
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
B0pkDw
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dir_lost_duplicated.gz b/tests/ubifs_tools-tests/images/dir_lost_duplicated.gz
new file mode 100644
index 0000000000000000000000000000000000000000..09def92c3cf51b942c0c52f4023e5f1c9fc9a04a
GIT binary patch
literal 5347
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dir_lost_not_recover.gz b/tests/ubifs_tools-tests/images/dir_lost_not_recover.gz
new file mode 100644
index 0000000000000000000000000000000000000000..6fe82bb449fb91a14c169121554cd4defa5dc905
GIT binary patch
literal 5396
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�iBx
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
J`+n!y_kX|gY;*ts
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/dir_many_dentry.gz b/tests/ubifs_tools-tests/images/dir_many_dentry.gz
new file mode 100644
index 0000000000000000000000000000000000000000..f027bfc4c7b8125fff294deb0a31b3371a891188
GIT binary patch
literal 5114
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
Wu?l7h{skIlTv4XUkd7T=KlXo(m?DY*
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/empty_tnc.gz b/tests/ubifs_tools-tests/images/empty_tnc.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7e85a8c977d7998f39fc594d5dd867bfb5b6820e
GIT binary patch
literal 4954
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/good.gz b/tests/ubifs_tools-tests/images/good.gz
new file mode 100644
index 0000000000000000000000000000000000000000..686e949f4eaca715e5edcd24c2453c9e3fdfda5e
GIT binary patch
literal 4960
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/index_size.gz b/tests/ubifs_tools-tests/images/index_size.gz
new file mode 100644
index 0000000000000000000000000000000000000000..0ebcf59bee8ef0eef7d61d78dbe111fa827c0569
GIT binary patch
literal 5070
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
Gk^cbA$Ono5
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/inode_data.gz b/tests/ubifs_tools-tests/images/inode_data.gz
new file mode 100644
index 0000000000000000000000000000000000000000..f8135dcab14fe393e78083d762e1b873eb8982fe
GIT binary patch
literal 5015
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
L&e*YI&yIfqs$s`9
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/inode_mode.gz b/tests/ubifs_tools-tests/images/inode_mode.gz
new file mode 100644
index 0000000000000000000000000000000000000000..1bdbb264c2d29e538abcb0c5380ac9e0fad79508
GIT binary patch
literal 5109
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/inode_nlink.gz b/tests/ubifs_tools-tests/images/inode_nlink.gz
new file mode 100644
index 0000000000000000000000000000000000000000..19461d944818cb8849c372624d9f13d43b8105a5
GIT binary patch
literal 5110
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
V?*iYk-n845L`FQ^x9`xt{{SFD9tZ#c
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/inode_size.gz b/tests/ubifs_tools-tests/images/inode_size.gz
new file mode 100644
index 0000000000000000000000000000000000000000..aa7574cbf40a7ad1b6b536b438122926ccf6d398
GIT binary patch
literal 5113
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
N;gbB)zI_Mx{R?;16*vF@
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/inode_xcnt.gz b/tests/ubifs_tools-tests/images/inode_xcnt.gz
new file mode 100644
index 0000000000000000000000000000000000000000..4bc5ac23fc822d93fad2510f157b86e57756e5b9
GIT binary patch
literal 5115
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
OI%CGDKXmAiL;nYjAQ_4P
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/journal_bud.gz b/tests/ubifs_tools-tests/images/journal_bud.gz
new file mode 100644
index 0000000000000000000000000000000000000000..37cf4538471f7f5c7a78df3137e67aad842e1afb
GIT binary patch
literal 5015
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/journal_log.gz b/tests/ubifs_tools-tests/images/journal_log.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e1ddc88745d15a9801cde30a907c1211267ad2e6
GIT binary patch
literal 4927
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
LbHRZFM-KcqiU!At
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/lpt_dirty.gz b/tests/ubifs_tools-tests/images/lpt_dirty.gz
new file mode 100644
index 0000000000000000000000000000000000000000..b7381c35a72e62564e4dc8f9f6c0f234929d9216
GIT binary patch
literal 5056
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/lpt_flags.gz b/tests/ubifs_tools-tests/images/lpt_flags.gz
new file mode 100644
index 0000000000000000000000000000000000000000..baf60973814dcea453a007a3529f53fcc2143a2a
GIT binary patch
literal 5060
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/lpt_free.gz b/tests/ubifs_tools-tests/images/lpt_free.gz
new file mode 100644
index 0000000000000000000000000000000000000000..abc46fd241a9ca0ed4d1404d6e4a8c75540733ef
GIT binary patch
literal 5046
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/lpt_pos.gz b/tests/ubifs_tools-tests/images/lpt_pos.gz
new file mode 100644
index 0000000000000000000000000000000000000000..d050f18edb9d037a387ed78838f8da51cb8c2cd0
GIT binary patch
literal 5070
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/ltab_dirty.gz b/tests/ubifs_tools-tests/images/ltab_dirty.gz
new file mode 100644
index 0000000000000000000000000000000000000000..56dabee0f44e1323090fd5af8fb30f6de493ed8d
GIT binary patch
literal 5104
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/ltab_free.gz b/tests/ubifs_tools-tests/images/ltab_free.gz
new file mode 100644
index 0000000000000000000000000000000000000000..985e275e7b7060475c4bf8a3eea0a9345adbfdaa
GIT binary patch
literal 5072
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
F{{?aH3D^Jt
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_highest_inum.gz b/tests/ubifs_tools-tests/images/master_highest_inum.gz
new file mode 100644
index 0000000000000000000000000000000000000000..08ed04450cc762d26e4aef53c748bad64a57f1d1
GIT binary patch
literal 4813
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_lpt.gz b/tests/ubifs_tools-tests/images/master_lpt.gz
new file mode 100644
index 0000000000000000000000000000000000000000..4b205a2654599764122614cba6a16ca902e06702
GIT binary patch
literal 4808
zcmb_gX*3&J*KX^oqG-7&s;a%_x#pp?v{jUv$Kt9n#+s*)s{=LHTohGBjX}&Ip^C;>
zQz9i&Q&NctVvZ!=`+e`P_ujt$zVmCZ{p_=zea_nJoM$~EsTVF_do;YxF$K7VhIxj#
z`acUhvx#ASVtULN-iZwYSXhWUe5m)Vs}~KhbkP5#B3jA);Wf9uDAC?{#4*2_tGp;T
z{KMwxYgtkEf~@!(v3gyHxQUXTVa1ssdM1jp(;2FbkDt^eQ;th#dRKRj!b$?Vk^F2U
zua|DuFWs&x(&F`F(_OY7DM{l#^Xs)e_ils1@@wq<B4VGx4*$qt0PNqf^ZxehsvpED
z2G_5lJI56akzYf9?H;z#802>KOKhVD=684f#hja0ldiiLno55S1Q~)_pAjZrBAz?(
zJ!cAMj?QCd`pj(go0S|ROsb{aT+!z4`+9{-N7R&*Toc#JxmOx8B^S5ph9JKjBR25D
zT(r8DlYwGuKxezh9+e_1C@tN@pPx~O9jUeV&TKx01{VrG*9I)Nc@Cfbb$901V5SWC
zk5DOu{5zt0L`F?xl_jqHdSq5uB`)c%bhaS?1#7=^`nLP=Y0<;oOUBovv-c`Nf8M!^
zyTU>+j@M|fugK89TV<Mgt#AW|NM&I=;bo<><mO)ZKe{7OCMe+_%s*ye!3;P-Ld_!I
zdA?bhY71a6g6(p+0h961A6;_L??UmmkMA5qyd7>7mVqGjRb4$p01TUbE5bTgHc|=$
zHtzvHV0jwzaM+<Bpj_TQ%x)I=u<xzX>bc>VBTmX;))syxRoigRW70qpTJkn!aTout
zdNaC#opBT`4P4R5oDIKLCtv({ZzsCbj+3GY%Px);D~vXOGfO5(mGn;stKFiQ?-)%D
z1mD$lcYG*r&|Q{6S;E=or#Z@KN(ptX0=~{|n&8XVFgia$A8`!4POHC71WYRLbA{?U
zZk62QVq7LtU_+ikjan^)3$^|+it*e^ji+RG(sy9r_}(_qCdMa2(>mZurx?*CY;uBK
zhA)HTFzKgI33=na(2F`Cj^-wQiYI51LMO4G4kReedcz&yuqXJI7ryMaX;-G@B>N&a
zhjFV-WhMz39!ItaM0mb-piSWkt&-}|7Ue)=pFEt5himN}1X|OS@!qzYW0}i9PhQMq
zZ<!7Km`Gg&kZL&o7{%Q}O<tTkZ#x~_?)l34k_xqk=tFjMA89{Kxj<_xU!?iTM7}#&
zKhiDj@zy6jD2tA}di<Pduq?DLQ(JP`Thb2X)O8Y>RRj94Kp=Y%LFFDum7D*Y(KcHR
zsR#9ArkIMq&N19khNVBsz>Ewts%bHzC&-BC+Au$Crq#H~a-X+<Aum;eGx{(Qw@l)Z
zg_eDmh<l(?u#}k{-ISy4Vzm)SCs5lHq8xVEC#@uNLP_!_Gkj&5B8ltI^je`UEjh_!
zc&!cKm|y&Yv(^M`^L_2}<=ZO@F~=+y1w@raE2*qpoVK^}in(IDRs}#LU=+RW_!6t$
z+Qkg*H*>qn_?t~vaTS?GCQtnkArghh51)uQAE@EX+h2XSt^nELL?7bc>fU~`@Rb%H
z@<!XNF65L^h8wNs^rIH;KbB28$rQ@}Ib$Y|9VS#CaiKCnM*p04G9{hLu+eQskX?di
zCVe~st_FjS#QjPdn|QX~jqAC`?&nxT;!JRUrNgsXvQ(2HDdSS%+;}|~o0d5K{-jdc
z;i~lOSdU6tb0yJ|SGPXrK(XMO0{nA4r8}EunzspgzII%?7BzkT3IGI8Hb!gFrh^l2
z1fK`MV>Y_)WOXtYABg)sVGafdco$OU_9Vwp1sibdIP8fi(Df0I5y#70%w})aa=oO-
zUAslFR<Vvnpf#OSYr1`h!^n#6Jpk8F)a-5?C?q%rY$j6J5#(S9!0?gERKBj<9L@n#
zsFa33CjOLxnBBK|!sJva=s$buP|ql~%vx;NN#{XxcBRKcdNwJqlG-RaAxn~wb*`?H
z|5pHrbv2peQqA|$u(DX}@REQ8Hg!jp^p0r_jI0C*d-&Qr!jU4#jRM@oP#?_!0$2ED
z*T?btJ8hz|w=p~^!w#eML<MdzQ5LfG`XO+s<#x_#BJ(ewI9+VV27}-Bt)->;?lP~*
z25O#dY+!**o431AY8>OZOHb_=BMp)1R-zhx*a53;IR1V?kKA)mvVKY0We+hK2ZXV0
zOLkuU|5u2FOA!C{)3~<OWgakOMgqYbVPdX#7<z}I|KCiuE%ekDx&e)Gqo03|A!5>%
zV{z`6S9InKOpwO-FHo-D3F99_{qI^yPza6jSz&_#66C}02F5ZiCkcOwd&;wqrP2O+
zP-0t1@RgqHeB%PI@+e78(RkSwWdZkZN;xuG9(ulNv7uWV-hSGN91h6E7&V-?0mWa{
z@eSHEA~G|{Pt5o1E-g0~n<*fAXWiWB&02_J8$-0Ji2Y5dI|Z+|KWWnneYPI;`tD;Y
z2hzdMfESwN=|Q+14n+4wq<cC#<=NF5cpvd`7=dr$6T)-SV3*sSdM0(g3pQ_^ZY9oq
z2UNXN4CT5u6?;-&T+HqalF_WBOvin0I5TG>7&@kt@1k1Ue0$kwsIKPFcd2;3-L}!V
zt$uH)u4H8{pKLEoMh^tFTD`EP&JLggri)2lD&taA&jf@*&>l<lL~DvAP`S(dM=pYE
z&P+T#IjvAg)t8c-h%gRceNci%H6CYHcc(?#C`4iA86)S0H*Zr_30+Ytb0kzid}ndZ
zZo|e&kQ#OZ5cRAmCd(K2IkoB-iJtu6#L&>J=(`2<$tVD{HFnf$Pat%406C4H5OY!o
z;nPFIxK84P&a9cTzt_%M=m-XXs&eeJFm_VVP@smr;<Eb(0hfa<B50O<>-GO}E9R(_
zb^Jxn=ae){r=M!3Q-^Qo$9#byz>Kc(XAEC&iJ+zYqK3wWuMJk#FJzoPa58%Kn-~>J
zHf`-fCE9MWKKPzb%u@=^hMEHQhxOc$_Ppr0x||Aa05wWWZlpoL38QM;T!>s&GEzcW
zgX<oRgkMm2X50gx`qrMYGPj|H5VJjNHNY>8dep!Tftc1sqnmxrm}^l%dMbKb_?lS7
zg?n+x=z`f%!@Z8dkLfX?QBA|!i|zDAaZ0H!Z2ojN0n))IP%X`=P)`C|fg-*KDx^}*
zW^7^br6wcKdUTXyBTKWED+1#Str{^{9k~kZSi1ABS8GGDKnQ4`BI+(9_#2eVgq)kJ
zhFbXZ)lhp<eySOCUI-bj<<xc9LMd&VL_acuB!|`d$44Etz3jJWAE?5DPl=<YNwcey
z=fgh<e2fk2<KADex+*jHTjfyiR@aCY<{;dp`jtXvS*mYL?Qp&WwHgetn8q6&LaQN-
zJ{O)qIqbA}dyTvecp8RH^2MNma^Kj|a)P{}kZB~^phTU4#7B7t@lbKhIA?HU+?y%n
z*zuzVzc?8}kH8O&K}C|+_HJ#tFgUs<EwD^+y4m2{*jb(=e2^4kY`1XsL!<L2$Ob)2
zJzd(TXhTv0%^apVZ&$OF?GVA0;nj$a4SQPgy&TbD<<G<0QS8Y1(^F^LbX}y_#a0CX
z!p~E3AxuvVX4To`G7eBX;o2+IweRP!gC*>O!aCf(&`2zDrT<vyqU-oWKz8??aQ7Uv
zp<1*xY$GVQ6{?}4*teOr(%IuL!(Wkzo+ImRoc_5maDTt`>Z}&Y6ew;Dgf-apr8JDn
zhV+UbWK!y=vX;<-ax)ZFS_zVejR1BjNkBV(OiQ`a-hb=+oPUEfoW;WdjyM8X-X9h0
z67}X3Q-}gO?JZh8-i9O+bM^v9Rf+|0zE>|0A|WPUm*@iLSxodN?32)&^*d|G@pKpz
z?rMF1a3Ut4c>W!S!Oim|$lCykk%cC3Q<FIvs9c=SmF6eB80ouY+}k*wD?`%K4f?j=
zlv%yk{&nor_n&4<>1p>0W-3ZsUAHl>svTYsTvSpS{nS5`LWg)~DW}di%)TJ6i37{C
zcuG}?7MW++j7tf)5AWYRTur~3!tXLq)P)9Yj<BAkO$d4b_PKhewQ;cvyfeG<v4hU4
zN@!l0w1~JEYWDEQd@Xht5E;~FW>prfJ^%&Rg|ux@MwGd6+FiCC8rmxH>GxA9N=&!4
zIa7b?K0cC`Tt-fK?P#kMIEU6vX?b<R?pxkVMW|8quN4OXoTfe)W#~Jves*W<9OwXW
zc{37=HS=S5>Z^>cn}p4lbY<}GwcICvV&1*#Js-od6jPPmz2#i8QpsB3TTSkmR}P-e
zTEraB?6!b>$ZD`YmAg&e<00TIg}t!WYhwGsveM|ti1mF_s5Z-ks?%HJfwZX`9fM-T
zM5@E)MWK4h?~ogxb|7?RPEM_bn3<Ndndg8eRabpxgJMo!hO2X`ZG05BEW8{N(`^Gx
zN+298RSyfBW7=YKo-y9Jwy)_3mcSSR<149PQZI+^+-AU4cb*Dfysm81eg?0bMEZl+
z7}G`hD-L3Drr@7;n!{e039W+_^EJ!1)<(6g{nJvET2R5lkneG+06_4vQE!+(^TYtb
zb#n6CNV4U)W1J3Lu_JQ81|i;f`9V4R?;Pg}&L{T5EHA+#ck@}<8nD01q1Zcdo<il9
zFBNPQ0cx_gc(J;wpWe@P4(?au+9#29{LRvvUokumH9=NM=~%aF9jW(7{W^f(mOU}N
z1oH*@jePtv&d*t7Y+5Z{d)9j{id-}`S|Q;r#taX0dV*<%FTbff^nC(ye%rSEaO5p|
zFx3&wqOQ>?qe10QC*pH+xU^wc-FE#CR5Ne35maw0(T`O37eb+`u4T3}U))h_NsL8S
zw4Le3;LDwa%f9`bPK0Y|T|(gQ-sbeo)#DHKF2U^8OMn=;`w&RNfeeaIFXEds-~3F=
zK7u2^_no)%?}LG5x}9?rT{ff=Tr16(!|0t_%eV%qFZ@`hmJgXcuRP-O#f=*4J0$EI
zWK3Gb1J}m$IA1ivd-jOAuqu?QJDJrLJt1qh-F4{G;AYSf57;uP(2~H3ES(HlhmSyn
z3m$kUj;m+Le|Mz1jaSq(_SzxKbj|e^1_$N79&Vpjc9aM!i`$f-4xaaSixP?yZoXnD
z)d3=J{_M-c*f2SjTBbO2SHXxUtzJujphsLDM|6VTJp?g{x55x<ucvP5p1O}V&_eh!
zk=nF=t{)ZNPB)8R=HBllR29pe#MBoG1xUrrkfF&X1eH+Kg;3`}m#)DVtd0Uq3+r<s
zFzXKJ>KD4Qc3M#0Ck%Wdn=Wjur=S7eqC*PT-0%7!lK5D<CfQT6y=U?pq)TIx9>@Gb
z8T|#~>7s+C3jVFD?lS)ug>)42PlY52e41n2!Y(BvpDsny#rBtHRgsuQV10*h5(X}B
zU~!dYJ11rD#?+S=Z>}Wsa$Ucut`w>G8i9O!<>9@jhZk1Prae9Q&7V6pxp^X3zCk?9
zr|ZR}gr+7QwS=Y3w5}e8b$@^TgT{zmrIzHNp_R2}>ULk0c!vXIu-lI<84kzh@?3k9
zU#8@D!#Oj$G31jtgTI~ch~0gH!Iiq&agvH^7(dRPbX|<?a->ByU)EPPBlhlwx+f${
zL*YXF+H<;RYhaoC@!>xx5iUA>U9hWBDXEmDlPIy52#vBtoepY-32B^=fI9~tRJin?
zG}nbf>2Rfw>!X^Z)gd?68T_0<ji@!o`rpMSz1f1o(7pSgo6lS+Io11HYW6Yx?-mR}
b;va_1e$L-bpubLf&;FI~LO65g{F(m&^O9K&
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_tnc.gz b/tests/ubifs_tools-tests/images/master_tnc.gz
new file mode 100644
index 0000000000000000000000000000000000000000..4219a5e0f7dee8d331cc3800aefe14743597ab99
GIT binary patch
literal 4805
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_total_dead.gz b/tests/ubifs_tools-tests/images/master_total_dead.gz
new file mode 100644
index 0000000000000000000000000000000000000000..165d78789b54ec4646de14be7b32087b0b7d5bbd
GIT binary patch
literal 4817
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_total_dirty.gz b/tests/ubifs_tools-tests/images/master_total_dirty.gz
new file mode 100644
index 0000000000000000000000000000000000000000..9416bbd6cf7afba06e16b2c73a2ce0c6134336f2
GIT binary patch
literal 4814
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/master_total_free.gz b/tests/ubifs_tools-tests/images/master_total_free.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e6e942ffb48ef2e99c04bfa1de4dd6bbed8de5cb
GIT binary patch
literal 4813
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/orphan_node.gz b/tests/ubifs_tools-tests/images/orphan_node.gz
new file mode 100644
index 0000000000000000000000000000000000000000..901d75d4a40a530c27eb025501e6bc5a54ae31da
GIT binary patch
literal 5379
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/root_dir.gz b/tests/ubifs_tools-tests/images/root_dir.gz
new file mode 100644
index 0000000000000000000000000000000000000000..d45e4dcc1ac09b33081ee65b175e469c6ee39a25
GIT binary patch
literal 5058
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/sb_fanout.gz b/tests/ubifs_tools-tests/images/sb_fanout.gz
new file mode 100644
index 0000000000000000000000000000000000000000..520fd09055ca671969093bd07bbb200746742d06
GIT binary patch
literal 5031
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/sb_fmt_version.gz b/tests/ubifs_tools-tests/images/sb_fmt_version.gz
new file mode 100644
index 0000000000000000000000000000000000000000..c6309e18c593a96a001ad332c967faf968d01431
GIT binary patch
literal 5032
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/sb_leb_size.gz b/tests/ubifs_tools-tests/images/sb_leb_size.gz
new file mode 100644
index 0000000000000000000000000000000000000000..a04ed7b630895e0a89890d5b589bcf4f39e540b5
GIT binary patch
literal 5033
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
b{$$nu5;*XGMfZNh?h+R5*x9vf|E~W5uHV*!
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/sb_log_lebs.gz b/tests/ubifs_tools-tests/images/sb_log_lebs.gz
new file mode 100644
index 0000000000000000000000000000000000000000..9113fe4d2407d31f40ca9ee6363ff36c56b7f937
GIT binary patch
literal 5031
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/sb_min_io_size.gz b/tests/ubifs_tools-tests/images/sb_min_io_size.gz
new file mode 100644
index 0000000000000000000000000000000000000000..788096deeed7a1959ab322bba3ec186e1a158d5c
GIT binary patch
literal 5035
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/soft_link_data_len.gz b/tests/ubifs_tools-tests/images/soft_link_data_len.gz
new file mode 100644
index 0000000000000000000000000000000000000000..10414a7710ab27242eeb479a4da393cd3189f4dc
GIT binary patch
literal 5112
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/soft_link_inode_mode.gz b/tests/ubifs_tools-tests/images/soft_link_inode_mode.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e333611964870cd1a888ad434a6178da4c60a644
GIT binary patch
literal 5121
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
YWBWDmw_5Y(I#1B%`Gb&My99Rq1JGgqEC2ui
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_lv0_key.gz b/tests/ubifs_tools-tests/images/tnc_lv0_key.gz
new file mode 100644
index 0000000000000000000000000000000000000000..bcc8ca2af6f9700762cbda165501d19e0608933a
GIT binary patch
literal 5118
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_lv0_len.gz b/tests/ubifs_tools-tests/images/tnc_lv0_len.gz
new file mode 100644
index 0000000000000000000000000000000000000000..1c64d794aaf2169d164a742b8124b306e2e83d2c
GIT binary patch
literal 5130
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
J=+Mza{{u^rD>488
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_lv0_pos.gz b/tests/ubifs_tools-tests/images/tnc_lv0_pos.gz
new file mode 100644
index 0000000000000000000000000000000000000000..5816123fa32543f3150c18927940a72c754e522a
GIT binary patch
literal 5118
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
BDR2M)
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_noleaf_key.gz b/tests/ubifs_tools-tests/images/tnc_noleaf_key.gz
new file mode 100644
index 0000000000000000000000000000000000000000..fc7c0edc87f66590d568b4c0a13edc83acaca369
GIT binary patch
literal 5140
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
Z_`d<>?EkD(Kdp*6h6>dmI3RG~{{VImH@pA<
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_noleaf_len.gz b/tests/ubifs_tools-tests/images/tnc_noleaf_len.gz
new file mode 100644
index 0000000000000000000000000000000000000000..2d0f80d3f8f142fd6b787fc3cabf71ee4e83df7b
GIT binary patch
literal 5145
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz b/tests/ubifs_tools-tests/images/tnc_noleaf_pos.gz
new file mode 100644
index 0000000000000000000000000000000000000000..1a05959b9773f9daf19199bad97d784a0664c2e8
GIT binary patch
literal 5125
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xent_host.gz b/tests/ubifs_tools-tests/images/xent_host.gz
new file mode 100644
index 0000000000000000000000000000000000000000..b2aef46b86914b66d3511b15b60542bcb0a9a18a
GIT binary patch
literal 5108
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
O1Sotn#||ChKlFbHa1pTp
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xentry_key.gz b/tests/ubifs_tools-tests/images/xentry_key.gz
new file mode 100644
index 0000000000000000000000000000000000000000..b4fa6d53e6fc471e3fbc7a40825555707b164990
GIT binary patch
literal 5085
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xentry_nlen.gz b/tests/ubifs_tools-tests/images/xentry_nlen.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e351280373c12a3d64c93992ca13686aaa397d6f
GIT binary patch
literal 5115
zcmb_gXH=8f*4A+p9R)@aLAnJM1fn9nWCT$<qI3vIF99MY^dvKbh=2%4CqSfkkd6e1
ziby9?0|^A_C4>ke1OmzX&As2B@6LGtedpI%&)#Q0&suw(z4qSXaVJmCwkf;)bS%Wx
zH_$)S!Pnc>_sGV~DXYPKvF@z^V^7m0*DP<@CMjE2qmgTlC-b@ng(M5}FI|#-c`H)a
z8hY#g7aOBKRnw_D*ShC!UnQ<U&RVywC<Dr^1Zvno4uc(*iv#w7AsQ2m0cgR-_)v8`
z^Du|Xy2}?wPSjNrUc2D6Y*s8={v{^m)2m;O{P=ty&MpJEer)>|Y+1x{IK0FF5+a=;
z8TWqdbwpE)0sI%!A*b}e24^yB8NkTa3#To#{MfI%imBXK+^Jipf{(TRBJw`E_^syd
zP37BK<ae_2|0L!+I_CdsTFOAF$9i145G5UB#(n>?I7qLoeB+{N)YkI%H?lS2tjVmQ
z$6>=dCPXz$gS*uMR**a<g(4$*U20P@jRBqbSZwHyiz#YwC{8QAC0kkR1pY6wk-Ru#
zHf|MFlppfUu}9$aU|^&9orDuD2GYWfvq6vf6s^aRkfeyy8aGhJm%W`FpYV<JBcJ}J
zH>bcuIn8TaxmjFbmhDNXad2z3+>V{gzf_bD{yp}Wv;U?;g&y*r`3LcM#bW@ppU49E
zq@pekNc_Y8zkqm+J{z~Ey$*<Tb+_tv3&+mp{#KQBiKag5`EpBD9^#Xl6|X3K<Frrl
zh!NyuzFb4s>FzsqoHXIw2b5FYnp$92$64E@8P58ry<9EZ{?1{xJ+tD{IR-zGXDO6B
zt+7bD-A8!aya~EQ;U8CaDFbDL%#_`^9Z?TMG;HTW<NGL&!bjaCDDGF2+!Gty`-U${
zLh0#EJ}H~j^L1bJP1+!>w7h5S#484|XP!HU=V9(=JvPb!iAPb)`Kp!7E+xz0Hq`A^
z4oJ2gE2?ed90d77Y3%(pEs{E8Sy5a*y^b(zou>KrAbf&K$nNe7wGHx^I9b7LvmXyd
zBH9}lFK+M?$HtZdp4QyrI%RX&G&s+Pu)Nf?{)tkRk#E;;BZ!uGWm%;jY!vD?m0{AS
zF6!dbnzcPp*VN?$!{Txc7|j88nRAta8`svz_wsyFircEvwa}(M`;+8f0&bvjuVJJ!
zE-I1X%@}t&jD8sf4vkx)ad$CCMQhtIRa$8Dee#5Mo~-R<+-4tik(2wwG<}HPhpz)&
z3R=>!H_4jLK4$wpcPc4Iqb}*kE(E+0>;E!VC8LzadE%Uf!S@X3gLB<APM_03os(3(
z$k(fHQTHab(na#7_5a%yz*M5%uuyYmv?{>TLZpTo*r<D-4aR8yzcab8`CQq~1OO+`
z!I@gBKbzs(?=iHrE&3w{`82-oL5yyp^z%}Ez1Ff*2}VV>Mst&9llIN{#5VbB9epbE
z1xw*ml*V(rik~`?wZICF$&g2ovm6H(iuk0&T#>#41?0vC>L{4t;h|6O`dG};Z7!Xr
z-M&v*g1W*~{)t)JFmJg)89|T(5UszxP$EQgKq)!Ws=jMJvO}3q07}+C*V-Sv-W8Q7
zT5~jP0=G@4fe_yc-f@nPEUamDM_*aw-8_l95#)(Ov9+VvkE57JDFCSk)wL5%)+o0|
zouUBg5P(#%lH0sO2b#@}{xkdE+L22vl%9gNm$aDOH1^s(3&7Qth%$sTX2$>kP64h>
zC&-!>aR3;W!)r1IC8V$b9F=w5*UQ@XA)7WE#BQcBRxKfHB?f?+VW8I21KY1-Q0u@C
zxY`Q-ss)RG)dG{k^KQ2y|0(9*NJN`71DNM615hF&l*^*98_4jV;=NvYbkGlu%3^EM
zXMo0!ylaC(7Gxa6NbHjZ(&PIOx3w$tceY<|Z)u<jy!|swDU62!SB8{?vT5SRbd6sF
zh(W#Y4CqZ&*no|=DtC)zhbAkVKrK)xy0u1|G{^0aZA7qgY&+^Yc`Z#&2<_4uyA+|$
ziT<<SUJYDJ5bZ~kVDF46mAiI`Y4w_s+iQ#q<a(df+sf0Lej8I<q)sKsG{MPOfSU4T
zd2{p{Pd4`3a??42gS3BO-;De8_7jJi#;*~J<<KwOL0`aqA!1=Ab2*tg!b=s1H>sQ{
zg0Y_vGzcZC)QVxclq`$#c)jo;60iD~Zi$pkZ##|BLEH~s<-+)i4*i2rK;jfr!T7pS
z%uD<8OIWH|oC`nOYPOyW+rU;VSqZcpoEO0C+d<|tut1{4lk}u2cyt8EeDaa}xNQKp
zy)QP0FyA9G_?XeM=Ux@(9e|Oc?NK*gIZ@vEg^IahcHT3}7Cuco*Eg-}+dxjQ6{Ra`
zT*E_xIm2?qkMw>wheuTGckEQRn!6&_30v-?Wz|(sicUa!&V1fa+2q$w@8#RYd#K4u
zwTvk@=J-<1M<&UWCJjwq_t!U|ztMZ&PmzKOm%Y}TM2H&Y`{SQWoo8X-W%^TnwyIh@
zD7y~a>h3r8W@)Lq!}vuLo1z2Tcsez(ytB9o*LbKV+Yb|@mqB0oqgA_$uj)8h;_?)4
z;)1PeN2{@?9_)to5ti4lT2Agoy^uBc$*=*1nUwIv1vuZ$jva?hcfdkN!W%Y|?4ArB
z7l1sjaS`5lX-r3idXM#-+Rs7Q83}-qA!XFm&J|x-c|G&oM<bIDORQapn!FWp$k&?)
z0f=tjv$i|nV23|yha>Ur^4it2Gm6)pdk&xH9;d5P+)t3%thEe~mymIQIEJht<uh#6
z*=VD-X$w+Y80pug`4z5z7#Db`)bUi{jhn!n8EJd+Ic2LLf1?}UZhKI&&3UM>Fm&AN
z?wPL|n%+gr)L*-8hL4ujf><@pM$M>UvZv>M?`hMj0G6bj^4(x<Z0x-R7AN8wr97>N
zK|dv}(%-!9?P<O;*Eil<O^QSRyqUcBV79VR!#|te<ZvQq<Mu>@CCRB@lb}fMU-jOM
zXc}gGRDj#_1f038k5%z8V!!nzXpCJ8j>jx|oV|sb95|&D!=GuLTTfh5`&k7oniV+O
z@}lYeYp*wv4)`mP=2_Kt-=5JzmR}s}c5m_)Nr_PFLZ)vU%TpYW8NR?(vVABw`-&l_
zP1KE4lw__wlE3E-pEl}-dNdIVC<bj7J2!koud4NMK76FQ7Q{9QYGBewq~S4PS`|GT
zRac<1JGYIAmKPRj?wsF3z3x@V$N9xgo6t1Z{b1c}S0l>K#47SBfv<LZRP@;4gs+27
zD(&{D?1U|#cYo&S169Okxz5A#j&+ON#B{Ysfhyho5)wVQ!uG<=&X<`F2r?aL^U>CO
z$Zt~xy#`|BG<DIL`#bGvl!6_+CA9f=A3;^*)}kB!!!h5BFCI$&geRULQ9Zw}P7MBB
zgK&_mC|Q1rt$kuhPV<|)!egVOs{2cmh>Vn!r+;lc<RJW7{A-Z`>$Yk=jAD7?ZmymB
z{CHLLrDIih<ky>JWcF@5r}tl+eF#ZET!`+rzOn<#>_*7%zU(@lER=OzQ*N%rNhm=8
zJlc1<-{$+<*;|sS!&zx>e*bVX`FV`10ZRFs*p!Of%o*IJl{5A0Eyp3Eg6twTQRTR$
zx>HSN)@J-!P%t9mV7tef3Qud>8(GP+hvLhWI$6Hu!Bvb=B7Gj`@6R4vpKfX4wxG&A
z;1!?oi3pkPnqN7()x6sfL#k?LvA&)VEx0adB^lL>_GwoBo)_x0l4Vh~ow8%3Z+sZA
zu{yde78>~}yKg`PXScqpX$q@BRJvA4G`<Z-Uyu{MHSIjMTi<vWj+&6N<)@L9*@@$v
z%=$VylYdaEhh0qhy4K~kmCqKQ2r&$sxL-09S$OYt5kD8c3R??z;00c$&;vvi#X+L2
z`A(xh!zwgB7NkfX5Kctwe)b_Jon2A&g=lR2fw9!-nM>1N{xUeZvyfylqj1%W{u6$n
zF{WOydJ4LcSr$Fc+$2eBHhhn8B1iVFMcXULV0ZEfR-F&Hxw7t)bO<qUaoVgOllX1)
zOY+0;foZ8MGZ3Ql_a!{tSf(WhK0h`5(p<<Bv2i&o4D91QC5cNO%wp3=8a^eTSe=Z^
z?pDH@rkGv_>1$9Z$vTqz$80N26ye-TJU02>tGGV!Uf1~iyJPiy?l<5O)~&@cvvA&n
zXR^3SLxLf+Dlnm?y6k9|XmPd2g{)bHX1&qxjE_@bOrJg?RTatd<r=p}#-|YnrXG2a
z3Qb%pY9+}g?jqBxVX%9U`pJUG2@`SBv;C~4`uaD;sl1S_vP|@F1~gb`I>hu%qokUv
zzROe{PkXTfxGvacGqg%rz$`gICC%h6;#?@gC8(gd%ESm93Zd`j<4L~8P!Cmq8j%dh
zoKfG4>8rmu%I;JsW*Cow&#?otSwr{@bx}~78~00ykj*43EH;R@vlR~^A)t9!A`S=d
z9<{CHQU<kLjhRvjHj6HaOz|#2ty9rLEzqGt<0ljrOxng4cOMjitJ(VO+`j3NVRe_B
zMRfk!^%6}9OK34g?w2HfbW>_@-h7E6;X!fCHu}>7)3`kCk!edE#-w?m_kn>tBk?*G
zWHNMy)8`Vpw)=%CEM@{1ZK<A`o;&egPEPw++iOJQdQ-jgmr7Qg8t}Bm^Hp(B=<_|y
zGPfx5jB*6r0Ril)NZ-C0slX&>jJkOf4eC2D+JpCBnQra5Q5W^N6+y9I^&2#T&8}NJ
zht#NEFxqY#6bG3gm(MZYaVF5z6A9DrJl;1hE+wJ{HmV6<AU7xB@vhV$rrTJrAi3ym
z65W;E@mhXn+18NuuA&-E)>k?`#XP=JbAW`&MaZt+i11v4EFJvmzr<c51(#-C=~B}8
zK?n2!qnFA;^+t~Y7+tA>rzd{UM*Zv6UWoZ$4ZHYb08mz*&`~8nI^6wPfBZ~YQAlFl
z?)Ld9cErQuIvH^O_VzzuMsv|8?Pqb8tcafU#v$92-F45r{u;JgT?uJi9GJ^O!89t6
zX5rfQB9Ly8j5D76Gd7kPzask657hmOS@uws0l<dd%FS9wTPPa(W!73b>QaNf=v?k+
z-i)tc0u`!C59fXliIRx@Y@TaLto=#{Q~CAIivdoyY}oNO8uJ)ZuWu6aM0rz6t;Pyj
zofchiV7^<Akk@+Bu;H#px{Z8NY{&qd3JD?N_&&N_m_>-4_Ll1B*oDQhauEPTC<}q0
zKao*6`r~%)O&xTBtl0glG>2$4T&Jpw6mK@fz)>{i`F?n6fqZ!#W(Iv$&sRY_m5dSk
zrc#viPB3+4-%>4Dz{d3=*O5bSwZ%eS{~~xTx@TI((kJZ~g;&|WYjQ3zbzK>)^|dOF
zM$kty-Z8hHg*gB5=v-YX`h$F7>#6gX!4i{ERsunJ_d?1Z&*!9Vw820ouZN}x;q-%<
zZ2E@)@(O|jte|oOc+^)Kx3D;!(|-B4%9J9v#JHU-yu>!+je&F>ow865D`7kn+x;Lx
zLu}Fc5}}{njoy;x01SWZT7OMCKaq3}Uc0aw1p%t!$_HM=yx#55;POAfep=7yAmei^
z<t$`>L!{tGBT9JeN$mScjM_Z!Ey}`JQa>RLEFtcWq$1STo2A4Wr0M&=F()D;+i8O-
z2^dxo*~)a4W8~<M{?iX*_!(va_xEpQ>&DO#Uk{-DyVK<*u>*LkU-rv1S_`+=oG9=8
zw+PIlvht+Vs9j17BOqk0PbnX1<R@|S4zvRWy+J@pAlc*u@E-g9TjXCcbB+AOf!%1{
z3QPX?nsO=cG^Ll<IGl3MR*289K(Pb|&9{Yb#%$<*0n)>rqb<0$W<_2-Np?Df*@>@5
zXubptDGOe1TMOW+kYc2f(`@WK?$8*YR_btg)i(2e_<V9gD=_u7)nB8tmom+&_jxN<
z-mN9p^z%aduMoGUgI)Pis&@=s#$bBo^IALubRjc=cz)fHoyr#vx~@sk*%z!iSjZ`L
z2vA=g8R(b*O;BgRlNyP-J#5M=9$v7S_+o*IAg$>AvNL{T+thxShTstghj--lM6{u`
zG>Ky5OjOZVU&F;Kz<6zlFf_&?Y0yr?Qa*DU1NMQ=FucBV#?HN}SL~vJ;h6ZHnwxhx
z`{(BYB&x$5@eC#31;X}h(GC9ZdP^qzeJ&vI_ia^%+RERjC<(+6aO^+-<!`L)zb~wQ
e-%$a0|I_QpS^!7<Z_o%+>FTp9QAduPIPyP|9R?!+
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xentry_type.gz b/tests/ubifs_tools-tests/images/xentry_type.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e0c57634415150394b01e80f7f109086e5b4b5ce
GIT binary patch
literal 5113
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
Ue;bBf3Py9;&m8{);`p)u0|Lz*e*gdg
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xinode_flags.gz b/tests/ubifs_tools-tests/images/xinode_flags.gz
new file mode 100644
index 0000000000000000000000000000000000000000..34d3ff71387f393c0814da352e03f36358e2df5a
GIT binary patch
literal 5112
zcmb_gXH?T!^LE``u%WJ9DOpfD7?fVK2#A1)^b&+sN<;{VfV70H3y6w<Ac(X?frSmd
zhZdr=P{hze2@pU^AR&Yn0wKxob^re_@7Z;~z4z0cd1mf2=ghg!nYp0&0|%y?wY>L#
zALip9;OXV=?ROUzv~OkVpi|$j1aUp+mQRw>kf)hfR+at8C3wmaNxMepycoF)3i&@e
z!;Z^GMpq`)W~PSZR(bV$nq?YBzofgEzggA(3`txKX_5$nAqi+23nd_I?(s=9U@vDV
zNP@Jfk8<2|v+T&#yfoP7&={N5luCbA6sUmxxpe=&Zy$u3BOUne+t|*OJr+2Ny1@rt
z_iJf-AOCjLZ%L+n$hV<=1jxT%&PV$33Bk?DPQ1k<>M?k@R~t28zvbzt!?(rUf7LP*
zYXL_UEY;O6{rN<W@aF!F7`dCL{5L^KxtZ}Mv*uFvM#)_y(+wy4?U#SHdIouRzZpHo
zU-{%MhhLg>qUb`QY6Z5&;_o>O214(;&9JQ!W3&bj$F3UMD*6zD;ns%c#uq!H{-tc5
zL{ttJH2|2Ktj`R-1D|=zSpG8~bZjftPJ`ae%$M=2D&p%%?VgEp5@Z{XwYL_?<frpL
z{Fc({E2e&2BHM**?}(L~YO%s>*<sojj7*{>;syH@6pa6mj<>K5QTYe4UAZ}*FmFT$
ztfWupUy1$){{Id{vjQ9VatsTgPF$eLhzDGXirioO;A!(Ff@mn?+s6*JQ@h}=VRwe|
zDL<?6xbdC)@n;jgJo%*>sU2!(68SMvJ*`x;g((5Obw}bFG(PVN(v4Frd4he0k~qI}
zB(+vr5f2yLvM-2SV4NP&y65$CHrQ1QCUO_$7^bf~8{XH$xE(p<EysW<CBt6T;(G?g
zC4CRSs)MJlGEY}^nprj5HnPed;&s|A4vEBi2*1Y|XWCf41w&#P_9x3UT_s;D5mxjJ
zoP{Tx@iZL-S%9Q2VqX|LT$>PMd)I+f)2l|O?_^TSCn86fquIosaA<^QrwW79>_!fc
zLgLjI&WoL*3=b~^&Ad|)hSr+o`d^zs_w}v}$Bse`oEr)fR2~<rxYce<UfB3lbp?|e
zq1!{*s+rI5g2z)P*Ae>Z^Z;7@)wb~EG0Q=G-nEGX%HdF}SdBTCjmlexg5<IYM*}8~
zcxfBuQ2HwQxbS2G2GOy>dHQ8<xy*@5y7eAjVN4yl^Iq9jim=+_J1;<gvrQl3c0=eu
zhkC2R3k9lyhllHKmqKo@WUQq5i@E9(=&sITtdiy%fvraoVR|h&7lMZAZ-hP*Trv+H
z>U^D6;wi1@+->eqmGZwb0f;kji4C-3sZgN2M9Yo15b^#Q7iAFlKWFOZ%Jy)HWdI7s
zL*<o)&9npvE(Ym~r*R8}4Am7xrKOUKj^`>!FBiz@X}tEo&{~;Mf%nJAgu`wk(t_HF
zU7V^>U-=byEYdVK>Orea(>on09-(Ul@~2=VU4dLec*_QQ%TS-aA}#V17}NM3(5Z2X
z;$h!=a`52WPAK=~%GmxZs1gJ}K5d@pmC14kKDJUFXOZvf1w|!5K$RfgCR&1G?~E$M
z#RZHIHa0;&BWDUL1iOQCzrOcaSJ{njY6ZQ}*ZG?G@Gil47Wsh-Y%|<{a$T9gXR^~P
z1gLQ~q58qLiLe2HvN-*X=2IDtWk9-}s<F}f%!Ysr^<uk^cq2{i9RW)|;EodnOh(tA
zu%`n+9m5?_<Ub)Z3;^&^6tUpU!uBvA_-fLk$_>b5lq~>^-T|%xG0hSP0TvMcC)Zly
zS1CyODg}@<yH}k@{zc!{){BOLcu2kgFuR|f^;K<mm<QF0&*!o~vxIAw>TW#-M!ArD
z4fySF;IVr60o~pAv$~p|X;a^C6FUm?Z1H4K*3RQR9MPCqU7JPTYYC&b8&$%XXO1;o
zh3!$AWLw4_`<txV_e#eWUXb;7#4L?@-{-HC!FyUnvx`@D+So&e*O8I_mAzx<awm1w
zl<xq~gThtgL^R}chS9}I8FQOqb`Pe0<S<<dL|z7uUE%WDa+0$ygYFv?SKk^uf0%@w
z+asYveIuXmfloU(E})0}_lnDh+<v`+SP*&5ZJGo9p?{1pF|)dLOC*SF(0zly;+xLS
zEapVvqdY;=W|#XYpph42HI}HBxNfENl?|!tc(6!ZLbk7HDesZ@^ludz$J9tNHxpkt
zpVJTA%UFD_AQX6#1X`V3oG6j_xawj8HrQ-x8PndI`H3mx-v(V4;qr<P919{(RExO#
z6J8JT#*|8lbjBQ}R-vXhs<EUEr#ke)PpWf1ECV&j*e?8hxXS9&Sb?lh_{9>~T<Xl2
zm=(T(sLP1?teg{WR?GRF<&9oRrEX<T^=LpZDoB`2M{HRrB1cstMqf_KB<O0D_6^7M
z6J@&LY!~VCM=?~2hE^alF^#W9u)6$dvPDrFYEd3*id*s?4K>E>M9~fs;$AbzRQRLi
zmu>6F_}sH##Zb)BJ<m~`|EJDv2TM5V7Ttjuncq7&DO8DRBr4#_koO*4yNTvn)}C*;
z^^?iv$W^%d<NGBDk2?Q*v8AZ6_7h~G?hudd5I=&PuEWqDH~5P=!VZN<Q@VJs#!zPH
zxbm(`!ED*v)aPKUm*)&F<6dK$#&cym94W545wqJiL!JwP(h0oatj|{3GhWE?>G5h#
ztHnW|&!dappf1fTQ9BI*5l&P*vwBddZ;EmebxSsS>s_RKdII-a+x;Ti*s%XW`NO87
zr2|y<2k&KV9J(_B^U0A(pl@_tPhV-XS;=#K;GA<V{VYU^a(87VvNlSA73u2BkNu@r
z=t+cZTuFc9$wgOH85QkSlj)Gh*rQ-dgOWT1Dc@-CfDzwJ{6y`qyZ0fF3c>W&mnlC2
z5ZUG<Rq#fr%gAN7N`(O=EIF$GyYBY4Q1#Hypt(kV`ges*Ng9mtg07;`gapozj&nUa
z;H~CuBhJEcn#x=%)YftI@Ny>Y4-KpZ?*~}#hX$j9B*jVVWb~|lUg;B$XT`h82XQwa
zEIBO|$!KvoS{k?i=zbBN$gwI2&<cJO5r19sd1#8*wj4_XcYcr*ywvM5UDsIPH}c1f
zo%3L<WTl}Cv|~h8n86M6<m&Z6SE`#Mt>Zm1B#*`&WhseU*LUpfX6L`@jwj>lvM~+t
zuu^E0%t2vuqy0Q(2?eWeQeB9u51PM>H=Sa!A}VUk;Ie)Gaoc*Q71Nn?8o`^Uz;G>J
zj-Eq^ps$mcVI>m`w96+1+3`*<Zqbu-_D<i%l_N(E%K6A*ZN=25cAhSTs;c?XX+k*=
zbbb9!V!mH~%TgqqV~WL;5Bk8fi;-#%`%5JZ<Ae&&ix(fz2{`+JP%g-uz}0cZb{_fM
zQ8#T$5cdeGd-+`K@mNGl-X&D;Y)Z-res3vrVoDPpw(@W!x0Z`{DIS?bMGRCp;^N!5
z(85Bpuk|aygCc<oItdDK<|Tp9o<~&c?^O!228c@FjFJ8xPEfqw$%*?VI~k8}8Fx0v
zA)m@&Uk+EL{2*kK;V<sqI3H((-qh_V%UOgh{ce4o`B8FokBC^V@OBWWm)DDWd+&L4
z^iE<X8*~uYb*DPOEn7_k^=X_`=udb}GJc?_WF&j1+;tLC+oIyl7h=?<dpu2C(b1^l
zNUd*v*qPXKDW5K`Rv_g?#k~!DI&v@>4x@k%Db$S(n6))}C8j*_>BgcGCBjEq6=A=`
zSYTs0=@!}gL`^E|ILuzvSb1Jh@S_D+gnglM6Je>0$Anloc3SzRMznKhw$5iX2p@ti
zz)(XS7wfz9+=3srJt#j&+6wW7+&nkKtN*eVTdRo^Uy?YU7999yZt-=J{u@WX3p8Im
zI5?(iekSt+34IhD<Tn<3bizR-e2HcI)8m?scjJsgICtHt*FW<_t+pY}d!h^{Rzwck
zYLGW2b3kJ<230d(60_r8+axtVxE--zLK?U(BD$rSP?>}kr)g`vYon^ahgr%bY-P4U
z)!zHlpGNhfY1x^fvU9`LbJmLLwh`l|>g&qBo@8xY9+Ms&WK(nTBzIZonJZBP@BG$5
zSJp*;jxiJ~-N4T(w@?d8Qak8e;&-az>d#vp?A&KYs-ZV+>1!M750HQ!IPL}<s1N1@
zi!$xX*>_UDkEd8UQ`Piv7IjgtyF%V)Krr1mqSEv<!pigLM}VgmelWOP#SlxKu&{Ju
zy2>X`vvC^ZNYq>1`7PvQo~RNO@8^BJb@)-z0w(jAhD6AmKR*j>ezne1#uDw`Sc4>C
z`x2R$Or-VRqBObv<mZLCKJ;gjD0;lYtXTD9;bzUM60f;$F;&RBjZ+@Tg&FqGTG^p3
z4<CItoK3H3n{oO?Vvs{o-Yh0!H}E>4UV8S&(v6hhMp1J3Ds>xbfw%t-3D?P<x;I1$
zp`JEdSslMfNf)_Y6~Ei<bMH|<+WDl;E$qn(>_ba@N@udLZ01YIz-Czi)+-O*Jtgd#
z*jPK_GuF3J42cy{BVbQPI`Ss(`uj9<rs6~svLcU|ue+X~LR!r}&jI(5+t90c9eMpN
z#UZbxP>qQG8PBRnpw<$0O#Rd2pv?nY+Uw*V-LlU<s&r-gR`fuDwTsSz^%s<j+C$!z
zwI%lV?EMrkY<t-OG6fanz!;DR_aznV^5!+oI~ZB&uVsd(n^nvs<ECQg8lPtxMUK=B
zlh$j)s+86<puvH7IF52utmW?PRnhKVL*Om7G|<<Id7DFasHIRb4!ZFBG~UxPN0A6!
zE0Esk)p|Z!Ct9@poS*z&%M3S!#Zrzb|4}>!tbEJ91uW-Cb#J*7nV=-!viz+rR&ev{
z{9|G8b^fulF}$F9;Gc8UU!D)dVjBP~c*O-|-v{~+u3!Ixlf7N!eZ#UNI$zyUws)1L
zD|M--Iev*Lf>4hENrfQ+x8=>d?jz0j<;8kiv1Xbf%Ws@5Bz6eN2}#?$fok8h^~}LO
zM@xauPn!w~R84f~zcA0>))>P8K|8t;0Xyk7-e3M^$Te27_7rgsJSZw#OoA}|9jtfT
zzFR+Jc<OnvUU`l06Ap@^m?Ebq=qTe(9}1gh3G-53)j1kztxD>aJ4Kb9G4<fuZC6LC
zUbCp#f|+Gm3tr1w9o)HBPjZ5=dNy6d9Uj`)uIseFn42Fi5(B`a*swcUQdCrqIoTCf
z@Pu*J<;CtLmU~<ip-tz9(XF)-Y{gOYR^DH`p7l7%?XI|*gnpEYbbsU{=-n#{q@X2t
z=7jrwvc1^8HC^#q>*sf%?27dJjrNx(l%}Hdy88Mc$EvOkoU`D>l^*o17Fvi`-2Z;f
zB=(7Um$ADs>(z#|%Cq5L)(ne;-8Cd5YATi_?@Kok+#!i>_=(9VFox8N7yir&jiT-8
zPe-oe9|{a6PAIom^75%iIwPf?jo;i<QMb`r<n$Tw>0vGI;PcuwYF$J`S-NgHp<^I&
zEn>HNd45@TliByYF>G%pjTt&>qA{dW7i7vRdtat6emCUiH0!}$0;p>9HYi?V5K+#|
zL4-Odvj=Wht+4}FH$k$BtI0|65Ki-HHWYL$vrmk{nyd+ZM~l9;yVDXnQChe;MVgnz
z8Jy!_jd|Yova$seNu-A@94-PrMXCLgA+;Iwb1ZLI3{kT(evi@MH#^E<`;PPNcefkc
zxw|FJ^n^d%ZN<7eGSbd!9DU3d=@?}R=JT?8qq*=yo#6%@NM3P(L7A)dJh%R8G`n&~
zQhyqhHk~l?!+OA>FVor2Q2xZ{Io&&eTFA`M+240Gh3D)F&jC=v(K^Dpc0xb*ZI=>)
zM-&B*l-^u{TJsbIbn0<-{B-IYFM)Pcm^%$6>SF51dAMOfnD%FsC(y58;zT}%flY83
z%ohG+a8vNpO(iou+Su+W!|n1_kh=VE8%2~T>TZT8kKb4~VS-u4e3vhVy{atu?xVg{
z{qJ-5&toPVNL_1d7efpUTz+47=u-zZd8a3tNCObs+7d3E5&wy}CqJLZtb{i>ZZbQK
z4IUL9?U-=#MVR<_f3abQJwh1_)_Lvuex%v@s(hxOLIJ}PQ`5MDt{s}qGawGxuv+_Z
z^Yjb%<^4nwME<`XwF+u6Lg4<x-=yHz)$G5oP`_<y1IPX=@h@{ZNC5hqYmmnK``=bR
VX&3dks2kG+8|8bs`}PU#`w!t_8?FEV
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xinode_key.gz b/tests/ubifs_tools-tests/images/xinode_key.gz
new file mode 100644
index 0000000000000000000000000000000000000000..c92956cde911a84c6c192973c5e7f3e1fe02b06e
GIT binary patch
literal 5110
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
literal 0
HcmV?d00001
diff --git a/tests/ubifs_tools-tests/images/xinode_mode.gz b/tests/ubifs_tools-tests/images/xinode_mode.gz
new file mode 100644
index 0000000000000000000000000000000000000000..0e5264815fd83f6c6aecaf1b7110ee69a0d16fee
GIT binary patch
literal 5112
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
a|H(f&x&LfxJ=x;&zwT`S?b~-?-@gEKt`qhE
literal 0
HcmV?d00001
--
2.13.6
This is the second part of 6/18 step in fsck. Add an extra checking for
non-check mode while traversing TNC, make sure that all LEBs(contains TNC)
can be scanned successful. There could be following steps and possible
errors:
Step 2. Scan all LEBs(contain TNC), remove TNC branch which points to
corrupted LEB.
a. corrupted node is found by scanning: If current node is index node,
danger mode with rebuild_fs and normal mode with 'yes' answer will
turn to rebuild filesystem, other modes will exit; If current node
is non-index node, danger mode and normal mode with 'yes' answer
will remove all TNC branches which point to the corrupted LEB,
other modes will exit.
b. LEB contains both index and non-index nodes: danger mode with
rebuild_fs and normal mode with 'yes' answer will turn to rebuild
filesystem, other modes will exit.
This is a preparation for space checking, which means that ubifs_scan
will always succeed when check properties for any TNC LEBs. We do this
before checking files(step 7) & extracting dentry tree(step 8), nodes
cannot be dropped(which may corrupted file and make file inconsistent
again) when scanning corrupted as long as the dentry tree is extracted.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 122 +++++++++++++++++++++++++++++++++--
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 1 +
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +-
ubifs-utils/fsck.ubifs/problem.c | 10 +++
4 files changed, 128 insertions(+), 7 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index 982c05b7..29848c4e 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include "linux_err.h"
#include "bitops.h"
#include "kmem.h"
#include "ubifs.h"
@@ -25,6 +26,7 @@ struct invalid_node {
struct iteration_info {
struct list_head invalid_nodes;
+ unsigned long *corrupted_lebs;
};
static int add_invalid_node(struct ubifs_info *c, union ubifs_key *key,
@@ -103,6 +105,49 @@ static int construct_file(struct ubifs_info *c, union ubifs_key *key,
return insert_or_update_file(c, tree, sn, key_type(c, key), inum);
}
+static int scan_check_leb(struct ubifs_info *c, int lnum, bool is_idx)
+{
+ int err = 0;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+
+ if (FSCK(c)->mode == CHECK_MODE)
+ /* Skip check mode. */
+ return 0;
+
+ ubifs_assert(c, lnum >= c->main_first);
+ if (test_bit(lnum - c->main_first, FSCK(c)->used_lebs))
+ return 0;
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED))
+ err = 1;
+ return err;
+ }
+
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ if (is_idx) {
+ if (snod->type != UBIFS_IDX_NODE) {
+ err = 1;
+ goto out;
+ }
+ } else {
+ if (snod->type == UBIFS_IDX_NODE) {
+ err = 1;
+ goto out;
+ }
+ }
+ }
+
+ set_bit(lnum - c->main_first, FSCK(c)->used_lebs);
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+}
+
static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
void *priv)
{
@@ -127,6 +172,23 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
return -EINVAL;
}
+ if (test_bit(lnum - c->main_first, iter->corrupted_lebs)) {
+ if (fix_problem(c, SCAN_CORRUPTED, zbr))
+ /* All nodes in corrupted LEB should be removed. */
+ return add_invalid_node(c, key, lnum, offs, iter);
+ return 0;
+ }
+
+ err = scan_check_leb(c, lnum, false);
+ if (err < 0) {
+ return err;
+ } else if (err) {
+ set_bit(lnum - c->main_first, iter->corrupted_lebs);
+ if (fix_problem(c, SCAN_CORRUPTED, zbr))
+ return add_invalid_node(c, key, lnum, offs, iter);
+ return 0;
+ }
+
node = kmalloc(len, GFP_NOFS);
if (!node)
return -ENOMEM;
@@ -147,6 +209,34 @@ out:
return err;
}
+static int check_znode(struct ubifs_info *c, struct ubifs_znode *znode,
+ __unused void *priv)
+{
+ int err;
+ const struct ubifs_zbranch *zbr;
+
+ if (znode->parent)
+ zbr = &znode->parent->zbranch[znode->iip];
+ else
+ zbr = &c->zroot;
+
+ if (zbr->lnum == 0) {
+ /* The znode has been split up. */
+ ubifs_assert(c, zbr->offs == 0 && zbr->len == 0);
+ return 0;
+ }
+
+ err = scan_check_leb(c, zbr->lnum, true);
+ if (err < 0) {
+ return err;
+ } else if (err) {
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int remove_invalid_nodes(struct ubifs_info *c,
struct list_head *invalid_nodes, int error)
{
@@ -176,10 +266,12 @@ static int remove_invalid_nodes(struct ubifs_info *c,
* traverse_tnc_and_construct_files - traverse TNC and construct all files.
* @c: UBIFS file-system description object
*
- * This function checks all index nodes and non-index nodes by traversing TNC,
- * then construct file according to scanned non-index nodes and insert file
- * into file tree. Returns zero in case of success, a negative error code in
- * case of failure.
+ * This function does two things by traversing TNC:
+ * 1. Check all index nodes and non-index nodes, then construct file according
+ * to scanned non-index nodes and insert file into file tree.
+ * 2. Make sure that LEB(contains any nodes from TNC) can be scanned by
+ * ubifs_scan, and the LEB only contains index nodes or non-index nodes.
+ * Returns zero in case of success, a negative error code in case of failure.
*/
int traverse_tnc_and_construct_files(struct ubifs_info *c)
{
@@ -187,15 +279,33 @@ int traverse_tnc_and_construct_files(struct ubifs_info *c)
struct iteration_info iter;
FSCK(c)->scanned_files = RB_ROOT;
+ FSCK(c)->used_lebs = kcalloc(BITS_TO_LONGS(c->main_lebs),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!FSCK(c)->used_lebs) {
+ err = -ENOMEM;
+ log_err(c, errno, "can not allocate bitmap of used lebs");
+ return err;
+ }
INIT_LIST_HEAD(&iter.invalid_nodes);
+ iter.corrupted_lebs = kcalloc(BITS_TO_LONGS(c->main_lebs),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!iter.corrupted_lebs) {
+ err = -ENOMEM;
+ log_err(c, errno, "can not allocate bitmap of corrupted lebs");
+ goto out;
+ }
- err = dbg_walk_index(c, check_leaf, NULL, &iter);
+ err = dbg_walk_index(c, check_leaf, check_znode, &iter);
ret = remove_invalid_nodes(c, &iter.invalid_nodes, err);
if (!err)
err = ret;
- if (err)
+ kfree(iter.corrupted_lebs);
+out:
+ if (err) {
+ kfree(FSCK(c)->used_lebs);
destroy_file_tree(c, &FSCK(c)->scanned_files);
+ }
return err;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index c85e9147..1486ab4d 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -443,6 +443,7 @@ static int do_fsck(void)
return err;
}
+ kfree(FSCK(c)->used_lebs);
destroy_file_tree(c, &FSCK(c)->scanned_files);
return err;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index fe6070ac..0d4a0d63 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -39,7 +39,7 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/* Types of inconsistent problems */
enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
TNC_CORRUPTED, TNC_DATA_CORRUPTED, ORPHAN_CORRUPTED, INVALID_INO_NODE,
- INVALID_DENT_NODE, INVALID_DATA_NODE };
+ INVALID_DENT_NODE, INVALID_DATA_NODE, SCAN_CORRUPTED };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index f99fd90e..c5ecd109 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -45,6 +45,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid inode node"}, // INVALID_INO_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid dentry node"}, // INVALID_DENT_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid data node"}, // INVALID_DATA_NODE
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data is scanned"}, // SCAN_CORRUPTED
};
static const char *get_question(const struct fsck_problem *problem,
@@ -60,6 +61,7 @@ static const char *get_question(const struct fsck_problem *problem,
case INVALID_INO_NODE:
case INVALID_DENT_NODE:
case INVALID_DATA_NODE:
+ case SCAN_CORRUPTED:
return "Drop it?";
case ORPHAN_CORRUPTED:
return "Drop orphans on the LEB?";
@@ -88,6 +90,14 @@ static void print_problem(const struct ubifs_info *c,
log_out(c, "problem: %s %d", problem->desc, *lnum);
break;
}
+ case SCAN_CORRUPTED:
+ {
+ const struct ubifs_zbranch *zbr = (const struct ubifs_zbranch *)priv;
+
+ log_out(c, "problem: %s in LEB %d, node in %d:%d becomes invalid",
+ problem->desc, zbr->lnum, zbr->lnum, zbr->offs);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6
This is the 15/18 step of fsck. Check whether the root dir is existed,
create a new one if it is not found. This step makes sure that filesystem
can be mounted successful.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 29 +++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 8 ++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 4 +++-
ubifs-utils/fsck.ubifs/problem.c | 3 +++
4 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index 4190bf91..d52a5660 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -524,3 +524,32 @@ bool tnc_is_empty(struct ubifs_info *c)
*/
return c->zroot.znode->child_cnt == 0;
}
+
+/**
+ * check_and_create_root - Check and create root dir.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks whether the root dir is existed, create a new root
+ * dir if it doesn't exist. Returns zero in case of success, a negative error
+ * code in case of failure.
+ */
+int check_and_create_root(struct ubifs_info *c)
+{
+ int err;
+ struct ubifs_inode *ui = ubifs_lookup_by_inum(c, UBIFS_ROOT_INO);
+
+ if (!IS_ERR(ui)) {
+ /* The root dir is found. */
+ dbg_fsck("root dir is found, in %s", c->dev_name);
+ kfree(ui);
+ return 0;
+ }
+
+ err = PTR_ERR(ui);
+ if (err != -ENOENT)
+ return err;
+
+ fix_problem(c, ROOT_DIR_NOT_FOUND, NULL);
+ dbg_fsck("root dir is lost, create a new one, in %s", c->dev_name);
+ return ubifs_create_root(c);
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index d3525e30..584965fc 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -519,6 +519,13 @@ static int do_fsck(void)
log_out(c, "Check and correct the index size");
err = check_and_correct_index_size(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
+
+ log_out(c, "Check and create root dir");
+ err = check_and_create_root(c);
if (err)
exit_code |= FSCK_ERROR;
@@ -575,6 +582,7 @@ int main(int argc, char *argv[])
* Step 12: Check and correct the space statistics
* Step 13: Commit problem fixing modifications
* Step 14: Check and correct the index size
+ * Step 15: Check and create root dir
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 2d17b205..5c305ba0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -45,7 +45,8 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
- LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT, INCORRECT_IDX_SZ };
+ LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT, INCORRECT_IDX_SZ,
+ ROOT_DIR_NOT_FOUND };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -374,6 +375,7 @@ void update_files_size(struct ubifs_info *c);
int handle_invalid_files(struct ubifs_info *c);
int handle_dentry_tree(struct ubifs_info *c);
bool tnc_is_empty(struct ubifs_info *c);
+int check_and_create_root(struct ubifs_info *c);
/* check_space.c */
int get_free_leb(struct ubifs_info *c);
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 32182c91..8e7e1e15 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -68,6 +68,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"}, // SPACE_STAT_INCORRECT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect index size"}, // INCORRECT_IDX_SZ
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Root dir is lost"}, // ROOT_DIR_NOT_FOUND
};
static const char *get_question(const struct fsck_problem *problem,
@@ -105,6 +106,8 @@ static const char *get_question(const struct fsck_problem *problem,
return "Put it into disconnected list?";
case LPT_CORRUPTED:
return "Rebuild LPT?";
+ case ROOT_DIR_NOT_FOUND:
+ return "Create a new one?";
}
return "Fix it?";
--
2.13.6
?? 2024/6/7 12:24, Zhihao Cheng д??:
> Introduction
> ============
[...]
>
> Environment: qemu, -smp 4, -m 16384/32768, nandsim/mtdram
> Code coverage[18]:
> fsck - Line 87.4%, functions 98.0%
> libubifs - Line 78.4%, functions 89.1%
> mkfs - Line 71.6%, functions 97.8%
This is the 1/18 step of fsck. Read and check master node, init lpt.
There could be following errors:
1. corrupted scanning data in master area or invalid master node:
danger mode with rebuild_fs and normal mode with 'yes' answer will
turn to rebuild filesystem, other modes will exit.
2. incorrect space statistics in master node: Set %FR_LPT_INCORRECT for
for lpt status. Ignore the error.
3. corrupted lpt: Set %FR_LPT_CORRUPTED for lpt status. Ignore the error.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 5 ++-
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 2 +-
ubifs-utils/fsck.ubifs/load_fs.c | 78 +++++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/problem.c | 1 +
ubifs-utils/libubifs/lpt.c | 18 +++++++--
ubifs-utils/libubifs/master.c | 7 +++-
6 files changed, 104 insertions(+), 7 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 683d7c27..34641c89 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -423,7 +423,10 @@ int main(int argc, char *argv[])
goto out_destroy_fsck;
}
- /* Init: Read superblock */
+ /*
+ * Init: Read superblock
+ * Step 1: Read master & init lpt
+ */
err = ubifs_load_filesystem(c);
if (err) {
if (FSCK(c)->try_rebuild)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 1d97aed3..cbe432c4 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -37,7 +37,7 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
DANGER_MODE1, REBUILD_MODE, CHECK_MODE };
/* Types of inconsistent problems */
-enum { SB_CORRUPTED = 0 };
+enum { SB_CORRUPTED = 0, MST_CORRUPTED };
struct scanned_file;
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index 4a06b4c2..036e307c 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -99,10 +99,81 @@ int ubifs_load_filesystem(struct ubifs_info *c)
goto out_mounting;
}
+ log_out(c, "Read master & init lpt");
+ err = ubifs_read_master(c);
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED)) {
+ if (fix_problem(c, MST_CORRUPTED))
+ FSCK(c)->try_rebuild = true;
+ } else
+ exit_code |= FSCK_ERROR;
+ goto out_master;
+ }
+
+ init_constants_master(c);
+
+ if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) {
+ ubifs_msg(c, "recovery needed");
+ c->need_recovery = 1;
+ }
+
+ if (c->need_recovery && !c->ro_mount) {
+ err = ubifs_recover_inl_heads(c, c->sbuf);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_master;
+ }
+ }
+
+ err = ubifs_lpt_init(c, 1, !c->ro_mount);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_master;
+ }
+
+ if (!c->ro_mount && c->space_fixup) {
+ err = ubifs_fixup_free_space(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_lpt;
+ }
+ }
+
+ if (!c->ro_mount && !c->need_recovery) {
+ /*
+ * Set the "dirty" flag so that if we reboot uncleanly we
+ * will notice this immediately on the next mount.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
+ err = ubifs_write_master(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_lpt;
+ }
+ }
+
+ if (!c->ro_mount && c->superblock_need_write) {
+ err = ubifs_write_sb_node(c, c->sup_node);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto out_lpt;
+ }
+ c->superblock_need_write = 0;
+ }
+
c->mounting = 0;
return 0;
+out_lpt:
+ ubifs_lpt_free(c, 0);
+out_master:
+ c->max_sqnum = 0;
+ c->highest_inum = 0;
+ c->calc_idx_sz = 0;
+ kfree(c->mst_node);
+ kfree(c->rcvrd_mst_node);
+ free_wbufs(c);
out_mounting:
c->mounting = 0;
out_free:
@@ -118,8 +189,15 @@ out_free:
void ubifs_destroy_filesystem(struct ubifs_info *c)
{
free_wbufs(c);
+ ubifs_lpt_free(c, 0);
+
+ c->max_sqnum = 0;
+ c->highest_inum = 0;
+ c->calc_idx_sz = 0;
kfree(c->cbuf);
+ kfree(c->rcvrd_mst_node);
+ kfree(c->mst_node);
kfree(c->ileb_buf);
kfree(c->sbuf);
kfree(c->bottom_up_buf);
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index acb9e45e..1af66632 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -36,6 +36,7 @@ struct fsck_problem {
static const struct fsck_problem problem_table[] = {
{0, "Corrupted superblock"}, // SB_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted master node"}, // MST_CORRUPTED
};
static void print_problem(const struct ubifs_info *c,
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index c0df7c7d..b07f1f77 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -1883,8 +1883,13 @@ static int lpt_init_rd(struct ubifs_info *c)
c->dirty_idx.max_cnt = LPT_HEAP_SZ;
err = read_ltab(c);
- if (err)
- return err;
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
+ else
+ return err;
+ }
err = lpt_check_hash(c);
if (err)
@@ -1938,8 +1943,13 @@ static int lpt_init_wr(struct ubifs_info *c)
if (!c->lsave)
return -ENOMEM;
err = read_lsave(c);
- if (err)
- return err;
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
+ else
+ return err;
+ }
}
for (i = 0; i < c->lpt_lebs; i++)
diff --git a/ubifs-utils/libubifs/master.c b/ubifs-utils/libubifs/master.c
index 61ff4cec..54d2a789 100644
--- a/ubifs-utils/libubifs/master.c
+++ b/ubifs-utils/libubifs/master.c
@@ -323,7 +323,12 @@ out:
set_failure_reason_callback(c, reason);
ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
- return -EINVAL;
+ err = -EINVAL;
+ if (can_ignore_failure_callback(c, reason)) {
+ clear_failure_reason_callback(c);
+ err = 0;
+ }
+ return err;
}
/**
--
2.13.6
Inject powercut while doing fsstress on mounted UBIFS for kinds of
flashes (eg. nand, nor).
This testcase mainly makes sure that fsck.ubifs can make UBIFS image
be consistent on different flashes (eg. nand, nor). Because the
min_io_size of nor flash is 1, the UBIFS image on nor flash will be
different from nand flash after doing powercut, so we need make sure
fsck.ubifs can handle these two types of flash.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/powercut_fsck_mount.sh.in | 144 +++++++++++++++++++++
4 files changed, 149 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh.in
diff --git a/.gitignore b/.gitignore
index 7de38e86..b63b9868 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,6 +116,7 @@ tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
+tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 9d17b9ab..99ce86c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -292,7 +292,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
- tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh])
+ tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
+ tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 68c77a62..d54514da 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -1,4 +1,5 @@
test_SCRIPTS += \
tests/ubifs_tools-tests/lib/common.sh \
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh \
- tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh \
+ tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh.in b/tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh.in
new file mode 100755
index 00000000..3c7ff2dc
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh.in
@@ -0,0 +1,144 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# For many kinds of flash, do following things
+# 1. mount UBIFS
+# 2. fsstress & powercut & unmount
+# 3. fsck UBIFS
+# 4. check UBIFS mounting result
+# Running time: 1h
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+function run_test()
+{
+ local simulator="$1";
+ local size="$2";
+ local peb_size="$3";
+ local page_size="$4";
+ local encryption=$5;
+
+ echo "======================================================================"
+ printf "%s" "$simulator: ${size}MiB PEB size ${peb_size}KiB"
+ if [ "$simulator" = "nandsim" ]; then
+ printf " %s" "page size ${page_size}Bytes"
+ fi
+ printf " $encryption\n"
+
+ if [ "$simulator" = "nandsim" ]; then
+ $TESTBINDIR/load_nandsim.sh "$size" "$peb_size" "$page_size" || echo "cannot load nandsim";
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ elif [ "$simulator" = "mtdram" ]; then
+ load_mtdram "$size" "$peb_size" || echo "cannot load mtdram"
+ mtdnum="$(find_mtd_device "$mtdram_patt")"
+ else
+ fatal "$simulator is not supported"
+ fi
+
+ flash_eraseall /dev/mtd$mtdnum
+ modprobe ubi mtd="$mtdnum,$page_size" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+ mount_ubifs $DEV $MNT || fatal "mount ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ encryption_set_key $MNT
+ fi
+
+ fsstress -d $MNT -l0 -p4 -n10000 &
+ sleep $((RANDOM % 120))
+ powercut
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ fsck.ubifs -a $DEV 2>&1 > $LOG_FILE
+ res=$?
+ cat $LOG_FILE
+ if [[ $res != $FSCK_OK ]]
+ then
+ # Powercut during layout_leb_in_gaps may change index LEBs
+ # without updating LPT.
+ log=`cat $LOG_FILE | grep "Inconsistent properties" | grep "is_idx 1"`
+ if [[ "$log" == "" ]]; then
+ fatal "fsck fail $res"
+ fi
+ if [[ $res != $FSCK_NONDESTRUCT ]]; then
+ fatal "fsck fail $res"
+ fi
+ fi
+
+ dmesg -c > /dev/null # powercut could reproduce error messages
+
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_set_key $MNT
+ fi
+
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+ check_err_msg
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+
+ check_err_msg
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r $simulator
+
+ echo "----------------------------------------------------------------------"
+}
+
+check_fsstress
+start_t=$(date +%s)
+echo "Do powercut+fsck+mount test in kinds of flashes"
+for simulator in "mtdram" "nandsim"; do
+ for encryption in "encrypted" "noencrypted"; do
+ run_test "$simulator" "16" "16" "512" $encryption
+ run_test "$simulator" "64" "16" "512" $encryption
+ run_test "$simulator" "128" "64" "2048" $encryption
+ run_test "$simulator" "256" "128" "2048" $encryption
+ run_test "$simulator" "512" "128" "2048" $encryption
+ run_test "$simulator" "1024" "512" "2048" $encryption
+ done
+done
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
Add run_all script to run all UBIFS fsck & mkfs testcases.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 1 +
tests/ubifs_tools-tests/Makemodule.am | 1 +
tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in | 65 +++++++++++++++++++++++
4 files changed, 68 insertions(+)
create mode 100755 tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in
diff --git a/.gitignore b/.gitignore
index ffbde6e5..673536e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,7 @@ tests/fs-tests/stress/fs_stress01.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
+tests/ubifs_tools-tests/ubifs_tools_run_all.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
diff --git a/configure.ac b/configure.ac
index 13974090..da1b0cfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -291,6 +291,7 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
+ tests/ubifs_tools-tests/ubifs_tools_run_all.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index f4d8a073..1715757e 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -1,5 +1,6 @@
test_SCRIPTS += \
tests/ubifs_tools-tests/lib/common.sh \
+ tests/ubifs_tools-tests/ubifs_tools_run_all.sh \
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh \
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh \
diff --git a/tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in b/tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in
new file mode 100755
index 00000000..a7caad04
--- /dev/null
+++ b/tests/ubifs_tools-tests/ubifs_tools_run_all.sh.in
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# Run all testcases under 'tests' directory
+
+function print_line()
+{
+ echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
+ echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
+}
+
+TESTBINDIR=@TESTBINDIR@
+
+print_line
+$TESTBINDIR/authentication_refuse.sh
+if [[ $? != 0 ]]; then
+ echo "authentication_refuse failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/powercut_fsck_mount.sh
+if [[ $? != 0 ]]; then
+ echo "powercut_fsck_mount failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/cycle_corrupted_fsck_fault_inject.sh
+if [[ $? != 0 ]]; then
+ echo "cycle_corrupted_fsck_fault_inject failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/fsck_bad_image.sh
+if [[ $? != 0 ]]; then
+ echo "fsck_bad_image failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/random_corrupted_fsck.sh
+if [[ $? != 0 ]]; then
+ echo "random_corrupted_fsck failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/cycle_powercut_mount_fsck.sh
+if [[ $? != 0 ]]; then
+ echo "cycle_powercut_mount_fsck failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/cycle_mount_fsck_check.sh
+if [[ $? != 0 ]]; then
+ echo "cycle_mount_fsck_check failed"
+ exit 1
+fi
+print_line
+$TESTBINDIR/build_fs_from_dir.sh
+if [[ $? != 0 ]]; then
+ echo "build_fs_from_dir failed"
+ exit 1
+fi
+
+exit 0
--
2.13.6
This is a preparation for adding testcases for fsck.ubifs and
mkfs.ubifs. Add some common functions, for example: powercut,
load_mtdram, mount_ubifs, encryption operations, etc.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
Makefile.am | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 2 +
tests/ubifs_tools-tests/lib/common.sh.in | 350 +++++++++++++++++++++++++++++++
5 files changed, 356 insertions(+), 1 deletion(-)
create mode 100644 tests/ubifs_tools-tests/Makemodule.am
create mode 100755 tests/ubifs_tools-tests/lib/common.sh.in
diff --git a/.gitignore b/.gitignore
index 3eb66c14..de0fce1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,6 +113,7 @@ tests/fs-tests/stress/fs_stress00.sh
tests/fs-tests/stress/fs_stress01.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
+tests/ubifs_tools-tests/lib/common.sh
#
# Files generated by autotools
diff --git a/Makefile.am b/Makefile.am
index 887ce938..0ebd45bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -63,6 +63,7 @@ include tests/jittertest/Makemodule.am
include tests/checkfs/Makemodule.am
include tests/fs-tests/Makemodule.am
include tests/mtd-tests/Makemodule.am
+include tests/ubifs_tools-tests/Makemodule.am
endif
if UNIT_TESTS
diff --git a/configure.ac b/configure.ac
index cf3a959e..d32541a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -289,7 +289,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/fs-tests/stress/fs_stress00.sh
tests/fs-tests/stress/fs_stress01.sh
tests/ubi-tests/runubitests.sh
- tests/ubi-tests/ubi-stress-test.sh])
+ tests/ubi-tests/ubi-stress-test.sh
+ tests/ubifs_tools-tests/lib/common.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
new file mode 100644
index 00000000..265c9cc7
--- /dev/null
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -0,0 +1,2 @@
+test_SCRIPTS += \
+ tests/ubifs_tools-tests/lib/common.sh
diff --git a/tests/ubifs_tools-tests/lib/common.sh.in b/tests/ubifs_tools-tests/lib/common.sh.in
new file mode 100755
index 00000000..5a07ebcd
--- /dev/null
+++ b/tests/ubifs_tools-tests/lib/common.sh.in
@@ -0,0 +1,350 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Provide basic functions.
+
+UBI_NUM=0
+DEV=/dev/ubi0_0
+MNT=/mnt/test_file_system
+TMP_FILE=/tmp/ubifs_test_file
+LOG_FILE=/tmp/ubifs_log
+KEY_FILE=/tmp/key
+nandsim_patt="NAND simulator"
+mtdram_patt="mtdram test device"
+
+# fsck returning code
+FSCK_OK=0 # No errors
+FSCK_NONDESTRUCT=1 # File system errors corrected
+FSCK_REBOOT=2 # System should be rebooted
+FSCK_UNCORRECTED=4 # File system errors left uncorrected
+FSCK_ERROR=8 # Operational error
+FSCK_USAGE=16 # Usage or syntax error
+FSCK_CANCELED=32 # Aborted with a signal or ^
+FSCK_LIBRARY=128 # Shared library error
+
+function fatal()
+{
+ echo "Error: $1" 1>&2
+ exit 1
+}
+
+# All loaded modules(mtdram/nandsim/ubi/ubifs) won't be removed if errors
+# happen, it is useful to debug based on the UBIFS image.
+function cleanup_handler()
+{
+ local ret="$1"
+
+ if [ "$ret" == "0" ]; then
+ umount $MNT >/dev/null 2>&1 ||:
+ modprobe -r ubifs >/dev/null 2>&1 ||:
+ modprobe -r ubi >/dev/null 2>&1 ||:
+ modprobe -r nandsim >/dev/null 2>&1 ||:
+ modprobe -r mtdram >/dev/null 2>&1 ||:
+ rm -rf $MNT >/dev/null 2>&1 ||:
+ rm -f $TMP_FILE >/dev/null 2>&1 ||:
+ rm -f $KEY_FILE >/dev/null 2>&1 ||:
+ rm -f $LOG_FILE >/dev/null 2>&1 ||:
+ exit 0
+ else
+ exit 1
+ fi
+}
+trap 'cleanup_handler $?' EXIT
+trap 'cleanup_handler 1' HUP PIPE INT QUIT TERM
+
+function find_mtd_device()
+{
+ printf "%s" "$(grep "$1" /proc/mtd | sed -e "s/^mtd\([0-9]\+\):.*$/\1/")"
+}
+
+function powercut()
+{
+ dmesg -c > /dev/null
+ echo 1 > /sys/kernel/debug/ubifs/tst_recovery;
+ while true;
+ do
+ msg=`dmesg -c | grep "Power cut emulated"`;
+ if [[ "$msg" != "" ]];
+ then
+ break;
+ fi
+ ro_error=`cat /sys/kernel/debug/ubifs/ubi${UBI_NUM}_0/ro_error`
+ if [[ $ro_error != 0 ]]; then
+ break;
+ fi
+ done
+ echo 0 > /sys/kernel/debug/ubifs/tst_recovery
+}
+
+# Load mtdram with specified size and PEB size
+# Usage: load_mtdram <flash size> <PEB size>
+# 1. Flash size is specified in MiB
+# 2. PEB size is specified in KiB
+function load_mtdram()
+{
+ local size="$1"; shift
+ local peb_size="$1"; shift
+
+ size="$(($size * 1024))"
+ modprobe mtdram total_size="$size" erase_size="$peb_size"
+}
+
+function check_fsstress()
+{
+ cmd=`fsstress | grep "op_name"`
+ if ! [[ "$cmd" =~ "op_name" ]]; then
+ fatal "fsstress is not found"
+ fi
+}
+
+# Check error messages
+function check_err_msg()
+{
+ msg=`dmesg | grep -E "dump_stack|UBIFS error|switched to read-only mode"`;
+ if [[ "$msg" != "" ]]
+ then
+ dmesg
+ fatal "error message detected!"
+ fi
+ dmesg -c > /dev/null
+}
+
+# Iterate all files under certain dir
+# $1: dir
+# $2: "md5sum" means that need record md5 for regular file, otherwise don't record md5 for regular file
+function read_dir() {
+ for file in `ls -a $1`
+ do
+ cur_f=$1"/"$file
+ if [ -b $cur_f ]
+ then
+ major=`stat -c %t $cur_f`
+ minor=`stat -c %T $cur_f`
+ echo "block $cur_f $major $minor" >> $TMP_FILE
+ elif [ -c $cur_f ]
+ then
+ major=`stat -c %t $cur_f`
+ minor=`stat -c %T $cur_f`
+ echo "char $cur_f $major $minor" >> $TMP_FILE
+ elif [ -L $cur_f ]
+ then
+ link=`stat -c %N $cur_f`
+ echo "symlink $cur_f $link" >> $TMP_FILE
+ elif [ -S $cur_f ]
+ then
+ echo "sock $cur_f" >> $TMP_FILE
+ elif [ -p $cur_f ]
+ then
+ echo "fifo $cur_f" >> $TMP_FILE
+ elif [ -f $cur_f ]
+ then
+ sz=`stat -c %s $cur_f`
+ if [[ "$2" != "md5sum" ]]; then
+ echo "reg $cur_f $sz" >> $TMP_FILE
+ else
+ md5=`md5sum $cur_f | awk '{print $1}'`
+ echo "reg $cur_f $md5 $sz" >> $TMP_FILE
+ fi
+ elif [ -d $cur_f ]
+ then
+ if [[ $file != '.' && $file != '..' ]]
+ then
+ echo "dir $cur_f" >> $TMP_FILE
+ read_dir $1"/"$file $2
+ fi
+ else
+ fatal "record unknown file type $cur_f"
+ fi
+ done
+}
+
+# Check whether there are files lost after fsck/mkfs
+# $1: "md5sum" means need record md5 for regular file, otherwise don't check md5 for regular file
+function parse_dir()
+{
+ while read line
+ do
+ array=(${line//\ / });
+ f_type=${array[0]};
+ cur_f=${array[1]};
+ cur_info=""
+ if [[ "$f_type" =~ "block" ]]
+ then
+ major=`stat -c %t $cur_f`
+ minor=`stat -c %T $cur_f`
+ cur_info="block $cur_f $major $minor"
+ elif [[ "$f_type" =~ "char" ]]
+ then
+ major=`stat -c %t $cur_f`
+ minor=`stat -c %T $cur_f`
+ cur_info="char $cur_f $major $minor"
+ elif [[ "$f_type" =~ "symlink" ]]
+ then
+ link=`stat -c %N $cur_f`
+ cur_info="symlink $cur_f $link"
+ elif [[ "$f_type" =~ "sock" ]]
+ then
+ cur_info="sock $cur_f"
+ elif [[ "$f_type" =~ "fifo" ]]
+ then
+ cur_info="fifo $cur_f"
+ elif [[ "$f_type" =~ "reg" ]]
+ then
+ sz=`stat -c %s $cur_f`
+ if [[ "$1" != "md5sum" ]]; then
+ cur_info="reg $cur_f $sz"
+ else
+ md5=`md5sum $cur_f | awk '{print $1}'`
+ cur_info="reg $cur_f $md5 $sz"
+ fi
+ elif [[ "$f_type" =~ "dir" ]]
+ then
+ cur_info="dir $cur_f"
+ else
+ fatal "parse unknown file type $cur_f"
+ fi
+ if [[ "$cur_info" != "$line" ]]
+ then
+ fatal "current info $cur_info, but expect $line"
+ fi
+ done < $TMP_FILE
+}
+
+function authentication()
+{
+ keyctl clear @s
+ res=$?
+ if [[ $res != 0 ]]; then
+ fatal "keyctl is not found"
+ fi
+ keyctl add logon ubifs:foo 12345678901234567890123456789012 @s
+}
+
+function encryption_gen_key()
+{
+ # CONFIG_FS_ENCRYPTION=y
+ head -c 64 /dev/urandom > $KEY_FILE
+ cmd=`fscryptctl -h | grep "set_policy"`
+ if ! [[ "$cmd" =~ "set_policy" ]]; then
+ fatal "fscryptctl is not found"
+ fi
+}
+
+function encryption_set_key()
+{
+ mnt=$1
+ # https://github.com/google/fscryptctl
+ key=$(fscryptctl add_key $mnt < $KEY_FILE)
+ fscryptctl set_policy $key $mnt
+ #fscryptctl get_policy $mnt
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "set encryption policy failed"
+ fi
+}
+
+function mount_ubifs()
+{
+ local dev=$1;
+ local mnt=$2;
+ local auth=$3;
+ local noatime=$4;
+ local option="";
+ if [[ "$noatime" == "noatime" ]]; then
+ option="-o noatime"
+ fi
+ if [[ "$auth" == "authentication" ]]; then
+ authentication
+ if [[ "$option" == "" ]]; then
+ option="-o auth_key=ubifs:foo,auth_hash_name=sha256"
+ else
+ option="$option,auth_key=ubifs:foo,auth_hash_name=sha256"
+ fi
+ fi
+ mount -t ubifs $option $dev $mnt
+}
+
+function enable_chkfs()
+{
+ echo 1 > /sys/kernel/debug/ubifs/chk_fs
+ echo 1 > /sys/kernel/debug/ubifs/chk_general
+ echo 1 > /sys/kernel/debug/ubifs/chk_index
+ echo 1 > /sys/kernel/debug/ubifs/chk_lprops
+ echo 1 > /sys/kernel/debug/ubifs/chk_orphans
+}
+
+function disable_chkfs()
+{
+ echo 0 > /sys/kernel/debug/ubifs/chk_fs
+ echo 0 > /sys/kernel/debug/ubifs/chk_general
+ echo 0 > /sys/kernel/debug/ubifs/chk_index
+ echo 0 > /sys/kernel/debug/ubifs/chk_lprops
+ echo 0 > /sys/kernel/debug/ubifs/chk_orphans
+}
+
+function inject_mem_err()
+{
+ # CONFIG_FAILSLAB=y
+ # CONFIG_FAIL_PAGE_ALLOC=y
+ local pid=$1;
+
+ if ! [ -f /sys/kernel/debug/failslab/probability ]; then
+ fatal "failslab is not enabled, injection failed"
+ fi
+ if ! [ -f /sys/kernel/debug/fail_page_alloc/probability ]; then
+ fatal "fail_page_alloc is not enabled, injection failed"
+ fi
+
+ echo 1 > /proc/$pid/make-it-fail
+
+ echo Y > /sys/kernel/debug/failslab/task-filter
+ echo 1 > /sys/kernel/debug/failslab/probability # 1% failure
+ echo 10000 > /sys/kernel/debug/failslab/times
+ echo 1 > /sys/kernel/debug/failslab/verbose
+ echo N > /sys/kernel/debug/failslab/ignore-gfp-wait
+
+ echo Y > /sys/kernel/debug/fail_page_alloc/task-filter
+ echo 1 > /sys/kernel/debug/fail_page_alloc/probability
+ echo 10000 > /sys/kernel/debug/fail_page_alloc/times
+ echo 0 > /sys/kernel/debug/fail_page_alloc/verbose
+ echo N > /sys/kernel/debug/fail_page_alloc/ignore-gfp-wait
+}
+
+function cancel_mem_err()
+{
+ echo 0 > /sys/kernel/debug/failslab/probability
+ echo 0 > /sys/kernel/debug/failslab/times
+ echo 0 > /sys/kernel/debug/failslab/verbose
+ echo N > /sys/kernel/debug/failslab/task-filter
+ echo Y > /sys/kernel/debug/failslab/ignore-gfp-wait
+
+ echo 0 > /sys/kernel/debug/fail_page_alloc/probability
+ echo 0 > /sys/kernel/debug/fail_page_alloc/times
+ echo 1 > /sys/kernel/debug/fail_page_alloc/verbose
+ echo N > /sys/kernel/debug/fail_page_alloc/task-filter
+ echo Y > /sys/kernel/debug/fail_page_alloc/ignore-gfp-wait
+}
+
+function inject_io_err()
+{
+ if ! [ -f /sys/kernel/debug/ubi/ubi$UBI_NUM/tst_emulate_io_failures ]; then
+ fatal "tst_emulate_io_failures is not enabled, skip injection"
+ fi
+
+ echo 1 > /sys/kernel/debug/ubi/ubi$UBI_NUM/tst_emulate_io_failures
+}
+
+function cancel_io_err()
+{
+ echo 0 > /sys/kernel/debug/ubi/ubi$UBI_NUM/tst_emulate_io_failures
+}
+
+if ! [ -d $MNT ]; then
+ mkdir -p $MNT
+fi
+
+modprobe ubi || fatal "common.sh: cannot load ubi"
+modprobe ubifs || fatal "common.sh: cannot load ubifs"
+modprobe -r ubifs
+modprobe -r ubi
--
2.13.6
This is the 9/18 step of fsck. Check and handle unreachable files, the
checking rule is same as rebuild mode which has been implemented in
file_is_reachable, but the methods of handling are different:
1. Move unreachable regular file into disconnected list, let subsequent
steps to handle them with lost+found.
2. Delete unreachable non-regular file.
3. Delete unreachable directory entries.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_files.c | 57 ++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/extract_files.c | 13 ++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 9 ++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 +-
ubifs-utils/fsck.ubifs/problem.c | 14 +++++++++
5 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index c5c606e1..2be96193 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
#include "linux_err.h"
#include "bitops.h"
@@ -442,3 +443,59 @@ int handle_invalid_files(struct ubifs_info *c)
return 0;
}
+
+/**
+ * handle_dentry_tree - Handle unreachable dentries and files.
+ * @c: UBIFS file-system description object
+ *
+ * This function iterates all directory entries and remove those unreachable
+ * ones. If file has no directory entries, it becomes unreachable:
+ * 1. If the unreachable file has non-regular type, delete it;
+ * 2. If the unreachable file has regular type, move it into the
+ * @FSCK(c)->disconnected_files.
+ * 'Unreachable' means that a directory entry can not be searched from '/'.
+ *
+ * Returns zero in case of success, a negative error code in case of failure.
+ */
+int handle_dentry_tree(struct ubifs_info *c)
+{
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
+ LIST_HEAD(unreachable);
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ /*
+ * Since all xattr files are already attached to corresponding
+ * host file, there are only non-xattr files in the file tree.
+ */
+ ubifs_assert(c, !file->ino.is_xattr);
+ if (!file_is_reachable(c, file, tree))
+ list_add(&file->list, &unreachable);
+ }
+
+ while (!list_empty(&unreachable)) {
+ file = list_entry(unreachable.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ if (S_ISREG(file->ino.mode)) {
+ /*
+ * Move regular type unreachable file into the
+ * @FSCK(c)->disconnected_files.
+ */
+ list_add(&file->list, &FSCK(c)->disconnected_files);
+ rb_erase(&file->rb, tree);
+ } else {
+ /* Delete non-regular type unreachable file. */
+ int err = delete_file(c, file);
+ if (err)
+ return err;
+ rb_erase(&file->rb, tree);
+ kfree(file);
+ }
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index 51b83b82..b24445be 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -1247,6 +1247,15 @@ retry:
dent_node = list_entry(path_list.next,
struct scanned_dent_node, list);
+ handle_invalid_file(c, DENTRY_IS_UNREACHABLE,
+ dent_node->file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum,
+ dent_node->header.offs);
+ if (err)
+ return err;
+ }
dbg_fsck("remove unreachable dentry %s, in %s",
c->encrypted && !file->ino.is_xattr ?
"<encrypted>" : dent_node->name, c->dev_name);
@@ -1260,6 +1269,10 @@ retry:
}
if (!rb_first(&file->dent_nodes)) {
+ if (S_ISREG(file->ino.mode))
+ handle_invalid_file(c, FILE_IS_DISCONNECTED, file, NULL);
+ else
+ handle_invalid_file(c, FILE_HAS_NO_DENT, file, NULL);
dbg_fsck("file %lu is unreachable, in %s", file->inum, c->dev_name);
return false;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 4b1b35b0..fd4890a0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -453,6 +453,14 @@ static int do_fsck(void)
goto free_used_lebs;
}
+ log_out(c, "Check and handle unreachable files");
+ err = handle_dentry_tree(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files;
+ }
+
+free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
kfree(FSCK(c)->used_lebs);
@@ -495,6 +503,7 @@ int main(int argc, char *argv[])
* Step 6: Traverse tnc and construct files
* Step 7: Update files' size
* Step 8: Check and handle invalid files
+ * Step 9: Check and handle unreachable files
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 34d300b2..ba4771a3 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -43,7 +43,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_HAS_0_NLINK_INODE, FILE_HAS_INCONSIST_TYPE, FILE_HAS_TOO_MANY_DENT,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
- FILE_ROOT_HAS_DENT };
+ FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
@@ -318,5 +318,6 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c);
int traverse_tnc_and_construct_files(struct ubifs_info *c);
void update_files_size(struct ubifs_info *c);
int handle_invalid_files(struct ubifs_info *c);
+int handle_dentry_tree(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 9222cba4..0395a34f 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -58,6 +58,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Encrypted file has no encryption information"}, // FILE_HAS_NO_ENCRYPT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"}, // FILE_IS_DISCONNECTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
};
static const char *get_question(const struct fsck_problem *problem,
@@ -84,6 +85,7 @@ static const char *get_question(const struct fsck_problem *problem,
case XATTR_HAS_WRONG_HOST:
case FILE_HAS_NO_ENCRYPT:
case FILE_ROOT_HAS_DENT:
+ case DENTRY_IS_UNREACHABLE:
return "Delete it?";
case FILE_HAS_INCONSIST_TYPE:
case FILE_HAS_TOO_MANY_DENT:
@@ -198,6 +200,18 @@ static void print_problem(const struct ubifs_info *c,
host->ino.is_xattr ? "(xattr)" : "");
break;
}
+ case DENTRY_IS_UNREACHABLE:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu, unreachable dentry %s, type %s%s",
+ problem->desc, ifp->file->inum,
+ c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
+ ubifs_get_type_name(dent_node->type),
+ key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6
Inject random corruption on UBIFS image by writting random data on
kinds of mtd devices (eg. nand, nor), check the consistency of UBIFS
after fsck.
This testcase simulates random bad UBIFS image caused by hardware
exceptions(eg. ecc uncorrectable, unwritten), and makes sure that
fsck.ubifs could make UBIFS be consistent after repairing UBIFS
image.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/random_corrupted_fsck.sh.in | 206 +++++++++++++++++++++
4 files changed, 211 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh.in
diff --git a/.gitignore b/.gitignore
index 1d150f7a..203f01f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,6 +119,7 @@ tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
+tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 1a7174a5..545fcc41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -295,7 +295,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
- tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh])
+ tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
+ tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 2c190e2b..9881ebbe 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -4,4 +4,5 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh \
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh \
- tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh \
+ tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh.in b/tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh.in
new file mode 100755
index 00000000..64760c94
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh.in
@@ -0,0 +1,206 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# For many kinds of flash, do following things
+# 1. mount UBIFS
+# 2. fsstress && unmount
+# 3. inject corruption into UBIFS image randomly
+# 3. fsck UBIFS
+# 4. check UBIFS mounting result
+# Running time: 1h
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+function run_test()
+{
+ local simulator="$1";
+ local size="$2";
+ local peb_size="$3";
+ local page_size="$4";
+ local encryption=$5;
+
+ echo "======================================================================"
+ printf "%s" "$simulator: ${size}MiB PEB size ${peb_size}KiB"
+ if [ "$simulator" = "nandsim" ]; then
+ printf " %s" "page size ${page_size}Bytes"
+ fi
+ printf " $encryption\n"
+
+ if [ "$simulator" = "nandsim" ]; then
+ $TESTBINDIR/load_nandsim.sh "$size" "$peb_size" "$page_size" || echo "cannot load nandsim";
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ elif [ "$simulator" = "mtdram" ]; then
+ load_mtdram "$size" "$peb_size" || echo "cannot load mtdram"
+ mtdnum="$(find_mtd_device "$mtdram_patt")"
+ else
+ fatal "$simulator is not supported"
+ fi
+
+ flash_eraseall /dev/mtd$mtdnum
+ modprobe ubi mtd="$mtdnum,$page_size" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+ mount_ubifs $DEV $MNT || fatal "mount ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ encryption_set_key $MNT
+ fi
+
+ fsstress -d $MNT -l0 -p4 -n10000 &
+
+ while true;
+ do
+ per=`df -Th | grep ubifs | awk '{print $6}'`;
+ if [[ ${per%?} -gt 95 ]]; then
+ # Used > 95%
+ break;
+ fi
+ done
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ # injection
+ times=$((RANDOM % 10))
+ let times=$times+10
+ i=0
+ tot_peb=`cat /sys/class/ubi/ubi$UBI_NUM/total_eraseblocks`;
+
+ modprobe -r ubifs
+ modprobe -r ubi # Stop wear-leveling & erasing worker
+ while [[ $i -lt $times ]]
+ do
+ let i=$i+1;
+ peb=$((RANDOM % $tot_peb));
+ pg=`expr $peb_size \* 1024`;
+ peb_off=`expr $pg \* $peb`
+ pages=`expr $pg / $page_size`;
+ pg=`expr $pages - 2`;
+ pg=$((RANDOM % $pg));
+ pg_off=`expr $pg + 2`;
+ pg_start=`expr $pages \* $peb`;
+ pg=`expr $pg_start + $pg_off`;
+ vid_pg=`expr $pg_start + 1`;
+ dd if=/dev/mtd$mtdnum of=$TMP_FILE bs=$page_size skip=$vid_pg count=1 2>/dev/null;
+ content=`cat $TMP_FILE | grep UBI!`; # vid header magic
+ if [[ "$content" == "" ]]; then
+ # Skip free PEB, otherwise data could be overwritten in UBIFS
+ continue;
+ fi
+ if [[ $((RANDOM % 2)) == 0 ]]; then
+ # Corrupts 1 page
+ dd if=/dev/urandom of=/dev/mtd$mtdnum bs=$page_size seek=$pg count=1;
+ else
+ # Erase 1 LEB, TNC points to an unmapped area
+ flash_erase /dev/mtd$mtdnum $peb_off 1
+ fi
+ done
+ rm -f $TMP_FILE 2>/dev/null
+ sync
+
+ skip=0
+ modprobe ubi mtd="$mtdnum,$page_size"
+ ret=$?
+ if [[ $ret != 0 ]]
+ then
+ skip=1
+ echo "UBI layout volume is corrupted, skip"
+ fi
+
+ if [[ $skip == 0 ]]; then
+ modprobe ubifs || fatal "modprobe ubifs2 fail"
+ dmesg -c > /dev/null
+ fsck.ubifs -yb $DEV 2>&1 > $LOG_FILE
+ res=$?
+ cat $LOG_FILE
+ let "ret=$res&~$FSCK_NONDESTRUCT"
+ if [[ $ret != $FSCK_OK ]]
+ then
+ # Skip superblock error
+ log=`cat $LOG_FILE | grep "bad node at LEB 0:"`
+ if [[ "$log" != "" ]]
+ then
+ skip=1
+ echo "SB is corrupted, skip fsck & mounting"
+ else
+ fatal "fsck fail $res"
+ fi
+ fi
+
+ if [[ $skip == 0 ]]; then
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ # Ignore the encrypting error, root dir could be
+ # corrupted, the new root dir cannot be
+ # encrypted because it is not empty.
+ encryption_set_key $MNT 1
+ fi
+
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+ # check_err_msg is not suggested in this testcase, because
+ # ubi_io_read(triggered by wear_leveling_worker -> ubi_eba_copy_leb)
+ # could print stack if ecc uncorrectable errors are detected.
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+ fi
+ modprobe -r ubifs
+ modprobe -r ubi
+ fi
+ modprobe -r $simulator
+
+ echo "----------------------------------------------------------------------"
+}
+
+check_fsstress
+start_t=$(date +%s)
+echo "Do random_corrruption+fsck+mount test in kinds of flashes"
+for simulator in "mtdram" "nandsim"; do
+ for encryption in "encrypted" "noencrypted"; do
+ run_test "$simulator" "16" "16" "512" $encryption
+ run_test "$simulator" "64" "16" "512" $encryption
+ run_test "$simulator" "128" "64" "2048" $encryption
+ run_test "$simulator" "256" "128" "2048" $encryption
+ run_test "$simulator" "512" "128" "2048" $encryption
+ run_test "$simulator" "1024" "512" "2048" $encryption
+ done
+done
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
This is the 18/18 step of fsck. Do final committing, commit problem
fixing modifications(which are generated since step 14) to disk, and
clear %UBIFS_MST_DIRTY flag for master node.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 46 ++++++++++++++++++++++++-------------
1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 831a13db..b1b56194 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -432,28 +432,25 @@ void handle_error(const struct ubifs_info *c, int reason_set)
exit_code |= FSCK_ERROR;
}
-static int commit_fix_modifications(struct ubifs_info *c)
+static int commit_fix_modifications(struct ubifs_info *c, bool final_commit)
{
int err;
- if (exit_code & FSCK_NONDESTRUCT) {
+ if (final_commit) {
+ log_out(c, "Final committing");
+ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
+ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
+ /* Force UBIFS to do commit by setting @c->mounting. */
+ c->mounting = 1;
+ } else if (exit_code & FSCK_NONDESTRUCT) {
log_out(c, "Commit problem fixing modifications");
-
- /*
- * Force UBIFS to do commit by setting @c->mounting if changes
- * happen on disk. Committing is required once before allocating
- * new space(subsequent steps may need), because building lpt
- * could mark LEB(which holds stale data nodes) as unused, if
- * the LEB is overwritten by new data, old data won't be found
- * in next fsck run(assume that first fsck run is interrupted by
- * the powercut), which could affect the correctness of LEB
- * properties after replaying journal in the second fsck run.
- */
+ /* Force UBIFS to do commit by setting @c->mounting. */
c->mounting = 1;
}
+
err = ubifs_run_commit(c);
- if (exit_code & FSCK_NONDESTRUCT)
+ if (c->mounting)
c->mounting = 0;
return err;
@@ -511,7 +508,16 @@ static int do_fsck(void)
goto free_disconnected_files_2;
}
- err = commit_fix_modifications(c);
+ /*
+ * Committing is required once before allocating new space(subsequent
+ * steps may need), because building lpt could mark LEB(which holds
+ * stale data nodes) as unused, if the LEB is overwritten by new data,
+ * old data won't be found in the next fsck run(assume that first fsck
+ * run is interrupted by the powercut), which could affect the
+ * correctness of LEB properties after replaying journal in the second
+ * fsck run.
+ */
+ err = commit_fix_modifications(c, false);
if (err) {
exit_code |= FSCK_ERROR;
goto free_disconnected_files_2;
@@ -532,7 +538,7 @@ static int do_fsck(void)
}
if (list_empty(&FSCK(c)->disconnected_files))
- return err;
+ goto final_commit;
log_out(c, "Check and create lost+found");
err = check_and_create_lost_found(c);
@@ -548,6 +554,13 @@ static int do_fsck(void)
goto free_disconnected_files_2;
}
+final_commit:
+ err = commit_fix_modifications(c, true);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
+ return err;
+
free_disconnected_files_2:
destroy_file_list(c, &FSCK(c)->disconnected_files);
return err;
@@ -604,6 +617,7 @@ int main(int argc, char *argv[])
* Step 15: Check and create root dir
* Step 16: Check and create lost+found
* Step 17: Handle disconnected files
+ * Step 18: Do final committing
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
--
2.13.6
This is the 2/18 step of fsck. Replay journal, update TNC & LPT.
There could be following steps and possible errors:
Step 1. scan log LEB, get all bud LEBs
a. corrupted scanning data in log area: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
Step 2. scan bud LEBs, get all nodes
a. corrupted scanning data in bud LEB: danger mode and normal mode with
'yes' answer will drop bud LEB and set %FR_LPT_INCORRECT for lpt
status, other modes will exit.
Step 3. apply nodes, record latest isize into size_tree
Step 4. apply nodes, update TNC & LPT
a. corrupted data searched from TNC: skip node and set %FR_LPT_INCORRECT
lpt status for danger mode and normal mode with 'yes' answer, other
modes will exit.
b. corrupted index node read from TNC: danger mode with rebuild_fs and
normal mode with 'yes' answer will turn to rebuild filesystem, other
modes will exit.
c. corrupted lpt: Set %FR_LPT_CORRUPTED for lpt status. Ignore the
error.
d. incorrect lpt: Set %FR_LPT_INCORRECT for lpt status. Ignore the
error.
e. If lpt status is not empty, skip updating lpt.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 11 +++++---
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 5 ++--
ubifs-utils/fsck.ubifs/load_fs.c | 29 ++++++++++++++++++--
ubifs-utils/fsck.ubifs/problem.c | 39 ++++++++++++++++++++++-----
ubifs-utils/libubifs/lprops.c | 21 +++++++++++++++
ubifs-utils/libubifs/replay.c | 54 ++++++++++++++++++++++++++++++++-----
ubifs-utils/libubifs/tnc.c | 13 +++++++++
ubifs-utils/libubifs/ubifs.h | 11 +++++---
8 files changed, 160 insertions(+), 23 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 34641c89..3e7e7093 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -325,13 +325,17 @@ static bool fsck_can_ignore_failure(const struct ubifs_info *c,
return false;
}
-static const unsigned int reason_mapping_table[] = {};
+static const unsigned int reason_mapping_table[] = {
+ BUD_CORRUPTED, /* FR_H_BUD_CORRUPTED */
+ TNC_DATA_CORRUPTED, /* FR_H_TNC_DATA_CORRUPTED */
+};
-static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason)
+static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason,
+ void *priv)
{
ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
- return fix_problem(c, reason_mapping_table[reason]);
+ return fix_problem(c, reason_mapping_table[reason], priv);
}
static void signal_cancel(int sig)
@@ -426,6 +430,7 @@ int main(int argc, char *argv[])
/*
* Init: Read superblock
* Step 1: Read master & init lpt
+ * Step 2: Replay journal
*/
err = ubifs_load_filesystem(c);
if (err) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index cbe432c4..917f124e 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -37,7 +37,8 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
DANGER_MODE1, REBUILD_MODE, CHECK_MODE };
/* Types of inconsistent problems */
-enum { SB_CORRUPTED = 0, MST_CORRUPTED };
+enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
+ TNC_CORRUPTED, TNC_DATA_CORRUPTED };
struct scanned_file;
@@ -260,7 +261,7 @@ static inline const char *mode_name(const struct ubifs_info *c)
extern int exit_code;
/* problem.c */
-bool fix_problem(const struct ubifs_info *c, int problem_type);
+bool fix_problem(const struct ubifs_info *c, int problem_type, const void *priv);
/* load_fs.c */
int ubifs_load_filesystem(struct ubifs_info *c);
diff --git a/ubifs-utils/fsck.ubifs/load_fs.c b/ubifs-utils/fsck.ubifs/load_fs.c
index 036e307c..f45c4117 100644
--- a/ubifs-utils/fsck.ubifs/load_fs.c
+++ b/ubifs-utils/fsck.ubifs/load_fs.c
@@ -72,7 +72,7 @@ int ubifs_load_filesystem(struct ubifs_info *c)
err = ubifs_read_superblock(c);
if (err) {
if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED))
- fix_problem(c, SB_CORRUPTED);
+ fix_problem(c, SB_CORRUPTED, NULL);
exit_code |= FSCK_ERROR;
goto out_mounting;
}
@@ -103,7 +103,7 @@ int ubifs_load_filesystem(struct ubifs_info *c)
err = ubifs_read_master(c);
if (err) {
if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED)) {
- if (fix_problem(c, MST_CORRUPTED))
+ if (fix_problem(c, MST_CORRUPTED, NULL))
FSCK(c)->try_rebuild = true;
} else
exit_code |= FSCK_ERROR;
@@ -161,10 +161,34 @@ int ubifs_load_filesystem(struct ubifs_info *c)
c->superblock_need_write = 0;
}
+ log_out(c, "Replay journal");
+ err = ubifs_replay_journal(c);
+ if (err) {
+ unsigned int reason = get_failure_reason_callback(c);
+
+ clear_failure_reason_callback(c);
+ if (reason & FR_DATA_CORRUPTED) {
+ if (fix_problem(c, LOG_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ } else if (reason & FR_TNC_CORRUPTED) {
+ if (fix_problem(c, TNC_CORRUPTED, NULL))
+ FSCK(c)->try_rebuild = true;
+ } else {
+ ubifs_assert(c, reason == 0);
+ exit_code |= FSCK_ERROR;
+ }
+ goto out_journal;
+ }
+
+ /* Calculate 'min_idx_lebs' after journal replay */
+ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
c->mounting = 0;
return 0;
+out_journal:
+ destroy_journal(c);
out_lpt:
ubifs_lpt_free(c, 0);
out_master:
@@ -188,6 +212,7 @@ out_free:
void ubifs_destroy_filesystem(struct ubifs_info *c)
{
+ destroy_journal(c);
free_wbufs(c);
ubifs_lpt_free(c, 0);
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 1af66632..9df2c2ae 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -37,12 +37,39 @@ struct fsck_problem {
static const struct fsck_problem problem_table[] = {
{0, "Corrupted superblock"}, // SB_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted master node"}, // MST_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted log area"}, // LOG_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted bud LEB"}, // BUD_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted index node"}, // TNC_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data searched from TNC"}, // TNC_DATA_CORRUPTED
};
+static const char *get_question(const struct fsck_problem *problem,
+ int problem_type)
+{
+ if (problem->flags & PROBLEM_NEED_REBUILD)
+ return "Rebuild filesystem?";
+
+ switch (problem_type) {
+ case BUD_CORRUPTED:
+ return "Drop bud?";
+ case TNC_DATA_CORRUPTED:
+ return "Drop it?";
+ }
+
+ return "Fix it?";
+}
+
static void print_problem(const struct ubifs_info *c,
- const struct fsck_problem *problem)
+ const struct fsck_problem *problem, int problem_type,
+ const void *priv)
{
- log_out(c, "problem: %s", problem->desc);
+ if (problem_type == BUD_CORRUPTED) {
+ const struct ubifs_bud *bud = (const struct ubifs_bud *)priv;
+
+ log_out(c, "problem: %s %d:%d %s", problem->desc, bud->lnum,
+ bud->start, dbg_jhead(bud->jhead));
+ } else
+ log_out(c, "problem: %s", problem->desc);
}
static void fatal_error(const struct ubifs_info *c,
@@ -59,17 +86,17 @@ static void fatal_error(const struct ubifs_info *c,
* fix_problem - whether fixing the inconsistent problem
* @c: UBIFS file-system description object
* @problem_type: the type of inconsistent problem
+ * @priv: private data for problem instance
*
* This function decides to fix/skip the inconsistent problem or abort the
* program according to @problem_type, returns %true if the problem should
* be fixed, returns %false if the problem will be skipped.
*/
-bool fix_problem(const struct ubifs_info *c, int problem_type)
+bool fix_problem(const struct ubifs_info *c, int problem_type, const void *priv)
{
bool ans, ask = true, def_y = true;
const struct fsck_problem *problem = &problem_table[problem_type];
- const char *question = (problem->flags & PROBLEM_NEED_REBUILD) ?
- "Rebuild filesystem?" : "Fix it?";
+ const char *question = get_question(problem, problem_type);
ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
@@ -88,7 +115,7 @@ bool fix_problem(const struct ubifs_info *c, int problem_type)
(FSCK(c)->mode == DANGER_MODE0 || FSCK(c)->mode == DANGER_MODE1))
ask = false;
- print_problem(c, problem);
+ print_problem(c, problem, problem_type, priv);
ans = def_y;
if (FSCK(c)->mode == NORMAL_MODE) {
printf("%s[%d] (%s%s)", c->program_name, getpid(),
diff --git a/ubifs-utils/libubifs/lprops.c b/ubifs-utils/libubifs/lprops.c
index 84cdb353..a7a23053 100644
--- a/ubifs-utils/libubifs/lprops.c
+++ b/ubifs-utils/libubifs/lprops.c
@@ -656,14 +656,24 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
int err = 0, flags;
const struct ubifs_lprops *lp;
+ if (!test_lpt_valid_callback(c, lnum, LPROPS_NC, LPROPS_NC, LPROPS_NC,
+ LPROPS_NC))
+ return 0;
+
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
goto out;
}
+ if (!test_lpt_valid_callback(c, lnum, lp->free, lp->dirty, free, dirty))
+ goto out;
+
flags = (lp->flags | flags_set) & ~flags_clean;
lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
if (IS_ERR(lp))
@@ -695,14 +705,25 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
int err = 0, flags;
const struct ubifs_lprops *lp;
+ if (!test_lpt_valid_callback(c, lnum, LPROPS_NC, LPROPS_NC, LPROPS_NC,
+ LPROPS_NC))
+ return 0;
+
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
goto out;
}
+ if (!test_lpt_valid_callback(c, lnum, lp->free, lp->dirty, free,
+ lp->dirty + dirty))
+ goto out;
+
flags = (lp->flags | flags_set) & ~flags_clean;
lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
if (IS_ERR(lp))
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index 0ed12e3a..0895f069 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -94,11 +94,18 @@ static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
const struct ubifs_lprops *lp;
int err = 0, dirty;
+ if (!test_lpt_valid_callback(c, b->bud->lnum, LPROPS_NC, LPROPS_NC,
+ LPROPS_NC, LPROPS_NC))
+ return 0;
+
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
goto out;
}
@@ -140,6 +147,10 @@ static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b)
b->bud->lnum, lp->free, lp->dirty, b->free,
b->dirty);
}
+ if (!test_lpt_valid_callback(c, b->bud->lnum, lp->free, lp->dirty,
+ b->free, dirty + b->dirty))
+ goto out;
+
lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty,
lp->flags | LPROPS_TAKEN, 0);
if (IS_ERR(lp)) {
@@ -766,6 +777,7 @@ out:
return err;
out_dump:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad node is at LEB %d:%d", lnum, snod->offs);
ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
ubifs_scan_destroy(sleb);
@@ -781,14 +793,24 @@ out_dump:
*/
static int replay_buds(struct ubifs_info *c)
{
- struct bud_entry *b;
+ struct bud_entry *b, *tmp_b;
int err;
unsigned long long prev_sqnum = 0;
- list_for_each_entry(b, &c->replay_buds, list) {
+ list_for_each_entry_safe(b, tmp_b, &c->replay_buds, list) {
err = replay_bud(c, b);
- if (err)
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED) &&
+ handle_failure_callback(c, FR_H_BUD_CORRUPTED, b->bud)) {
+ /* Set %FR_LPT_INCORRECT for lpt status. */
+ set_lpt_invalid_callback(c, FR_LPT_INCORRECT);
+ /* Skip replaying the bud LEB. */
+ list_del(&b->list);
+ kfree(b);
+ continue;
+ }
return err;
+ }
ubifs_assert(c, b->sqnum > prev_sqnum);
prev_sqnum = b->sqnum;
@@ -1062,6 +1084,7 @@ out:
return err;
out_dump:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "log error detected while replaying the log at LEB %d:%d",
lnum, offs + snod->offs);
ubifs_dump_node(c, snod->node, c->leb_size - snod->offs);
@@ -1086,11 +1109,20 @@ static int take_ihead(struct ubifs_info *c)
lp = ubifs_lpt_lookup_dirty(c, c->ihead_lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED) &&
+ can_ignore_failure_callback(c, FR_LPT_CORRUPTED))
+ err = 0;
goto out;
}
free = lp->free;
+ if (!test_lpt_valid_callback(c, c->ihead_lnum, LPROPS_NC, LPROPS_NC,
+ LPROPS_NC, LPROPS_NC)) {
+ err = free;
+ goto out;
+ }
+
lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
lp->flags | LPROPS_TAKEN, 0);
if (IS_ERR(lp)) {
@@ -1123,10 +1155,17 @@ int ubifs_replay_journal(struct ubifs_info *c)
if (free < 0)
return free; /* Error code */
- if (c->ihead_offs != c->leb_size - free) {
- ubifs_err(c, "bad index head LEB %d:%d", c->ihead_lnum,
- c->ihead_offs);
- return -EINVAL;
+ if (c->program_type != FSCK_PROGRAM_TYPE) {
+ /*
+ * Skip index head checking for fsck, it is hard to check it
+ * caused by possible corrupted/incorrect lpt, tnc updating
+ * will report error code if index tree is really corrupted.
+ */
+ if (c->ihead_offs != c->leb_size - free) {
+ ubifs_err(c, "bad index head LEB %d:%d", c->ihead_lnum,
+ c->ihead_offs);
+ return -EINVAL;
+ }
}
dbg_mnt("start replaying the journal");
@@ -1147,6 +1186,7 @@ int ubifs_replay_journal(struct ubifs_info *c)
* something went wrong and we cannot proceed mounting
* the file-system.
*/
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted",
lnum, 0);
err = -EINVAL;
diff --git a/ubifs-utils/libubifs/tnc.c b/ubifs-utils/libubifs/tnc.c
index 12c56e0a..cd1013d5 100644
--- a/ubifs-utils/libubifs/tnc.c
+++ b/ubifs-utils/libubifs/tnc.c
@@ -2402,9 +2402,22 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
xent = ubifs_tnc_next_ent(c, &key1, &nm);
if (IS_ERR(xent)) {
+ unsigned int reason;
+
err = PTR_ERR(xent);
if (err == -ENOENT)
break;
+
+ reason = get_failure_reason_callback(c);
+ if (reason & FR_DATA_CORRUPTED) {
+ test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED);
+ if (handle_failure_callback(c, FR_H_TNC_DATA_CORRUPTED, NULL)) {
+ /* Set %FR_LPT_INCORRECT for lpt status. */
+ set_lpt_invalid_callback(c, FR_LPT_INCORRECT);
+ /* Leave xattrs to be deleted by subsequent steps */
+ break;
+ }
+ }
kfree(pxent);
return err;
}
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 6f965555..ae812ff8 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1286,7 +1286,7 @@ struct ubifs_info {
bool (*can_ignore_failure_cb)(const struct ubifs_info *c,
unsigned int reason);
bool (*handle_failure_cb)(const struct ubifs_info *c,
- unsigned int reason);
+ unsigned int reason, void *priv);
};
extern atomic_long_t ubifs_clean_zn_cnt;
@@ -1542,6 +1542,11 @@ enum {
FR_LPT_CORRUPTED = 4, /* LPT is corrupted */
FR_LPT_INCORRECT = 8 /* Space statistics are wrong */
};
+/* Partial failure reasons in common libs, which are handled by fsck. */
+enum {
+ FR_H_BUD_CORRUPTED = 0, /* Bud LEB is corrupted */
+ FR_H_TNC_DATA_CORRUPTED, /* Data searched from TNC is corrupted */
+};
/* Callback functions for failure(which can be handled by fsck) happens. */
static inline void set_failure_reason_callback(const struct ubifs_info *c,
unsigned int reason)
@@ -1596,10 +1601,10 @@ static inline bool can_ignore_failure_callback(const struct ubifs_info *c,
return false;
}
static inline bool handle_failure_callback(const struct ubifs_info *c,
- unsigned int reason)
+ unsigned int reason, void *priv)
{
if (c->handle_failure_cb)
- return c->handle_failure_cb(c, reason);
+ return c->handle_failure_cb(c, reason, priv);
return false;
}
--
2.13.6
?? 2024/6/7 12:24, Zhihao Cheng д??:
> Introduction
> ============
>
[...]
I found some of patches are not displayed in the linux-mtd patchwork, so
I send an attachment(all_patches.tar.gz) that contains all patches. Or
you can download the patches from
https://lore.kernel.org/all/[email protected]/T/#t,
in which, the order of patches is mess.
This is the 9/12 step of repairing. Construct TNC according to scanned
files, and write TNC on flash, just like mkfs does.
Building TNC can effectively solve many failed mounting problems caused
by bad TNC (eg. bad node pointed by TNC, bad key order in znode, etc.).
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/rebuild_fs.c | 283 +++++++++++++++++++++++++++++++++---
1 file changed, 262 insertions(+), 21 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 62bd4128..e1d1957f 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -34,6 +34,29 @@ struct scanned_info {
struct rb_root del_dents;
};
+/**
+ * struct idx_entry - index entry.
+ * @list: link in the list index entries for building index tree
+ * @key: key
+ * @name: directory entry name used for sorting colliding keys by name
+ * @lnum: LEB number
+ * @offs: offset
+ * @len: length
+ *
+ * The index is recorded as a linked list which is sorted and used to create
+ * the bottom level of the on-flash index tree. The remaining levels of the
+ * index tree are each built from the level below.
+ */
+struct idx_entry {
+ struct list_head list;
+ union ubifs_key key;
+ char *name;
+ int name_len;
+ int lnum;
+ int offs;
+ int len;
+};
+
static int init_rebuild_info(struct ubifs_info *c)
{
int err;
@@ -146,6 +169,17 @@ static int insert_or_update_ino_node(struct ubifs_info *c,
return 0;
}
+static int namecmp(const char *a, int la, const char *b, int lb)
+{
+ int cmp, len = min(la, lb);
+
+ cmp = memcmp(a, b, len);
+ if (cmp)
+ return cmp;
+
+ return la - lb;
+}
+
/**
* insert_or_update_dent_node - insert or update dentry node.
* @c: UBIFS file-system description object
@@ -160,7 +194,7 @@ static int insert_or_update_dent_node(struct ubifs_info *c,
struct scanned_dent_node *new_dent,
struct rb_root *tree)
{
- int cmp, nlen;
+ int cmp;
struct scanned_dent_node *dent_node, *old_dent_node = NULL;
struct rb_node **p, *parent = NULL;
@@ -174,9 +208,8 @@ static int insert_or_update_dent_node(struct ubifs_info *c,
} else if (cmp > 0) {
p = &(*p)->rb_right;
} else {
- nlen = min(new_dent->nlen, dent_node->nlen);
- cmp = strncmp(new_dent->name, dent_node->name, nlen) ? :
- new_dent->nlen - dent_node->nlen;
+ cmp = namecmp(new_dent->name, new_dent->nlen,
+ dent_node->name, dent_node->nlen);
if (cmp < 0) {
p = &(*p)->rb_left;
} else if (cmp > 0) {
@@ -424,7 +457,7 @@ static struct scanned_dent_node *
lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si,
struct scanned_dent_node *target)
{
- int cmp, nlen;
+ int cmp;
struct scanned_dent_node *dent_node;
struct rb_node *p;
@@ -437,9 +470,8 @@ lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si,
} else if (cmp > 0) {
p = p->rb_right;
} else {
- nlen = min(target->nlen, dent_node->nlen);
- cmp = strncmp(target->name, dent_node->name, nlen) ? :
- target->nlen - dent_node->nlen;
+ cmp = namecmp(target->name, target->nlen,
+ dent_node->name, dent_node->nlen);
if (cmp < 0) {
p = p->rb_left;
} else if (cmp > 0) {
@@ -885,9 +917,12 @@ static const char *get_file_name(struct ubifs_info *c, struct scanned_file *file
return name;
}
-static void parse_node_location(struct ubifs_info *c, struct scanned_node *sn)
+static int parse_node_info(struct ubifs_info *c, struct scanned_node *sn,
+ union ubifs_key *key, char *name, int name_len,
+ struct list_head *idx_list, int *idx_cnt)
{
int lnum, pos;
+ struct idx_entry *e;
lnum = sn->lnum - c->main_first;
pos = sn->offs + ALIGN(sn->len, 8);
@@ -895,11 +930,184 @@ static void parse_node_location(struct ubifs_info *c, struct scanned_node *sn)
set_bit(lnum, FSCK(c)->rebuild->used_lebs);
FSCK(c)->rebuild->lpts[lnum].end = max_t(int,
FSCK(c)->rebuild->lpts[lnum].end, pos);
+
+ if (idx_cnt == NULL)
+ /* Skip truncation node. */
+ return 0;
+
+ e = kmalloc(sizeof(struct idx_entry), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ key_copy(c, key, &e->key);
+ e->name = name;
+ e->name_len = name_len;
+ e->lnum = sn->lnum;
+ e->offs = sn->offs;
+ e->len = sn->len;
+ list_add_tail(&e->list, idx_list);
+ *idx_cnt = *idx_cnt + 1;
+
+ return 0;
+}
+
+static int add_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
+ union ubifs_key *key, int child_cnt,
+ struct idx_entry *e)
+{
+ int err, lnum, offs, len;
+
+ len = ubifs_idx_node_sz(c, child_cnt);
+ ubifs_prepare_node(c, idx, len, 0);
+
+ err = reserve_space(c, len, &lnum, &offs);
+ if (err)
+ return err;
+
+ copy_node_data(c, idx, offs, len);
+
+ c->calc_idx_sz += ALIGN(len, 8);
+
+ /* The last index node written will be the root */
+ c->zroot.lnum = lnum;
+ c->zroot.offs = offs;
+ c->zroot.len = len;
+
+ key_copy(c, key, &e->key);
+ e->lnum = lnum;
+ e->offs = offs;
+ e->len = len;
+
+ return err;
+}
+
+static int cmp_idx(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ int cmp;
+ struct ubifs_info *c = priv;
+ struct idx_entry *ia, *ib;
+
+ if (a == b)
+ return 0;
+
+ ia = list_entry(a, struct idx_entry, list);
+ ib = list_entry(b, struct idx_entry, list);
+
+ cmp = keys_cmp(c, &ia->key, &ib->key);
+ if (cmp)
+ return cmp;
+
+ return namecmp(ia->name, ia->name_len, ib->name, ib->name_len);
+}
+
+/**
+ * build_tnc - construct TNC and write it into flash.
+ * @c: UBIFS file-system description object
+ * @lower_idxs: leaf entries of TNC
+ * @lower_cnt: the count of @lower_idxs
+ *
+ * This function builds TNC according to @lower_idxs and writes all index
+ * nodes into flash.
+ */
+static int build_tnc(struct ubifs_info *c, struct list_head *lower_idxs,
+ int lower_cnt)
+{
+ int i, j, err, upper_cnt, child_cnt, idx_sz, level = 0;
+ struct idx_entry *pe, *tmp_e, *e = NULL;
+ struct ubifs_idx_node *idx;
+ struct ubifs_branch *br;
+ union ubifs_key key;
+ LIST_HEAD(upper_idxs);
+
+ idx_sz = ubifs_idx_node_sz(c, c->fanout);
+ idx = kmalloc(idx_sz, GFP_KERNEL);
+ if (!idx)
+ return -ENOMEM;
+
+ list_sort(c, lower_idxs, cmp_idx);
+
+ ubifs_assert(c, lower_cnt != 0);
+
+ do {
+ upper_cnt = lower_cnt / c->fanout;
+ if (lower_cnt % c->fanout)
+ upper_cnt += 1;
+ e = list_first_entry(lower_idxs, struct idx_entry, list);
+
+ for (i = 0; i < upper_cnt; i++) {
+ if (i == upper_cnt - 1) {
+ child_cnt = lower_cnt % c->fanout;
+ if (child_cnt == 0)
+ child_cnt = c->fanout;
+ } else
+ child_cnt = c->fanout;
+
+ key_copy(c, &e->key, &key);
+ memset(idx, 0, idx_sz);
+ idx->ch.node_type = UBIFS_IDX_NODE;
+ idx->child_cnt = cpu_to_le16(child_cnt);
+ idx->level = cpu_to_le16(level);
+ for (j = 0; j < child_cnt; j++) {
+ ubifs_assert(c,
+ !list_entry_is_head(e, lower_idxs, list));
+ br = ubifs_idx_branch(c, idx, j);
+ key_write_idx(c, &e->key, &br->key);
+ br->lnum = cpu_to_le32(e->lnum);
+ br->offs = cpu_to_le32(e->offs);
+ br->len = cpu_to_le32(e->len);
+ e = list_next_entry(e, list);
+ }
+
+ pe = kmalloc(sizeof(struct idx_entry), GFP_KERNEL);
+ if (!pe) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = add_idx_node(c, idx, &key, child_cnt, pe);
+ if (err) {
+ kfree(pe);
+ goto out;
+ }
+
+ list_add_tail(&pe->list, &upper_idxs);
+ }
+
+ level++;
+ list_for_each_entry_safe(e, tmp_e, lower_idxs, list) {
+ list_del(&e->list);
+ kfree(e);
+ }
+ list_splice_init(&upper_idxs, lower_idxs);
+ lower_cnt = upper_cnt;
+ } while (lower_cnt > 1);
+
+ /* Set the index head */
+ c->ihead_lnum = FSCK(c)->rebuild->head_lnum;
+ c->ihead_offs = ALIGN(FSCK(c)->rebuild->head_offs, c->min_io_size);
+
+ /* Flush the last index LEB */
+ err = flush_write_buf(c);
+
+out:
+ list_for_each_entry_safe(e, tmp_e, lower_idxs, list) {
+ list_del(&e->list);
+ kfree(e);
+ }
+ list_for_each_entry_safe(e, tmp_e, &upper_idxs, list) {
+ list_del(&e->list);
+ kfree(e);
+ }
+ kfree(idx);
+ return err;
}
-static void record_file_used_lebs(struct ubifs_info *c,
- struct scanned_file *file)
+static int record_file_used_lebs(struct ubifs_info *c,
+ struct scanned_file *file,
+ struct list_head *idx_list, int *idx_cnt)
{
+ int err;
struct rb_node *node;
struct scanned_file *xattr_file;
struct scanned_dent_node *dent_node;
@@ -911,28 +1119,46 @@ static void record_file_used_lebs(struct ubifs_info *c,
ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
c->dev_name);
- parse_node_location(c, &file->ino.header);
+ err = parse_node_info(c, &file->ino.header, &file->ino.key,
+ NULL, 0, idx_list, idx_cnt);
+ if (err)
+ return err;
- if (file->trun.header.exist)
- parse_node_location(c, &file->trun.header);
+ if (file->trun.header.exist) {
+ err = parse_node_info(c, &file->trun.header, NULL, NULL,
+ 0, idx_list, NULL);
+ if (err)
+ return err;
+ }
for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) {
data_node = rb_entry(node, struct scanned_data_node, rb);
- parse_node_location(c, &data_node->header);
+ err = parse_node_info(c, &data_node->header, &data_node->key,
+ NULL, 0, idx_list, idx_cnt);
+ if (err)
+ return err;
}
for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
dent_node = rb_entry(node, struct scanned_dent_node, rb);
- parse_node_location(c, &dent_node->header);
+ err = parse_node_info(c, &dent_node->header, &dent_node->key,
+ dent_node->name, dent_node->nlen,
+ idx_list, idx_cnt);
+ if (err)
+ return err;
}
for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
xattr_file = rb_entry(node, struct scanned_file, rb);
- record_file_used_lebs(c, xattr_file);
+ err = record_file_used_lebs(c, xattr_file, idx_list, idx_cnt);
+ if (err)
+ return err;
}
+
+ return err;
}
/**
@@ -946,13 +1172,16 @@ static void record_file_used_lebs(struct ubifs_info *c,
* LEBs could be taken for storing new index tree.
* 3. Re-write data to prevent failed gc scanning in the subsequent mounting
* process caused by corrupted data.
+ * 4. Build TNC.
*/
static int traverse_files_and_nodes(struct ubifs_info *c)
{
- int i, err = 0;
+ int i, err = 0, idx_cnt = 0;
struct rb_node *node;
struct scanned_file *file;
struct rb_root *tree = &FSCK(c)->rebuild->scanned_files;
+ struct idx_entry *ie, *tmp_ie;
+ LIST_HEAD(idx_list);
if (rb_first(tree) == NULL) {
/* No scanned files. Create root dir. */
@@ -966,7 +1195,9 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
for (node = rb_first(tree); node; node = rb_next(node)) {
file = rb_entry(node, struct scanned_file, rb);
- record_file_used_lebs(c, file);
+ err = record_file_used_lebs(c, file, &idx_list, &idx_cnt);
+ if (err)
+ goto out_idx_list;
}
/* Re-write data. */
@@ -985,16 +1216,25 @@ static int traverse_files_and_nodes(struct ubifs_info *c)
err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 0);
if (err && err != -EBADMSG)
- return err;
+ goto out_idx_list;
if (len > end)
ubifs_pad(c, c->sbuf + end, len - end);
err = ubifs_leb_change(c, lnum, c->sbuf, len);
if (err)
- return err;
+ goto out_idx_list;
}
+ /* Build TNC. */
+ log_out(c, "Build TNC");
+ err = build_tnc(c, &idx_list, idx_cnt);
+
+out_idx_list:
+ list_for_each_entry_safe(ie, tmp_ie, &idx_list, list) {
+ list_del(&ie->list);
+ kfree(ie);
+ }
return err;
}
@@ -1059,6 +1299,7 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/*
* Step 7: Record used LEBs.
* Step 8: Re-write data to clean corrupted data.
+ * Step 9: Build TNC.
*/
err = traverse_files_and_nodes(c);
if (err)
--
2.13.6
This is the 12/18 step of fsck. Check and correct the space statistics.
There could be following steps and possible errors:
Step 1. Exit for check mode, if %FR_LPT_CORRUPTED or %FR_LPT_INCORRECT
is set in lpt status, the exit code should have %FSCK_UNCORRECTED.
Step 2. Check lpt status, if %FR_LPT_CORRUPTED is set in lpt status,
normal mode with 'no' answer will exit, other modes will rebuild lpt.
Step 3. Traverse LPT nodes, check the correctness of nnode and pnode,
compare LEB scanning result with LEB properties.
a. LPT node is corrupted, normal mode with 'no' answer will exit,
rebuild lpt for other modes.
b. Incorrect nnode/pnode, normal mode with 'no' answer will exit,
other other modes will correct the nnode/pnode.
c. Inconsistent comparing result, normal mode with 'no' answer
will exit, other modes will correct the space statistics.
Step 4. Check and correct the lprops table information.
Step 5. Set gc lnum(ubifs_rcvry_gc_commit / take_gc_lnum).
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/fsck.ubifs/check_space.c | 572 ++++++++++++++++++++++++++++++++++-
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 13 +
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 58 +++-
ubifs-utils/fsck.ubifs/problem.c | 51 ++++
ubifs-utils/fsck.ubifs/rebuild_fs.c | 5 +-
ubifs-utils/libubifs/lpt.c | 9 +-
ubifs-utils/libubifs/lpt_commit.c | 18 +-
ubifs-utils/libubifs/replay.c | 2 +-
ubifs-utils/libubifs/ubifs.h | 5 +-
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 2 +-
10 files changed, 716 insertions(+), 19 deletions(-)
diff --git a/ubifs-utils/fsck.ubifs/check_space.c b/ubifs-utils/fsck.ubifs/check_space.c
index f758bf1a..cf4b8d3f 100644
--- a/ubifs-utils/fsck.ubifs/check_space.c
+++ b/ubifs-utils/fsck.ubifs/check_space.c
@@ -44,18 +44,20 @@ int get_free_leb(struct ubifs_info *c)
* build_lpt - construct LPT and write it into flash.
* @c: UBIFS file-system description object
* @calculate_lp_cb: callback function to calculate the properties for given LEB
+ * @free_ltab: %true means to release c->ltab after creating lpt
*
* This function builds LPT according to the calculated results by
* @calculate_lp_cb and writes LPT into flash. Returns zero in case of success,
* a negative error code in case of failure.
*/
-int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
+ bool free_ltab)
{
int i, err, lnum, free, dirty;
u8 hash_lpt[UBIFS_HASH_ARR_SZ];
memset(&c->lst, 0, sizeof(struct ubifs_lp_stats));
- /* Set gc lnum. */
+ /* Set gc lnum, equivalent to ubifs_rcvry_gc_commit/take_gc_lnum. */
lnum = get_free_leb(c);
if (lnum < 0)
return lnum;
@@ -63,7 +65,7 @@ int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
/* Update LPT. */
for (i = 0; i < c->main_lebs; i++) {
- err = calculate_lp_cb(c, i, &free, &dirty);
+ err = calculate_lp_cb(c, i, &free, &dirty, NULL);
if (err)
return err;
@@ -87,8 +89,570 @@ int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
c->lst.total_dark += ubifs_calc_dark(c, spc);
c->lst.total_used += c->leb_size - spc;
}
+
+ dbg_fsck("build properties for LEB %d, free %d dirty %d is_idx %d, in %s",
+ i + c->main_first, free, dirty,
+ FSCK(c)->lpts[i].flags & LPROPS_INDEX ? 1 : 0,
+ c->dev_name);
}
/* Write LPT. */
- return ubifs_create_lpt(c, FSCK(c)->lpts, c->main_lebs, hash_lpt);
+ return ubifs_create_lpt(c, FSCK(c)->lpts, c->main_lebs, hash_lpt, free_ltab);
+}
+
+static int scan_get_lp(struct ubifs_info *c, int index, int *free, int *dirty,
+ int *is_idx)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int used, idx_leb, lnum = index + c->main_first, err = 0;
+ bool is_build_lpt = FSCK(c)->lpt_status & FR_LPT_CORRUPTED;
+
+ if (is_build_lpt) {
+ if (!test_bit(index, FSCK(c)->used_lebs) || c->gc_lnum == lnum) {
+ *free = c->leb_size;
+ *dirty = 0;
+ return 0;
+ }
+ } else {
+ if (!test_bit(index, FSCK(c)->used_lebs)) {
+ *free = c->leb_size;
+ *dirty = 0;
+ return 0;
+ }
+ }
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ /* All TNC LEBs have passed ubifs_scan in previous steps. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ return PTR_ERR(sleb);
+ }
+
+ idx_leb = -1;
+ used = 0;
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ int found, level = 0;
+
+ if (idx_leb == -1)
+ idx_leb = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
+
+ if (idx_leb)
+ /*
+ * Previous steps have ensured that every TNC LEB
+ * contains only index nodes or non-index nodes.
+ */
+ ubifs_assert(c, snod->type == UBIFS_IDX_NODE);
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_idx_node *idx = snod->node;
+
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ level = le16_to_cpu(idx->level);
+ }
+
+ found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
+ snod->offs, idx_leb);
+ if (found) {
+ if (found < 0) {
+ err = found;
+ /*
+ * TNC traversing is finished in previous steps,
+ * any TNC path is accessible.
+ */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out;
+ }
+ used += ALIGN(snod->len, 8);
+ }
+ }
+
+ if (is_build_lpt && !used) {
+ *free = c->leb_size;
+ *dirty = 0;
+ } else {
+ *free = c->leb_size - sleb->endpt;
+ *dirty = sleb->endpt - used;
+ if (idx_leb == 1) {
+ if (is_build_lpt)
+ FSCK(c)->lpts[index].flags = LPROPS_INDEX;
+ else
+ *is_idx = 1;
+ }
+ }
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+}
+
+static void clear_buds(struct ubifs_info *c)
+{
+ int i;
+
+ /*
+ * Since lpt is invalid, space statistics cannot be trusted, the buds
+ * were used to trace taken LEBs(LPT related), and fsck makes sure that
+ * there will be no new journal writings(no space allocations) before
+ * committing, so we should clear buds to prevent wrong lpt updating in
+ * committing stage(eg. ubifs_return_leb operation for @c->old_buds).
+ */
+ free_buds(c, true);
+ for (i = 0; i < c->jhead_cnt; i++) {
+ c->jheads[i].wbuf.lnum = -1;
+ c->jheads[i].wbuf.offs = -1;
+ }
+}
+
+static void claer_lp_lists_and_heaps(struct ubifs_info *c)
+{
+ int i;
+
+ /*
+ * Since lpt is invalid, clear in-memory fast accessing paths (lp
+ * lists & heaps).
+ */
+ c->freeable_cnt = 0;
+ c->in_a_category_cnt = 0;
+ for (i = 0; i < LPROPS_HEAP_CNT; i++) {
+ memset(c->lpt_heap[i].arr, 0, LPT_HEAP_SZ * sizeof(void *));
+ c->lpt_heap[i].cnt = 0;
+ c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
+ }
+ memset(c->dirty_idx.arr, 0, LPT_HEAP_SZ * sizeof(void *));
+ c->dirty_idx.cnt = 0;
+ c->dirty_idx.max_cnt = LPT_HEAP_SZ;
+ INIT_LIST_HEAD(&c->uncat_list);
+ INIT_LIST_HEAD(&c->empty_list);
+ INIT_LIST_HEAD(&c->freeable_list);
+ INIT_LIST_HEAD(&c->frdi_idx_list);
+}
+
+static int retake_ihead(struct ubifs_info *c)
+{
+ int err = take_ihead(c);
+
+ if (err < 0) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ } else
+ err = 0;
+
+ return err;
+}
+
+static int rebuild_lpt(struct ubifs_info *c)
+{
+ int err;
+
+ /* Clear buds. */
+ clear_buds(c);
+ /* Clear stale in-memory lpt data. */
+ c->lpt_drty_flgs = 0;
+ c->dirty_nn_cnt = 0;
+ c->dirty_pn_cnt = 0;
+ claer_lp_lists_and_heaps(c);
+ ubifs_free_lpt_nodes(c);
+ kfree(c->ltab);
+ c->ltab = NULL;
+
+ FSCK(c)->lpts = kzalloc(sizeof(struct ubifs_lprops) * c->main_lebs,
+ GFP_KERNEL);
+ if (!FSCK(c)->lpts) {
+ log_err(c, errno, "can not allocate lpts");
+ return -ENOMEM;
+ }
+
+ err = build_lpt(c, scan_get_lp, false);
+ if (err)
+ goto out;
+
+ err = retake_ihead(c);
+ if (err)
+ goto out;
+
+ FSCK(c)->lpt_status = 0;
+
+out:
+ kfree(FSCK(c)->lpts);
+ return err;
+}
+
+static void check_and_correct_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode,
+ struct ubifs_nnode *parent_nnode,
+ int row, int col, int *corrected)
+{
+ int num = ubifs_calc_nnode_num(row, col);
+
+ if (nnode->num != num) {
+ struct nnode_problem nnp = {
+ .nnode = nnode,
+ .parent_nnode = parent_nnode,
+ .num = num,
+ };
+
+ /*
+ * The nnode number is read from disk in big lpt mode, which
+ * could lead to the wrong nnode number, otherwise, ther nnode
+ * number cannot be wrong.
+ */
+ ubifs_assert(c, c->big_lpt);
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, NNODE_INCORRECT, &nnp)) {
+ nnode->num = num;
+ *corrected = 1;
+ }
+ }
+}
+
+static int check_and_correct_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode, int col,
+ struct ubifs_lp_stats *lst,
+ int *freeable_cnt, int *corrected)
+{
+ int i, index, lnum;
+ const int lp_cnt = UBIFS_LPT_FANOUT;
+
+ if (pnode->num != col) {
+ struct pnode_problem pnp = {
+ .pnode = pnode,
+ .num = col,
+ };
+
+ /*
+ * The pnode number is read from disk in big lpt mode, which
+ * could lead to the wrong pnode number, otherwise, ther pnode
+ * number cannot be wrong.
+ */
+ ubifs_assert(c, c->big_lpt);
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, PNODE_INCORRECT, &pnp)) {
+ pnode->num = col;
+ *corrected = 1;
+ }
+ }
+
+ index = pnode->num << UBIFS_LPT_FANOUT_SHIFT;
+ lnum = index + c->main_first;
+ for (i = 0; i < lp_cnt && lnum < c->leb_cnt; i++, index++, lnum++) {
+ int err, cat, free, dirty, is_idx = 0;
+ struct ubifs_lprops *lp = &pnode->lprops[i];
+
+ err = scan_get_lp(c, index, &free, &dirty, &is_idx);
+ if (err)
+ return err;
+
+ dbg_fsck("calculate properties for LEB %d, free %d dirty %d is_idx %d, in %s",
+ lnum, free, dirty, is_idx, c->dev_name);
+
+ if (!FSCK(c)->lpt_status && lp->free + lp->dirty == c->leb_size
+ && !test_bit(index, FSCK(c)->used_lebs)) {
+ /*
+ * Some LEBs may become freeable in the following cases:
+ * a. LEBs become freeable after replaying the journal.
+ * b. Unclean reboot while doing gc for a freeable
+ * non-index LEB
+ * c. Freeable index LEBs in an uncompleted commit due
+ * to an unclean unmount.
+ * , which makes that these LEBs won't be accounted into
+ * the FSCK(c)->used_lebs, but they actually have
+ * free/dirty space statistics. So we should skip
+ * checking space for these LEBs.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ is_idx = (lp->flags & LPROPS_INDEX) ? 1 : 0;
+ }
+ if (lnum != lp->lnum ||
+ free != lp->free || dirty != lp->dirty ||
+ (is_idx && !(lp->flags & LPROPS_INDEX)) ||
+ (!is_idx && (lp->flags & LPROPS_INDEX))) {
+ struct lp_problem lpp = {
+ .lnum = lnum,
+ .lp = lp,
+ .free = free,
+ .dirty = dirty,
+ .is_idx = is_idx,
+ };
+
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, LP_INCORRECT, &lpp)) {
+ lp->lnum = lnum;
+ lp->free = free;
+ lp->dirty = dirty;
+ lp->flags = is_idx ? LPROPS_INDEX : 0;
+ *corrected = 1;
+ }
+ }
+
+ cat = ubifs_categorize_lprops(c, lp);
+ if (cat != (lp->flags & LPROPS_CAT_MASK)) {
+ if (FSCK(c)->lpt_status & FR_LPT_INCORRECT) {
+ lp->flags |= cat;
+ } else {
+ /* lp could be in the heap or un-categorized(add heap failed). */
+ ubifs_assert(c, (lp->flags & LPROPS_CAT_MASK) == LPROPS_UNCAT);
+ }
+ }
+ if (cat == LPROPS_FREEABLE)
+ *freeable_cnt = *freeable_cnt + 1;
+ if ((lp->flags & LPROPS_TAKEN) && free == c->leb_size)
+ lst->taken_empty_lebs += 1;
+
+ lst->total_free += free;
+ lst->total_dirty += dirty;
+
+ if (free == c->leb_size)
+ lst->empty_lebs++;
+
+ if (is_idx) {
+ lst->idx_lebs += 1;
+ } else {
+ int spc;
+
+ spc = free + dirty;
+ if (spc < c->dead_wm)
+ lst->total_dead += spc;
+ else
+ lst->total_dark += ubifs_calc_dark(c, spc);
+ lst->total_used += c->leb_size - spc;
+ }
+ }
+
+ return 0;
+}
+
+static int check_and_correct_lpt(struct ubifs_info *c, int *lpt_corrected)
+{
+ int err, i, cnt, iip, row, col, corrected, lnum, max_num, freeable_cnt;
+ struct ubifs_cnode *cn, *cnode;
+ struct ubifs_nnode *nnode, *nn;
+ struct ubifs_pnode *pnode;
+ struct ubifs_lp_stats lst;
+
+ max_num = 0;
+ freeable_cnt = 0;
+ memset(&lst, 0, sizeof(struct ubifs_lp_stats));
+
+ /* Load the entire LPT tree, check whether there are corrupted nodes. */
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ pnode = ubifs_pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ if (pnode->num > max_num)
+ max_num = pnode->num;
+ }
+
+ /* Check whether there are pnodes exceeding the 'c->main_lebs'. */
+ pnode = ubifs_pnode_lookup(c, 0);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ while (pnode) {
+ if (pnode->num > max_num) {
+ ubifs_err(c, "pnode(%d) exceeds max number(%d)",
+ pnode->num, max_num);
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
+ }
+ pnode = ubifs_find_next_pnode(c, pnode);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ }
+
+ /* Check & correct nnodes and pnodes(including LEB properties). */
+ row = col = iip = 0;
+ cnode = (struct ubifs_cnode *)c->nroot;
+ while (cnode) {
+ ubifs_assert(c, row >= 0);
+ nnode = cnode->parent;
+ if (cnode->level) {
+ corrected = 0;
+ /* cnode is a nnode */
+ nn = (struct ubifs_nnode *)cnode;
+ check_and_correct_nnode(c, nn, nnode, row, col,
+ &corrected);
+ if (corrected)
+ ubifs_make_nnode_dirty(c, nn);
+ while (iip < UBIFS_LPT_FANOUT) {
+ cn = nn->nbranch[iip].cnode;
+ if (cn) {
+ /* Go down */
+ row += 1;
+ col <<= UBIFS_LPT_FANOUT_SHIFT;
+ col += iip;
+ iip = 0;
+ cnode = cn;
+ break;
+ }
+ /* Go right */
+ iip += 1;
+ }
+ if (iip < UBIFS_LPT_FANOUT)
+ continue;
+ } else {
+ corrected = 0;
+ /* cnode is a pnode */
+ pnode = (struct ubifs_pnode *)cnode;
+ err = check_and_correct_pnode(c, pnode, col, &lst,
+ &freeable_cnt, &corrected);
+ if (err)
+ return err;
+ if (corrected)
+ ubifs_make_pnode_dirty(c, pnode);
+ }
+ /* Go up and to the right */
+ row -= 1;
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ iip = cnode->iip + 1;
+ cnode = (struct ubifs_cnode *)nnode;
+ }
+
+ dbg_fsck("empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld,"
+ " total_used %lld, total_dead %lld, total_dark %lld,"
+ " taken_empty_lebs %d, freeable_cnt %d, in %s",
+ lst.empty_lebs, lst.idx_lebs, lst.total_free, lst.total_dirty,
+ lst.total_used, lst.total_dead, lst.total_dark,
+ lst.taken_empty_lebs, freeable_cnt, c->dev_name);
+
+ /* Check & correct the global space statistics. */
+ if (lst.empty_lebs != c->lst.empty_lebs ||
+ lst.idx_lebs != c->lst.idx_lebs ||
+ lst.total_free != c->lst.total_free ||
+ lst.total_dirty != c->lst.total_dirty ||
+ lst.total_used != c->lst.total_used ||
+ lst.total_dead != c->lst.total_dead ||
+ lst.total_dark != c->lst.total_dark) {
+ struct space_stat_problem ssp = {
+ .lst = &c->lst,
+ .calc_lst = &lst,
+ };
+
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, SPACE_STAT_INCORRECT, &ssp)) {
+ c->lst.empty_lebs = lst.empty_lebs;
+ c->lst.idx_lebs = lst.idx_lebs;
+ c->lst.total_free = lst.total_free;
+ c->lst.total_dirty = lst.total_dirty;
+ c->lst.total_used = lst.total_used;
+ c->lst.total_dead = lst.total_dead;
+ c->lst.total_dark = lst.total_dark;
+ }
+ }
+
+ /* Check & correct the lprops table information. */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ err = dbg_check_ltab_lnum(c, lnum);
+ if (err)
+ return err;
+ }
+
+ if (FSCK(c)->lpt_status & FR_LPT_INCORRECT) {
+ c->lst.taken_empty_lebs = 0;
+ /* Clear buds. */
+ clear_buds(c);
+ /* Clear lp lists & heaps. */
+ claer_lp_lists_and_heaps(c);
+ /*
+ * Build lp lists & heaps, subsequent steps could recover
+ * disconnected files by allocating free space.
+ */
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ int cat;
+ struct ubifs_lprops *lp = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+
+ cat = lp->flags & LPROPS_CAT_MASK;
+ ubifs_add_to_cat(c, lp, cat);
+ }
+ /*
+ * The %LPROPS_TAKEN flag is cleared in LEB properties, just
+ * remark it.
+ */
+ err = retake_ihead(c);
+ if (err)
+ return err;
+
+ *lpt_corrected = 1;
+ FSCK(c)->lpt_status &= ~FR_LPT_INCORRECT;
+ } else {
+ ubifs_assert(c, c->freeable_cnt == freeable_cnt);
+ ubifs_assert(c, c->lst.taken_empty_lebs == lst.taken_empty_lebs);
+ ubifs_assert(c, c->in_a_category_cnt == c->main_lebs);
+ }
+
+ return 0;
+}
+
+/**
+ * check_and_correct_space - check & correct the space statistics.
+ * @c: UBIFS file-system description object
+ *
+ * This function does following things:
+ * 1. Check fsck mode, exit program if current mode is check mode.
+ * 2. Check space statistics by comparing lpt records with scanning results
+ * for all main LEBs. There could be following problems:
+ * a) comparison result is inconsistent: correct the lpt records by LEB
+ * scanning results.
+ * b) lpt is corrupted: rebuild lpt.
+ * 3. Set the gc lnum.
+ * Returns zero in case of success, a negative error code in case of failure.
+ */
+int check_and_correct_space(struct ubifs_info *c)
+{
+ int err, lpt_corrected = 0;
+
+ if (FSCK(c)->mode == CHECK_MODE) {
+ /*
+ * The check mode will exit, because unclean LEBs are not
+ * rewritten for readonly mode in previous steps.
+ */
+ if (FSCK(c)->lpt_status)
+ exit_code |= FSCK_UNCORRECTED;
+ dbg_fsck("skip checking & correcting space%s, in %s",
+ mode_name(c), c->dev_name);
+ exit(exit_code);
+ }
+
+ log_out(c, "Check and correct the space statistics");
+
+ if (FSCK(c)->lpt_status & FR_LPT_CORRUPTED) {
+rebuild:
+ if (fix_problem(c, LPT_CORRUPTED, NULL))
+ return rebuild_lpt(c);
+ }
+
+ err = check_and_correct_lpt(c, &lpt_corrected);
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED))
+ goto rebuild;
+ return err;
+ }
+
+ /* Set gc lnum. */
+ if (c->need_recovery || lpt_corrected) {
+ err = ubifs_rcvry_gc_commit(c);
+ if (err) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ return err;
+ }
+ } else {
+ err = take_gc_lnum(c);
+ if (err) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ return err;
+ }
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ return err;
+ }
+
+ return err;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 14f77fc2..436831cb 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -58,6 +58,7 @@ static const char *helptext =
"-b, --rebuild Forcedly repair the filesystem even by rebuilding filesystem.\n"
" Depends on -y option\n"
"-n, --nochange Make no changes to the filesystem, only check filesystem.\n"
+" This mode don't check space, because unclean LEBs are not rewritten in readonly mode.\n"
" Can not be specified at the same time as the -a or -y options\n"
"Examples:\n"
"\t1. Check and repair filesystem from UBI volume /dev/ubi0_0\n"
@@ -329,6 +330,7 @@ static const unsigned int reason_mapping_table[] = {
BUD_CORRUPTED, /* FR_H_BUD_CORRUPTED */
TNC_DATA_CORRUPTED, /* FR_H_TNC_DATA_CORRUPTED */
ORPHAN_CORRUPTED, /* FR_H_ORPHAN_CORRUPTED */
+ LTAB_INCORRECT, /* FR_H_LTAB_INCORRECT */
};
static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason,
@@ -471,8 +473,18 @@ static int do_fsck(void)
if (tnc_is_empty(c) && fix_problem(c, EMPTY_TNC, NULL)) {
err = -EINVAL;
FSCK(c)->try_rebuild = true;
+ goto free_disconnected_files;
}
+ err = check_and_correct_space(c);
+ kfree(FSCK(c)->used_lebs);
+ destroy_file_tree(c, &FSCK(c)->scanned_files);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
+ destroy_file_list(c, &FSCK(c)->disconnected_files);
+ return err;
+
free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
@@ -519,6 +531,7 @@ int main(int argc, char *argv[])
* Step 9: Check and handle unreachable files
* Step 10: Check and correct files
* Step 11: Check whether the TNC is empty
+ * Step 12: Check and correct the space statistics
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 011835ff..cc8b71f5 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -44,12 +44,14 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
- EMPTY_TNC };
+ EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
+ LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
typedef int (*calculate_lp_callback)(struct ubifs_info *c,
- int index, int *free, int *dirty);
+ int index, int *free, int *dirty,
+ int *is_idx);
struct scanned_file;
@@ -201,6 +203,54 @@ struct invalid_file_problem {
};
/**
+ * nnode_problem - problem instance for incorrect nnode
+ * @nnode: incorrect nnode
+ * @parent_nnode: the parent nnode of @nnode, could be NULL if @nnode is root
+ * @num: calculated num
+ */
+struct nnode_problem {
+ struct ubifs_nnode *nnode;
+ struct ubifs_nnode *parent_nnode;
+ int num;
+};
+
+/**
+ * pnode_problem - problem instance for incorrect pnode
+ * @pnode: incorrect pnode
+ * @num: calculated num
+ */
+struct pnode_problem {
+ struct ubifs_pnode *pnode;
+ int num;
+};
+
+/**
+ * lp_problem - problem instance for incorrect LEB proerties
+ * @lp: incorrect LEB properties
+ * @lnum: LEB number
+ * @free: calculated free space in LEB
+ * @dirty: calculated dirty bytes in LEB
+ * @is_idx: %true means that the LEB is an index LEB
+ */
+struct lp_problem {
+ struct ubifs_lprops *lp;
+ int lnum;
+ int free;
+ int dirty;
+ int is_idx;
+};
+
+/**
+ * space_stat_problem - problem instance for incorrect space statistics
+ * @lst: current space statistics
+ * @calc_lst: calculated space statistics
+ */
+struct space_stat_problem {
+ struct ubifs_lp_stats *lst;
+ struct ubifs_lp_stats *calc_lst;
+};
+
+/**
* ubifs_rebuild_info - UBIFS rebuilding information.
* @write_buf: write buffer for LEB @head_lnum
* @head_lnum: current writing LEB number
@@ -327,6 +377,8 @@ bool tnc_is_empty(struct ubifs_info *c);
/* check_space.c */
int get_free_leb(struct ubifs_info *c);
-int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb);
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
+ bool free_ltab);
+int check_and_correct_space(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 795f05fa..f987e480 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -61,6 +61,12 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"}, // FILE_IS_INCONSISTENT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "TNC is empty"}, // EMPTY_TNC
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Corrupted pnode/nnode"}, // LPT_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for nnode"}, // NNODE_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for pnode"}, // PNODE_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for LEB"}, // LP_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"}, // SPACE_STAT_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
};
static const char *get_question(const struct fsck_problem *problem,
@@ -96,6 +102,8 @@ static const char *get_question(const struct fsck_problem *problem,
return "Remove data block?";
case FILE_IS_DISCONNECTED:
return "Put it into disconnected list?";
+ case LPT_CORRUPTED:
+ return "Rebuild LPT?";
}
return "Fix it?";
@@ -229,6 +237,49 @@ static void print_problem(const struct ubifs_info *c,
file->calc_xnms, file->calc_size);
break;
}
+ case NNODE_INCORRECT:
+ {
+ const struct nnode_problem *nnp = (const struct nnode_problem *)priv;
+
+ log_out(c, "problem: %s, nnode num %d expected %d parent num %d iip %d",
+ problem->desc, nnp->nnode->num, nnp->num,
+ nnp->parent_nnode ? nnp->parent_nnode->num : 0,
+ nnp->nnode->iip);
+ break;
+ }
+ case PNODE_INCORRECT:
+ {
+ const struct pnode_problem *pnp = (const struct pnode_problem *)priv;
+
+ log_out(c, "problem: %s, pnode num %d expected %d parent num %d iip %d",
+ problem->desc, pnp->pnode->num, pnp->num,
+ pnp->pnode->parent->num, pnp->pnode->iip);
+ break;
+ }
+ case LP_INCORRECT:
+ {
+ const struct lp_problem *lpp = (const struct lp_problem *)priv;
+
+ log_out(c, "problem: %s %d, free %d dirty %d is_idx %d, should be lnum %d free %d dirty %d is_idx %d",
+ problem->desc, lpp->lp->lnum, lpp->lp->free,
+ lpp->lp->dirty, lpp->lp->flags & LPROPS_INDEX ? 1 : 0,
+ lpp->lnum, lpp->free, lpp->dirty, lpp->is_idx);
+ break;
+ }
+ case SPACE_STAT_INCORRECT:
+ {
+ const struct space_stat_problem *ssp = (const struct space_stat_problem *)priv;
+
+ log_out(c, "problem: %s, empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld, should be empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld",
+ problem->desc, ssp->lst->empty_lebs, ssp->lst->idx_lebs,
+ ssp->lst->total_free, ssp->lst->total_dirty,
+ ssp->lst->total_used, ssp->lst->total_dead,
+ ssp->lst->total_dark, ssp->calc_lst->empty_lebs,
+ ssp->calc_lst->idx_lebs, ssp->calc_lst->total_free,
+ ssp->calc_lst->total_dirty, ssp->calc_lst->total_used,
+ ssp->calc_lst->total_dead, ssp->calc_lst->total_dark);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 1161f5af..e65d655d 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -1224,7 +1224,8 @@ out_idx_list:
return err;
}
-static int calculate_lp(struct ubifs_info *c, int index, int *free, int *dirty)
+static int calculate_lp(struct ubifs_info *c, int index, int *free, int *dirty,
+ __unused int *is_idx)
{
if (!test_bit(index, FSCK(c)->used_lebs) ||
c->gc_lnum == index + c->main_first) {
@@ -1421,7 +1422,7 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 10. Build LPT. */
log_out(c, "Build LPT");
- err = build_lpt(c, calculate_lp);
+ err = build_lpt(c, calculate_lp, true);
if (err) {
exit_code |= FSCK_ERROR;
goto out;
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index fc70cad5..8e20a173 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -605,6 +605,7 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
* @lps: array of logical eraseblock properties
* @lp_cnt: the length of @lps
* @hash: hash of the LPT is returned here
+ * @free_ltab: %true means to release c->ltab after creating lpt
*
* This function creates lpt, the pnode will be initialized based on
* corresponding elements in @lps. If there are no corresponding lprops
@@ -612,7 +613,7 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
* as free state.
*/
int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
- u8 *hash)
+ u8 *hash, bool free_ltab)
{
int lnum, err = 0, i, j, cnt, len, alen, row;
int blnum, boffs, bsz, bcnt;
@@ -910,10 +911,12 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
out:
- c->ltab = NULL;
+ if (free_ltab || err) {
+ c->ltab = NULL;
+ vfree(ltab);
+ }
kfree(desc);
kfree(lsave);
- vfree(ltab);
vfree(buf);
kfree(nnode);
kfree(pnode);
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index 8a44546d..ee84f801 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -1599,7 +1599,7 @@ static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum,
*
* This function returns %0 on success and a negative error code on failure.
*/
-static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
+int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
{
int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len;
int ret;
@@ -1608,7 +1608,7 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
buf = p = __vmalloc(c->leb_size, GFP_NOFS);
if (!buf) {
ubifs_err(c, "cannot allocate memory for ltab checking");
- return 0;
+ return -ENOMEM;
}
dbg_lp("LEB %d", lnum);
@@ -1632,20 +1632,30 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
continue;
}
if (!dbg_is_all_ff(p, len)) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid empty space in LEB %d at %d",
lnum, c->leb_size - len);
err = -EINVAL;
+ goto out;
}
i = lnum - c->lpt_first;
if (len != c->ltab[i].free) {
ubifs_err(c, "invalid free space in LEB %d (free %d, expected %d)",
- lnum, len, c->ltab[i].free);
+ lnum, c->ltab[i].free, len);
err = -EINVAL;
+ if (handle_failure_callback(c, FR_H_LTAB_INCORRECT, NULL)) {
+ c->ltab[i].free = len;
+ err = 0;
+ }
}
if (dirty != c->ltab[i].dirty) {
ubifs_err(c, "invalid dirty space in LEB %d (dirty %d, expected %d)",
- lnum, dirty, c->ltab[i].dirty);
+ lnum, c->ltab[i].dirty, dirty);
err = -EINVAL;
+ if (handle_failure_callback(c, FR_H_LTAB_INCORRECT, NULL)) {
+ c->ltab[i].dirty = dirty;
+ err = 0;
+ }
}
goto out;
}
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index 0895f069..7991d974 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -1099,7 +1099,7 @@ out_dump:
* This function returns the amount of free space in the index head LEB or a
* negative error code.
*/
-static int take_ihead(struct ubifs_info *c)
+int take_ihead(struct ubifs_info *c)
{
const struct ubifs_lprops *lp;
int err, free;
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 45c4105c..a4b05a66 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1563,6 +1563,7 @@ enum {
FR_H_BUD_CORRUPTED = 0, /* Bud LEB is corrupted */
FR_H_TNC_DATA_CORRUPTED, /* Data searched from TNC is corrupted */
FR_H_ORPHAN_CORRUPTED, /* Orphan LEB is corrupted */
+ FR_H_LTAB_INCORRECT, /* Lprops table is incorrect */
};
/* Callback functions for failure(which can be handled by fsck) happens. */
static inline void set_failure_reason_callback(const struct ubifs_info *c,
@@ -1734,6 +1735,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c);
/* replay.c */
int ubifs_validate_entry(struct ubifs_info *c,
const struct ubifs_dent_node *dent);
+int take_ihead(struct ubifs_info *c);
int ubifs_replay_journal(struct ubifs_info *c);
/* gc.c */
@@ -1754,7 +1756,7 @@ int ubifs_clear_orphans(struct ubifs_info *c);
int ubifs_calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt);
int ubifs_calc_lpt_geom(struct ubifs_info *c);
int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
- u8 *hash);
+ u8 *hash, bool free_ltab);
int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
@@ -1795,6 +1797,7 @@ int ubifs_lpt_end_commit(struct ubifs_info *c);
int ubifs_lpt_post_commit(struct ubifs_info *c);
void ubifs_free_lpt_nodes(struct ubifs_info *c);
void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
+int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum);
/* lprops.c */
const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 2817b6ce..b5f38929 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -2675,7 +2675,7 @@ static int write_lpt(void)
int err, lnum;
c->lscan_lnum = c->main_first;
- err = ubifs_create_lpt(c, c->lpt, c->main_lebs, c->lpt_hash);
+ err = ubifs_create_lpt(c, c->lpt, c->main_lebs, c->lpt_hash, true);
if (err)
return err;
--
2.13.6
Add some file operations, such as ubifs_lookup, ubifs_mkdir, etc., this
is a preparation for recovering disconnected files or root dir in fsck.
File writing operations are based on the journal subsystem, generated
dirty data depends on a new commit in subsequent steps to update disk
content.
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/common/linux_types.h | 3 +
ubifs-utils/fsck.ubifs/rebuild_fs.c | 2 -
ubifs-utils/libubifs/dir.c | 359 ++++++++++++++++++++++++++++++++++++
ubifs-utils/libubifs/journal.c | 194 +++++++++++++++++++
ubifs-utils/libubifs/misc.h | 9 +
ubifs-utils/libubifs/ubifs.h | 45 +++++
6 files changed, 610 insertions(+), 2 deletions(-)
diff --git a/ubifs-utils/common/linux_types.h b/ubifs-utils/common/linux_types.h
index 556b2e25..ebf9ecde 100644
--- a/ubifs-utils/common/linux_types.h
+++ b/ubifs-utils/common/linux_types.h
@@ -28,6 +28,9 @@ struct fscrypt_name {
#define fname_name(p) ((p)->disk_name.name)
#define fname_len(p) ((p)->disk_name.len)
+#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+
#define t16(x) ({ \
uint16_t __b = (x); \
(__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index e65d655d..b82d7282 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -707,8 +707,6 @@ static void extract_dentry_tree(struct ubifs_info *c)
static void init_root_ino(struct ubifs_info *c, struct ubifs_ino_node *ino)
{
-#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
-#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
__le64 tmp_le64;
/* Create default root inode */
diff --git a/ubifs-utils/libubifs/dir.c b/ubifs-utils/libubifs/dir.c
index 9d5f6446..89f77ebc 100644
--- a/ubifs-utils/libubifs/dir.c
+++ b/ubifs-utils/libubifs/dir.c
@@ -28,4 +28,363 @@
* write it, but just marks it as dirty.
*/
+#include <sys/stat.h>
+
+#include "linux_err.h"
+#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+
+/**
+ * inherit_flags - inherit flags of the parent inode.
+ * @c: UBIFS file-system description object
+ * @dir: parent inode
+ * @mode: new inode mode flags
+ *
+ * This is a helper function for 'ubifs_new_inode()' which inherits flag of the
+ * parent directory inode @dir. UBIFS inodes inherit the following flags:
+ * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on
+ * sub-directory basis;
+ * o %UBIFS_SYNC_FL - useful for the same reasons;
+ * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories.
+ *
+ * This function returns the inherited flags.
+ */
+static int inherit_flags(struct ubifs_info *c, const struct inode *dir,
+ unsigned int mode)
+{
+ int flags;
+ const struct ubifs_inode *ui = ubifs_inode(dir);
+
+ ubifs_assert(c, S_ISDIR(dir->mode));
+
+ flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL);
+ if (!S_ISDIR(mode))
+ /* The "DIRSYNC" flag only applies to directories */
+ flags &= ~UBIFS_DIRSYNC_FL;
+ return flags;
+}
+
+/**
+ * ubifs_new_inode - allocate new UBIFS inode object.
+ * @c: UBIFS file-system description object
+ * @dir: parent inode
+ * @mode: inode mode flags
+ *
+ * This function finds an unused inode number, allocates new ubifs inode and
+ * initializes it. Returns new ubifs inode in case of success and an error code
+ * in case of failure.
+ */
+static struct ubifs_inode *ubifs_new_inode(struct ubifs_info *c,
+ const struct inode *dir,
+ unsigned int mode)
+{
+ int err;
+ time_t now = time(NULL);
+ struct ubifs_inode *ui;
+ struct inode *inode;
+
+ ui = kzalloc(sizeof(struct ubifs_inode), GFP_KERNEL);
+ if (!ui)
+ return ERR_PTR(-ENOMEM);
+
+ inode = &ui->vfs_inode;
+ inode->atime_sec = inode->ctime_sec = inode->mtime_sec = now;
+ inode->nlink = 1;
+ inode->mode = mode;
+ if (dir) {
+ /* Create non root dir. */
+ inode->uid = dir->uid;
+ inode->gid = dir->gid;
+ if ((dir->mode & S_ISGID) && S_ISDIR(mode))
+ inode->mode |= S_ISGID;
+ ui->flags = inherit_flags(c, dir, mode);
+ }
+ if (S_ISDIR(mode))
+ ui->ui_size = UBIFS_INO_NODE_SZ;
+ if (S_ISREG(mode))
+ ui->compr_type = c->default_compr;
+ else
+ ui->compr_type = UBIFS_COMPR_NONE;
+
+ if (dir) {
+ spin_lock(&c->cnt_lock);
+ /* Inode number overflow is currently not supported */
+ if (c->highest_inum >= INUM_WARN_WATERMARK) {
+ if (c->highest_inum >= INUM_WATERMARK) {
+ spin_unlock(&c->cnt_lock);
+ ubifs_err(c, "out of inode numbers");
+ err = -EINVAL;
+ goto out;
+ }
+ ubifs_warn(c, "running out of inode numbers (current %lu, max %u)",
+ (unsigned long)c->highest_inum, INUM_WATERMARK);
+ }
+ inode->inum = ++c->highest_inum;
+ } else {
+ /* Create root dir. */
+ inode->inum = UBIFS_ROOT_INO;
+ }
+ /*
+ * The creation sequence number remains with this inode for its
+ * lifetime. All nodes for this inode have a greater sequence number,
+ * and so it is possible to distinguish obsolete nodes belonging to a
+ * previous incarnation of the same inode number - for example, for the
+ * purpose of rebuilding the index.
+ */
+ ui->creat_sqnum = ++c->max_sqnum;
+ spin_unlock(&c->cnt_lock);
+
+ return ui;
+
+out:
+ kfree(ui);
+ return ERR_PTR(err);
+}
+
+/**
+ * ubifs_lookup_by_inum - look up the UBIFS inode according to inode number.
+ * @c: UBIFS file-system description object
+ * @inum: inode number
+ *
+ * This function looks up the UBIFS inode according to a given inode number.
+ * Returns zero in case of success and an error code in case of failure.
+ */
+struct ubifs_inode *ubifs_lookup_by_inum(struct ubifs_info *c, ino_t inum)
+{
+ int err;
+ union ubifs_key key;
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ struct ubifs_ino_node *ino = NULL;
+
+ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ino)
+ return ERR_PTR(-ENOMEM);
+
+ ui = kzalloc(sizeof(struct ubifs_inode), GFP_KERNEL);
+ if (!ui) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ inode = &ui->vfs_inode;
+ ino_key_init(c, &key, inum);
+ err = ubifs_tnc_lookup(c, &key, ino);
+ if (err) {
+ kfree(ui);
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out;
+ }
+
+ inode = &ui->vfs_inode;
+ inode->inum = inum;
+ inode->uid = le32_to_cpu(ino->uid);
+ inode->gid = le32_to_cpu(ino->gid);
+ inode->mode = le32_to_cpu(ino->mode);
+ inode->nlink = le32_to_cpu(ino->nlink);
+ inode->atime_sec = le64_to_cpu(ino->atime_sec);
+ inode->ctime_sec = le64_to_cpu(ino->ctime_sec);
+ inode->mtime_sec = le64_to_cpu(ino->mtime_sec);
+ inode->atime_nsec = le32_to_cpu(ino->atime_nsec);
+ inode->ctime_nsec = le32_to_cpu(ino->ctime_nsec);
+ inode->mtime_nsec = le32_to_cpu(ino->mtime_nsec);
+ ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum);
+ ui->xattr_size = le32_to_cpu(ino->xattr_size);
+ ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ ui->xattr_names = le32_to_cpu(ino->xattr_names);
+ ui->compr_type = le16_to_cpu(ino->compr_type);
+ ui->ui_size = le64_to_cpu(ino->size);
+ ui->flags = le32_to_cpu(ino->flags);
+ ui->data_len = le32_to_cpu(ino->data_len);
+
+out:
+ kfree(ino);
+ return err ? ERR_PTR(err) : ui;
+}
+
+struct ubifs_inode *ubifs_lookup(struct ubifs_info *c,
+ struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm)
+{
+ int err;
+ ino_t inum;
+ union ubifs_key key;
+ struct ubifs_dent_node *dent;
+
+ if (fname_len(nm) > UBIFS_MAX_NLEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+ if (!dent)
+ return ERR_PTR(-ENOMEM);
+
+ dent_key_init(c, &key, dir_ui->vfs_inode.inum, nm);
+ err = ubifs_tnc_lookup_nm(c, &key, dent, nm);
+ if (err) {
+ kfree(dent);
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ return ERR_PTR(err);
+ }
+ inum = le64_to_cpu(dent->inum);
+ kfree(dent);
+
+ return ubifs_lookup_by_inum(c, inum);
+}
+
+int ubifs_mkdir(struct ubifs_info *c, struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm, unsigned int mode)
+{
+ struct ubifs_inode *ui;
+ struct inode *inode, *dir = &dir_ui->vfs_inode;
+ int err, sz_change;
+ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+ .dirtied_ino = 1};
+ /*
+ * Budget request settings: new inode, new direntry and changing parent
+ * directory inode.
+ */
+ dbg_gen("dent '%s', mode %#hx in dir ino %lu",
+ fname_name(nm), mode, dir->inum);
+
+ /* New dir is not allowed to be created under an encrypted directory. */
+ ubifs_assert(c, !(dir_ui->flags & UBIFS_CRYPT_FL));
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(nm));
+
+ ui = ubifs_new_inode(c, dir, S_IFDIR | mode);
+ if (IS_ERR(ui)) {
+ err = PTR_ERR(ui);
+ goto out_budg;
+ }
+
+ inode = &ui->vfs_inode;
+ inode->nlink++;
+ dir->nlink++;
+ dir_ui->ui_size += sz_change;
+ dir->ctime_sec = dir->mtime_sec = inode->ctime_sec;
+ err = ubifs_jnl_update_file(c, dir_ui, nm, ui);
+ if (err) {
+ ubifs_err(c, "cannot create directory, error %d", err);
+ goto out_cancel;
+ }
+
+ kfree(ui);
+ ubifs_release_budget(c, &req);
+ return 0;
+
+out_cancel:
+ dir_ui->ui_size -= sz_change;
+ dir->nlink--;
+ kfree(ui);
+out_budg:
+ ubifs_release_budget(c, &req);
+ return err;
+}
+
+/**
+ * ubifs_link_recovery - link a disconnected file into the target directory.
+ * @c: UBIFS file-system description object
+ * @dir_ui: target directory
+ * @ui: the UBIFS inode of disconnected file
+ * @nm: directory entry name
+ *
+ * This function links the inode of disconnected file to a directory entry name
+ * under the target directory. Returns zero in case of success and an error code
+ * in case of failure.
+ */
+int ubifs_link_recovery(struct ubifs_info *c, struct ubifs_inode *dir_ui,
+ struct ubifs_inode *ui, const struct fscrypt_name *nm)
+{
+ struct inode *inode = &ui->vfs_inode, *dir = &dir_ui->vfs_inode;
+ int err, sz_change;
+ struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2,
+ .dirtied_ino_d = ALIGN(ui->data_len, 8) };
+ time_t now = time(NULL);
+
+ /*
+ * Budget request settings: new direntry, changing the target inode,
+ * changing the parent inode.
+ */
+ dbg_gen("dent '%s' to ino %lu (nlink %d) in dir ino %lu",
+ fname_name(nm), inode->inum, inode->nlink, dir->inum);
+
+ /* New dir is not allowed to be created under an encrypted directory. */
+ ubifs_assert(c, !(dir_ui->flags & UBIFS_CRYPT_FL));
+
+ sz_change = CALC_DENT_SIZE(fname_len(nm));
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ inode->ctime_sec = now;
+ dir_ui->ui_size += sz_change;
+ dir->ctime_sec = dir->mtime_sec = now;
+ err = ubifs_jnl_update_file(c, dir_ui, nm, ui);
+ if (err)
+ goto out_cancel;
+
+ ubifs_release_budget(c, &req);
+ return 0;
+
+out_cancel:
+ dir_ui->ui_size -= sz_change;
+ ubifs_release_budget(c, &req);
+ return err;
+}
+
+/**
+ * ubifs_create_root - create the root inode.
+ * @c: UBIFS file-system description object
+ *
+ * This function creates a new inode for the root directory. Returns zero in
+ * case of success and an error code in case of failure.
+ */
+int ubifs_create_root(struct ubifs_info *c)
+{
+ int err;
+ struct inode *inode;
+ struct ubifs_budget_req req = { .new_ino = 1 };
+ struct ubifs_inode *ui;
+
+ /* Budget request settings: new inode. */
+ dbg_gen("create root dir");
+
+ err = ubifs_budget_space(c, &req);
+ if (err)
+ return err;
+
+ ui = ubifs_new_inode(c, NULL, S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO);
+ if (IS_ERR(ui)) {
+ err = PTR_ERR(ui);
+ goto out_budg;
+ }
+
+ inode = &ui->vfs_inode;
+ inode->nlink = 2;
+ ui->ui_size = UBIFS_INO_NODE_SZ;
+ ui->flags = UBIFS_COMPR_FL;
+ err = ubifs_jnl_update_file(c, NULL, NULL, ui);
+ if (err)
+ goto out_ui;
+
+ kfree(ui);
+ ubifs_release_budget(c, &req);
+ return 0;
+
+out_ui:
+ kfree(ui);
+out_budg:
+ ubifs_release_budget(c, &req);
+ ubifs_err(c, "cannot create root dir, error %d", err);
+ return err;
+}
diff --git a/ubifs-utils/libubifs/journal.c b/ubifs-utils/libubifs/journal.c
index d3fdb76a..e78ea14f 100644
--- a/ubifs-utils/libubifs/journal.c
+++ b/ubifs-utils/libubifs/journal.c
@@ -47,9 +47,11 @@
*/
#include "bitops.h"
+#include "kmem.h"
#include "ubifs.h"
#include "defs.h"
#include "debug.h"
+#include "key.h"
#include "misc.h"
/**
@@ -437,3 +439,195 @@ static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
else
dent->cookie = 0;
}
+
+/**
+ * pack_inode - pack an ubifs inode node.
+ * @c: UBIFS file-system description object
+ * @ino: buffer in which to pack inode node
+ * @ui: ubifs inode to pack
+ * @last: indicates the last node of the group
+ */
+static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino,
+ const struct ubifs_inode *ui, int last)
+{
+ const struct inode *inode = &ui->vfs_inode;
+ int data_len = 0, last_reference = !inode->nlink;
+
+ ino->ch.node_type = UBIFS_INO_NODE;
+ ino_key_init_flash(c, &ino->key, inode->inum);
+ ino->creat_sqnum = cpu_to_le64(ui->creat_sqnum);
+ ino->atime_sec = cpu_to_le64(inode->atime_sec);
+ ino->atime_nsec = cpu_to_le32(inode->atime_nsec);
+ ino->ctime_sec = cpu_to_le64(inode->ctime_sec);
+ ino->ctime_nsec = cpu_to_le32(inode->ctime_nsec);
+ ino->mtime_sec = cpu_to_le64(inode->mtime_sec);
+ ino->mtime_nsec = cpu_to_le32(inode->mtime_nsec);
+ ino->uid = cpu_to_le32(inode->uid);
+ ino->gid = cpu_to_le32(inode->gid);
+ ino->mode = cpu_to_le32(inode->mode);
+ ino->flags = cpu_to_le32(ui->flags);
+ ino->size = cpu_to_le64(ui->ui_size);
+ ino->nlink = cpu_to_le32(inode->nlink);
+ ino->compr_type = cpu_to_le16(ui->compr_type);
+ ino->data_len = cpu_to_le32(ui->data_len);
+ ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt);
+ ino->xattr_size = cpu_to_le32(ui->xattr_size);
+ ino->xattr_names = cpu_to_le32(ui->xattr_names);
+ zero_ino_node_unused(ino);
+
+ /*
+ * Drop the attached data if this is a deletion inode, the data is not
+ * needed anymore.
+ */
+ if (!last_reference) {
+ memcpy(ino->data, ui->data, ui->data_len);
+ data_len = ui->data_len;
+ }
+
+ ubifs_prep_grp_node(c, ino, UBIFS_INO_NODE_SZ + data_len, last);
+}
+
+/**
+ * ubifs_jnl_update_file - update file.
+ * @c: UBIFS file-system description object
+ * @dir_ui: parent ubifs inode
+ * @nm: directory entry name
+ * @ui: ubifs inode to update
+ *
+ * This function updates an file by writing a directory entry node, the inode
+ * node itself, and the parent directory inode node to the journal. If the
+ * @dir_ui and @nm are NULL, only update @ui.
+ *
+ * Returns zero on success. In case of failure, a negative error code is
+ * returned.
+ */
+int ubifs_jnl_update_file(struct ubifs_info *c,
+ const struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm,
+ const struct ubifs_inode *ui)
+{
+ const struct inode *dir = NULL, *inode = &ui->vfs_inode;
+ int err, dlen, ilen, len, lnum, ino_offs, dent_offs, dir_ilen;
+ int aligned_dlen, aligned_ilen;
+ struct ubifs_dent_node *dent;
+ struct ubifs_ino_node *ino;
+ union ubifs_key dent_key, ino_key;
+ u8 hash_dent[UBIFS_HASH_ARR_SZ];
+ u8 hash_ino[UBIFS_HASH_ARR_SZ];
+ u8 hash_ino_dir[UBIFS_HASH_ARR_SZ];
+
+ ubifs_assert(c, (!nm && !dir_ui) || (nm && dir_ui));
+ ubifs_assert(c, inode->nlink != 0);
+
+ ilen = UBIFS_INO_NODE_SZ + ui->data_len;
+
+ if (nm)
+ dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
+ else
+ dlen = 0;
+
+ if (dir_ui) {
+ dir = &dir_ui->vfs_inode;
+ ubifs_assert(c, dir->nlink != 0);
+ dir_ilen = UBIFS_INO_NODE_SZ + dir_ui->data_len;
+ } else
+ dir_ilen = 0;
+
+ aligned_dlen = ALIGN(dlen, 8);
+ aligned_ilen = ALIGN(ilen, 8);
+ len = aligned_dlen + aligned_ilen + dir_ilen;
+ if (ubifs_authenticated(c))
+ len += ALIGN(dir_ilen, 8) + ubifs_auth_node_sz(c);
+
+ dent = kzalloc(len, GFP_NOFS);
+ if (!dent)
+ return -ENOMEM;
+
+ /* Make reservation before allocating sequence numbers */
+ err = make_reservation(c, BASEHD, len);
+ if (err)
+ goto out_free;
+
+ if (nm) {
+ dent->ch.node_type = UBIFS_DENT_NODE;
+ dent_key_init(c, &dent_key, dir->inum, nm);
+
+ key_write(c, &dent_key, dent->key);
+ dent->inum = cpu_to_le64(inode->inum);
+ dent->type = ubifs_get_dent_type(inode->mode);
+ dent->nlen = cpu_to_le16(fname_len(nm));
+ memcpy(dent->name, fname_name(nm), fname_len(nm));
+ dent->name[fname_len(nm)] = '\0';
+ set_dent_cookie(c, dent);
+
+ zero_dent_node_unused(dent);
+ ubifs_prep_grp_node(c, dent, dlen, 0);
+ err = ubifs_node_calc_hash(c, dent, hash_dent);
+ if (err)
+ goto out_release;
+ }
+
+ ino = (void *)dent + aligned_dlen;
+ pack_inode(c, ino, ui, dir_ui == NULL ? 1 : 0);
+ err = ubifs_node_calc_hash(c, ino, hash_ino);
+ if (err)
+ goto out_release;
+
+ if (dir_ui) {
+ ino = (void *)ino + aligned_ilen;
+ pack_inode(c, ino, dir_ui, 1);
+ err = ubifs_node_calc_hash(c, ino, hash_ino_dir);
+ if (err)
+ goto out_release;
+ }
+
+ err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, 0);
+ if (err)
+ goto out_release;
+ release_head(c, BASEHD);
+ kfree(dent);
+ ubifs_add_auth_dirt(c, lnum);
+
+ if (nm) {
+ err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
+ hash_dent, nm);
+ if (err) {
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out_ro;
+ }
+ }
+
+ ino_key_init(c, &ino_key, inode->inum);
+ ino_offs = dent_offs + aligned_dlen;
+ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
+ if (err) {
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out_ro;
+ }
+
+ if (dir_ui) {
+ ino_key_init(c, &ino_key, dir->inum);
+ ino_offs += aligned_ilen;
+ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, dir_ilen,
+ hash_ino_dir);
+ if (err) {
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out_ro;
+ }
+ }
+
+ finish_reservation(c);
+ return 0;
+
+out_free:
+ kfree(dent);
+ return err;
+
+out_release:
+ release_head(c, BASEHD);
+ kfree(dent);
+out_ro:
+ ubifs_ro_mode(c, err);
+ finish_reservation(c);
+ return err;
+}
diff --git a/ubifs-utils/libubifs/misc.h b/ubifs-utils/libubifs/misc.h
index 4b718068..1b4404f3 100644
--- a/ubifs-utils/libubifs/misc.h
+++ b/ubifs-utils/libubifs/misc.h
@@ -70,6 +70,15 @@ ubifs_tnc_find_child(struct ubifs_znode *znode, int start)
}
/**
+ * ubifs_inode - get UBIFS inode information by VFS 'struct inode' object.
+ * @inode: the VFS 'struct inode' pointer
+ */
+static inline struct ubifs_inode *ubifs_inode(const struct inode *inode)
+{
+ return container_of(inode, struct ubifs_inode, vfs_inode);
+}
+
+/**
* ubifs_wbuf_sync - synchronize write-buffer.
* @wbuf: write-buffer to synchronize
*
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index a4b05a66..0908a228 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -262,7 +262,36 @@ struct ubifs_gced_idx_leb {
};
/**
+ * struct inode - inode description.
+ * @uid: owner ID
+ * @gid: group ID
+ * @mode: access flags
+ * @nlink: number of hard links
+ * @inum: inode number
+ * @atime_sec: access time seconds
+ * @ctime_sec: creation time seconds
+ * @mtime_sec: modification time seconds
+ * @atime_nsec: access time nanoseconds
+ * @ctime_nsec: creation time nanoseconds
+ * @mtime_nsec: modification time nanoseconds
+ */
+struct inode {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int mode;
+ unsigned int nlink;
+ ino_t inum;
+ unsigned long long atime_sec;
+ unsigned long long ctime_sec;
+ unsigned long long mtime_sec;
+ unsigned int atime_nsec;
+ unsigned int ctime_nsec;
+ unsigned int mtime_nsec;
+};
+
+/**
* struct ubifs_inode - UBIFS in-memory inode description.
+ * @vfs_inode: VFS inode description object
* @creat_sqnum: sequence number at time of creation
* @xattr_size: summarized size of all extended attributes in bytes
* @xattr_cnt: count of extended attributes this inode has
@@ -275,6 +304,7 @@ struct ubifs_gced_idx_leb {
* @data: inode's data
*/
struct ubifs_inode {
+ struct inode vfs_inode;
unsigned long long creat_sqnum;
unsigned int xattr_size;
unsigned int xattr_cnt;
@@ -1640,6 +1670,10 @@ int ubifs_consolidate_log(struct ubifs_info *c);
/* journal.c */
int ubifs_get_dent_type(int mode);
+int ubifs_jnl_update_file(struct ubifs_info *c,
+ const struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm,
+ const struct ubifs_inode *ui);
/* budget.c */
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req);
@@ -1823,6 +1857,17 @@ const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c);
const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);
int ubifs_calc_dark(const struct ubifs_info *c, int spc);
+/* dir.c */
+struct ubifs_inode *ubifs_lookup_by_inum(struct ubifs_info *c, ino_t inum);
+struct ubifs_inode *ubifs_lookup(struct ubifs_info *c,
+ struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm);
+int ubifs_mkdir(struct ubifs_info *c, struct ubifs_inode *dir_ui,
+ const struct fscrypt_name *nm, unsigned int mode);
+int ubifs_link_recovery(struct ubifs_info *c, struct ubifs_inode *dir_ui,
+ struct ubifs_inode *ui, const struct fscrypt_name *nm);
+int ubifs_create_root(struct ubifs_info *c);
+
/* super.c */
int open_ubi(struct ubifs_info *c, const char *node);
void close_ubi(struct ubifs_info *c);
--
2.13.6
From: Huang Xiaojia <[email protected]>
This is the 16/18 step of fsck. Check whether the lost+found is existed,
create a new one if it is not found. This step makes sure that disconnected
file can be recovered under the lost+found.
Signed-off-by: Huang Xiaojia <[email protected]>
Signed-off-by: Zhihao Cheng <[email protected]>
---
ubifs-utils/Makemodule.am | 3 +-
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 15 ++++-
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 5 ++
ubifs-utils/fsck.ubifs/handle_disconnected.c | 89 ++++++++++++++++++++++++++++
4 files changed, 110 insertions(+), 2 deletions(-)
create mode 100644 ubifs-utils/fsck.ubifs/handle_disconnected.c
diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am
index f63ca7a2..77a9be1a 100644
--- a/ubifs-utils/Makemodule.am
+++ b/ubifs-utils/Makemodule.am
@@ -85,7 +85,8 @@ fsck_ubifs_SOURCES = \
ubifs-utils/fsck.ubifs/extract_files.c \
ubifs-utils/fsck.ubifs/rebuild_fs.c \
ubifs-utils/fsck.ubifs/check_files.c \
- ubifs-utils/fsck.ubifs/check_space.c
+ ubifs-utils/fsck.ubifs/check_space.c \
+ ubifs-utils/fsck.ubifs/handle_disconnected.c
fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm -lpthread
fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 584965fc..0910676c 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -526,8 +526,20 @@ static int do_fsck(void)
log_out(c, "Check and create root dir");
err = check_and_create_root(c);
- if (err)
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
+
+ if (list_empty(&FSCK(c)->disconnected_files))
+ return err;
+
+ log_out(c, "Check and create lost+found");
+ err = check_and_create_lost_found(c);
+ if (err) {
exit_code |= FSCK_ERROR;
+ goto free_disconnected_files_2;
+ }
free_disconnected_files_2:
destroy_file_list(c, &FSCK(c)->disconnected_files);
@@ -583,6 +595,7 @@ int main(int argc, char *argv[])
* Step 13: Commit problem fixing modifications
* Step 14: Check and correct the index size
* Step 15: Check and create root dir
+ * Step 16: Check and create lost+found
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 5c305ba0..dc24e83c 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -277,6 +277,7 @@ struct ubifs_rebuild_info {
* @lpts: lprops table
* @try_rebuild: %true means that try to rebuild fs when fsck failed
* @rebuild: rebuilding-related information
+ * @lost_and_found: inode number of the lost+found directory, %0 means invalid
*/
struct ubifs_fsck_info {
int mode;
@@ -288,6 +289,7 @@ struct ubifs_fsck_info {
struct ubifs_lprops *lpts;
bool try_rebuild;
struct ubifs_rebuild_info *rebuild;
+ ino_t lost_and_found;
};
#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
@@ -384,4 +386,7 @@ int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
int check_and_correct_space(struct ubifs_info *c);
int check_and_correct_index_size(struct ubifs_info *c);
+/* handle_disconnected.c */
+int check_and_create_lost_found(struct ubifs_info *c);
+
#endif
diff --git a/ubifs-utils/fsck.ubifs/handle_disconnected.c b/ubifs-utils/fsck.ubifs/handle_disconnected.c
new file mode 100644
index 00000000..b9a380f9
--- /dev/null
+++ b/ubifs-utils/fsck.ubifs/handle_disconnected.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Huang Xiaojia <[email protected]>
+ * Zhihao Cheng <[email protected]>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "linux_err.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "fsck.ubifs.h"
+
+#define LOST_FOUND_DIR_NAME "lost+found"
+
+/**
+ * check_and_create_lost_found - Check and create the lost+found directory.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks whether the lost+found directory exists and creates a
+ * new one if no valid lost+found existing. If there is a valid lost+found
+ * directory, inode number is stored in @FSCK(c)->lost_and_found. Returns zero
+ * in case of success, a negative error code in case of failure.
+ */
+int check_and_create_lost_found(struct ubifs_info *c)
+{
+ struct ubifs_inode *root_ui, *lost_found_ui;
+ struct fscrypt_name nm;
+ int err = 0;
+
+ root_ui = ubifs_lookup_by_inum(c, UBIFS_ROOT_INO);
+ if (IS_ERR(root_ui)) {
+ err = PTR_ERR(root_ui);
+ /* Previous step ensures that the root dir is valid. */
+ ubifs_assert(c, err != -ENOENT);
+ return err;
+ }
+
+ if (root_ui->flags & UBIFS_CRYPT_FL) {
+ ubifs_msg(c, "The root dir is encrypted, skip checking lost+found");
+ goto free_root;
+ }
+
+ fname_name(&nm) = LOST_FOUND_DIR_NAME;
+ fname_len(&nm) = strlen(LOST_FOUND_DIR_NAME);
+ lost_found_ui = ubifs_lookup(c, root_ui, &nm);
+ if (IS_ERR(lost_found_ui)) {
+ err = PTR_ERR(lost_found_ui);
+ if (err != -ENOENT)
+ goto free_root;
+
+ /* Not found. Create a new lost+found. */
+ err = ubifs_mkdir(c, root_ui, &nm, S_IRUGO | S_IWUSR | S_IXUSR);
+ if (err < 0) {
+ if (err == -ENOSPC) {
+ ubifs_msg(c, "No free space to create lost+found");
+ err = 0;
+ }
+ goto free_root;
+ }
+ lost_found_ui = ubifs_lookup(c, root_ui, &nm);
+ if (IS_ERR(lost_found_ui)) {
+ err = PTR_ERR(lost_found_ui);
+ ubifs_assert(c, err != -ENOENT);
+ goto free_root;
+ }
+ FSCK(c)->lost_and_found = lost_found_ui->vfs_inode.inum;
+ ubifs_msg(c, "Create the lost+found");
+ } else if (!(lost_found_ui->flags & UBIFS_CRYPT_FL) &&
+ S_ISDIR(lost_found_ui->vfs_inode.mode)) {
+ FSCK(c)->lost_and_found = lost_found_ui->vfs_inode.inum;
+ } else {
+ ubifs_msg(c, "The type of lost+found is %s%s",
+ ubifs_get_type_name(ubifs_get_dent_type(lost_found_ui->vfs_inode.mode)),
+ lost_found_ui->flags & UBIFS_CRYPT_FL ? ", encrypted" : "");
+ }
+
+ kfree(lost_found_ui);
+free_root:
+ kfree(root_ui);
+ return err;
+}
--
2.13.6
Authenticated UBIFS image is not supported in fsck, add testcase
to check that.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/authentication_refuse.sh.in | 66 ++++++++++++++++++++++
4 files changed, 71 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh.in
diff --git a/.gitignore b/.gitignore
index de0fce1f..799290a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,7 @@ tests/fs-tests/stress/fs_stress01.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
tests/ubifs_tools-tests/lib/common.sh
+tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index d32541a1..160fa812 100644
--- a/configure.ac
+++ b/configure.ac
@@ -290,7 +290,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/fs-tests/stress/fs_stress01.sh
tests/ubi-tests/runubitests.sh
tests/ubi-tests/ubi-stress-test.sh
- tests/ubifs_tools-tests/lib/common.sh])
+ tests/ubifs_tools-tests/lib/common.sh
+ tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 265c9cc7..6b533982 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -1,2 +1,3 @@
test_SCRIPTS += \
- tests/ubifs_tools-tests/lib/common.sh
+ tests/ubifs_tools-tests/lib/common.sh \
+ tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh.in b/tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh.in
new file mode 100755
index 00000000..268a7de1
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh.in
@@ -0,0 +1,66 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# Refuse checking authenticated UBIFS image
+# Running time: 10s
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+ID="0xec,0xa1,0x00,0x15" # 128M 128KB 2KB 512-sub-page
+
+function run_test()
+{
+ echo "Do authentication_refused test"
+
+ modprobe nandsim id_bytes=$ID
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ flash_eraseall /dev/mtd$mtdnum
+
+ modprobe ubi mtd="$mtdnum,2048" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+
+ mount_ubifs $DEV $MNT "authentication" || fatal "mount ubifs failed"
+ fsstress -d $MNT/fsstress -l0 -p4 -n10000 &
+ sleep $((RANDOM % 5))
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ fsck.ubifs -a $DEV # 'fsck.ubifs $DEV' is fine too.
+ res=$?
+ if [[ $res == $FSCK_OK ]]
+ then
+ fatal "fsck should not be success!"
+ fi
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r nandsim
+}
+
+start_t=$(date +%s)
+run_test
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
Inject powercut while doing fsstress on mounted UBIFS, check the
consistency of UBIFS after fsck.
This testscase mainly makes sure that fsck.ubifs can make UBIFS
image be consistent in common stress cases and powercut cases.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/cycle_powercut_mount_fsck.sh.in | 144 +++++++++++++++++++++
4 files changed, 149 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh.in
diff --git a/.gitignore b/.gitignore
index ea99a614..1d150f7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,6 +118,7 @@ tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
+tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index ed8a5f12..1a7174a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -294,7 +294,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
- tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh])
+ tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 932a2bcb..2c190e2b 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -3,4 +3,5 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh \
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh \
- tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh \
+ tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh.in b/tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh.in
new file mode 100755
index 00000000..fac5a033
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh.in
@@ -0,0 +1,144 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# Do many cycles of mount/fsstress/powercut/umount/fsck/mount, check whether
+# mount is successful.
+# Running time: 9h
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+ID="0x20,0xa7,0x00,0x26" # 4G 256KB 4KB 2KB-sub-page
+
+function run_test()
+{
+ local encryption=$1
+
+ echo "Do cycle mount+powercut+fsck+umount($encryption) test"
+ modprobe nandsim id_bytes=$ID
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ flash_eraseall /dev/mtd$mtdnum
+
+ dmesg -c > /dev/null
+
+ modprobe ubi mtd="$mtdnum,4096" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ fi
+
+ round=0
+ while [[ $round -lt 60 ]]
+ do
+ echo "---------------------- ROUND $round ----------------------"
+ let round=$round+1
+
+ mount_ubifs $DEV $MNT || fatal "mount ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_set_key $MNT
+ fi
+
+ if [[ $(($round % 30)) == 0 ]]
+ then
+ echo "Clean files"
+ rm -rf $MNT/*
+ check_err_msg
+ fi
+
+ fsstress -d $MNT -l0 -p4 -n10000 &
+ sleep $((RANDOM % 30))
+
+ per=`df -Th | grep ubifs | awk '{print $6}'`;
+ if [[ ${per%?} -gt 95 ]]; then
+ dmesg -c > /dev/null # The ENOSPC error messages may exist
+ else
+ check_err_msg # Make sure new operations are okay after fsck
+ fi
+ powercut
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ fsck.ubifs -a $DEV 2>&1 > $LOG_FILE
+ res=$?
+ cat $LOG_FILE
+ if [[ $res != $FSCK_OK ]]
+ then
+ # Powercut during layout_leb_in_gaps may change index
+ # LEBs without updating LPT.
+ log=`cat $LOG_FILE | grep "Inconsistent properties" | grep "is_idx 1"`
+ if [[ "$log" == "" ]]; then
+ fatal "fsck fail $res"
+ fi
+ if [[ $res != $FSCK_NONDESTRUCT ]]; then
+ fatal "fsck fail $res"
+ fi
+ fi
+
+ dmesg -c > /dev/null # powercut could reproduce error messages
+
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_set_key $MNT
+ fi
+
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+ check_err_msg
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+
+ check_err_msg
+
+ disable_chkfs
+ done
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r nandsim
+}
+
+check_fsstress
+start_t=$(date +%s)
+run_test "encrypted"
+run_test "noencrypted"
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
Add document for fsck.ubifs and mkfs.ubifs testcases, explain all
testcases and how to run them.
Signed-off-by: Zhihao Cheng <[email protected]>
---
tests/ubifs_tools-tests/README.txt | 303 +++++++++++++++++++++++++++++++++++++
1 file changed, 303 insertions(+)
create mode 100644 tests/ubifs_tools-tests/README.txt
diff --git a/tests/ubifs_tools-tests/README.txt b/tests/ubifs_tools-tests/README.txt
new file mode 100644
index 00000000..3848e23f
--- /dev/null
+++ b/tests/ubifs_tools-tests/README.txt
@@ -0,0 +1,303 @@
+
+ ubifs_tools tests
+ ==================
+
+ There are seven testcases for fsck.ubifs on encryption/non-encryption
+ situations:
+ 1) authentication_refuse: Currently authenticated UBIFS image is not
+ supported for fsck.ubifs, check whether fsck.ubifs can refuse the
+ authenticated UBIFS image.
+ 2) random_corrupted_fsck: Inject random corruption on UBIFS image
+ by writting random data on kinds of mtd devices (eg. nand, nor),
+ check the consistency of UBIFS after fsck.
+ This testcase simulate random bad UBIFS image caused by hardware
+ exceptions(eg. ecc uncorrectable, unwritten), and makes sure that
+ fsck.ubifs could make UBIFS be consistent after repairing UBIFS
+ image.
+ 3) cycle_corrupted_fsck_fault_inject: Inject memory/io fault while
+ doing fsck for corrupted UBIFS images.
+ This testcase mainly checks whether fsck.ubifs has problems (eg.
+ UAF, null-ptr-def, etc.) in random error paths. Besides, it
+ provides a similar way to simulate powercut during fsck, and
+ checks whether the fsck.ubifs can fix an UBIFS image after many
+ rounds interrupted by kinds of errors.
+ 4) cycle_powercut_mount_fsck: Inject powercut while doing fsstress
+ on mounted UBIFS, check the consistency of UBIFS after fsck.
+ This testscase mainly makes sure that fsck.ubifs can make UBIFS
+ image be consistent in common stress cases and powercut cases.
+ 5) powercut_fsck_mount: Inject powercut while doing fsstress on
+ mounted UBIFS for kinds of flashes (eg. nand, nor).
+ This testcase mainly makes sure that fsck.ubifs can make UBIFS
+ image be consistent on different flashes (eg. nand, nor). Because
+ the min_io_size of nor flash is 1, the UBIFS image on nor flash
+ will be different from nand flash after doing powercut, so we need
+ make sure fsck.ubifs can handle these two types of flash.
+ 6) cycle_mount_fsck_check: Do fsstress and fsck ubifs image, make
+ sure all files(and their data) are not lost after fsck.
+ This testcase mainly checks whether fsck.ubifs could corrupt the
+ filesystem content in common case.
+ 7) fsck_bad_image: For kinds of inconsistent UBIFS images(which
+ can simulate corruptions caused by some potentional UBIFS bug), check
+ the result of fsck.
+ This testcase mainly checks whether the behavior is in expected after
+ repairing specific inconsistent UBIFS image. There is no debugfs tools
+ (for example: debugfs[ext4], xfs_db) for UBIFS, so no way to inject
+ precise corruption into UBIFS image, we have to prepare inconsistent
+ UBIFS images in advance like e2fsprogs[1] does. (Goto [2] to see how to
+ generate inconsistent UBIFS images).
+ Original UBIFS image content:
+ /
+ ├── corrupt_file (xattr - user.corrupt:123, 2K data)
+ ├── dir
+ │ ├── block_dev
+ │ ├── char_dev
+ │ ├── dir
+ │ └── file (content: '123')
+ ├── hardl_corrupt_file => corrupt_file
+ └── softl_corrupt_file -> corrupt_file
+ Here's a descriptons of the various test images:
+ =========================================================================
+ image | Description | expectancy
+ -------------------------------------------------------------------------
+ good | good image contains | fsck success, fs content is
+ | kinds of files. | not changed.
+ -------------------------------------------------------------------------
+ sb_fanout | invalid fanout in | fsck failed.
+ | superblock. |
+ -------------------------------------------------------------------------
+ sb_fmt_version | invalid fmt_version | fsck failed.
+ | in superblock. |
+ -------------------------------------------------------------------------
+ sb_leb_size | invalid leb_size in | fsck failed.
+ | superblock. |
+ -------------------------------------------------------------------------
+ sb_log_lebs | invalid log lebs in | fsck failed.
+ | superblock. |
+ -------------------------------------------------------------------------
+ sb_min_io_size | invalid min_io_size | fsck failed.
+ | in superblock. |
+ -------------------------------------------------------------------------
+ master_highest_inum | invalid highest_inum| fsck success, fs content is
+ | in master nodes. | not changed.
+ -------------------------------------------------------------------------
+ master_lpt | bad lpt pos in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ master_tnc | bad tnc pos in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ master_total_dead | bad total_dead in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ master_total_dirty | bad total_dirty in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ master_total_free | bad total_free in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ journal_log | corrupted log area. | fsck success, fs content is
+ | | not changed.
+ -------------------------------------------------------------------------
+ journal_bud | corrupted bud area. | fsck success, file data is
+ | | lost.
+ -------------------------------------------------------------------------
+ orphan_node | bad orphan node. | fsck success, file is
+ | | deleted as expected.
+ -------------------------------------------------------------------------
+ lpt_dirty | bad dirty in pnode. | fsck success, fs content is
+ | | not changed.
+ -------------------------------------------------------------------------
+ lpt_flags | bad flags in pnode | fsck success, fs content is
+ | (eg. index). | not changed.
+ -------------------------------------------------------------------------
+ lpt_free | bad free in pnode. | fsck success, fs content is
+ | | not changed.
+ -------------------------------------------------------------------------
+ lpt_pos | bad pos in nnode. | fsck success, fs content is
+ | | not changed.
+ -------------------------------------------------------------------------
+ ltab_dirty | bad dirty in lprops | fsck success, fs content is
+ | table. | not changed.
+ -------------------------------------------------------------------------
+ ltab_free | bad free in lprops | fsck success, fs content is
+ | table. | not changed.
+ -------------------------------------------------------------------------
+ index_size | bad index size in | fsck success, fs content is
+ | master nodes. | not changed.
+ -------------------------------------------------------------------------
+ tnc_lv0_key | bad key in lv0 | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ tnc_lv0_len | bad len in lv0 | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ tnc_lv0_pos | bad pos in lv0 | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ tnc_noleaf_key | bad key in non-leaf | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ tnc_noleaf_len | bad len in non-leaf | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ tnc_noleaf_pos | bad pos in non-leaf | fsck success, fs content is
+ | znode. | not changed.
+ -------------------------------------------------------------------------
+ corrupted_data_leb | corrupted data leb. | fsck success, partial data of
+ | | file is lost.
+ -------------------------------------------------------------------------
+ corrupted_idx_leb | corrupted index leb.| fsck success, fs content is
+ | | not changed.
+ -------------------------------------------------------------------------
+ inode_data | bad data node. | fsck success, file content
+ | | is changed, other files are
+ | | not changed.
+ -------------------------------------------------------------------------
+ inode_mode | bad inode mode for | fsck success, file is
+ | file. | dropped, other files are not
+ | | changed.
+ -------------------------------------------------------------------------
+ inode_nlink | wrong nlink for | fsck success, nlink is
+ | file. | corrected, fs content is not
+ | | changed.
+ -------------------------------------------------------------------------
+ inode_size | wrong inode size | fsck success, inode size is
+ | for file. | corrected, fs content is not
+ | | changed.
+ -------------------------------------------------------------------------
+ inode_xcnt | wrong inode | fsck success, xattr_cnt is
+ | xattr_cnt for file. | corrected, fs content is not
+ | | changed.
+ -------------------------------------------------------------------------
+ soft_link_inode_mode| bad inode mode for | fsck success, soft link
+ | solf link file. | file is dropped, other files
+ | | are not changed.
+ -------------------------------------------------------------------------
+ soft_link_data_len | bad inode data_len | fsck success, soft link
+ | for solt link file. | file is dropped, other files
+ | | are not changed.
+ -------------------------------------------------------------------------
+ dentry_key | bad dentry key for | fsck success, dentry is
+ | file. | removed, other files are
+ | | not changed.
+ -------------------------------------------------------------------------
+ dentry_nlen | inconsistent nlen | fsck success, dentry is
+ | and name in dentry | removed, other files are
+ | for file. | not changed.
+ -------------------------------------------------------------------------
+ dentry_type | inconsistent type | fsck success, dentry is
+ | between dentry and | removed, other files are
+ | inode for file. | not changed.
+ -------------------------------------------------------------------------
+ xinode_flags | lost UBIFS_XATTR_FL | fsck success, xattr is
+ | in xattr inode | removed, other files are
+ | flags for file. | not changed.
+ -------------------------------------------------------------------------
+ xinode_key | bad xattr inode key | fsck success, xattr is
+ | for file. | removed, other files are
+ | | not changed.
+ -------------------------------------------------------------------------
+ xinode_mode | bad xattr inode | fsck success, xattr is
+ | mode for file. | removed, other files are
+ | | not changed.
+ -------------------------------------------------------------------------
+ xentry_key | bad xattr entry key | fsck success, xattr is
+ | for file. | removed, other files are
+ | | not changed.
+ -------------------------------------------------------------------------
+ xentry_nlen | inconsistent nlen | fsck success, xattr is
+ | and name in xattr | removed, other files are
+ | entry for file. | not changed.
+ -------------------------------------------------------------------------
+ xentry_type | inconsistent type | fsck success, xattr is
+ | between xattr entry | removed, other files are
+ | and xattr inode for | not changed.
+ | file. |
+ -------------------------------------------------------------------------
+ xent_host | the xattr's host | fsck success, file, hard
+ | is a xattr too, the | link and soft link are
+ | flag of corrupt_file| dropped, other files are
+ | inode is modified. | not changed.
+ -------------------------------------------------------------------------
+ dir_many_dentry | dir has too many | fsck success, hard link is
+ | dentries, the dentry| dropped, other files are not
+ | of hard link is | changed.
+ | modified. |
+ -------------------------------------------------------------------------
+ dir_lost | bad dentry for dir. | fsck success, the 'file' is
+ | | recovered under lost+found,
+ | | left files under dir are
+ | | removed, other files are not
+ | | changed.
+ -------------------------------------------------------------------------
+ dir_lost_duplicated | bad inode for dir, | fsck success, the 'file' is
+ | there is a file | recovered with INO_<inum>_1
+ | under lost+found, | under lost+found, left files
+ | which named with the| under dir are removed, other
+ | inum of the 'file'. | files are not changed.
+ -------------------------------------------------------------------------
+ dir_lost_not_recover| bad inode for dir, | fsck success, all files
+ | lost+found is a | under dir are removed,
+ | regular file and | other files are not changed.
+ | exists under root |
+ | dir. |
+ -------------------------------------------------------------------------
+ root_dir | bad '/'. | fsck success, create new
+ | | root dir('/'). All regular
+ | | files are reocovered under
+ | | lost+found, other files are
+ | | removed.
+ -------------------------------------------------------------------------
+ empty_tnc | all files have bad | fsck success, fs content
+ | inode. | becomes empty.
+ =========================================================================
+
+ There is one testcase for mkfs.ubifs on encryption/non-encryption
+ situations:
+ 1) build_fs_from_dir: Initialize UBIFS image from a given directory, then
+ check whether the fs content in mounted UBIFS is consistent with the
+ original directory. Both UBI volume and file are chosen as storage
+ mediums to test. This testcase mainly ensures that mkfs.ubifs can
+ format an UBIFS image as user expected.
+
+ Dependence
+ ----------
+ kernel configs:
+ CONFIG_MTD_NAND_NANDSIM=m
+ CONFIG_MTD_MTDRAM=m
+ CONFIG_MTD_UBI=m
+ CONFIG_UBIFS_FS=m
+ CONFIG_UBIFS_FS_XATTR=y
+ CONFIG_UBIFS_FS_AUTHENTICATION=y
+ CONFIG_FS_ENCRYPTION=y
+ CONFIG_FAILSLAB=y
+ CONFIG_FAIL_PAGE_ALLOC=y
+
+ tools:
+ fsstress [3][4]
+ keyctl [5]
+ fscryptctl [6]
+ setfattr/getfattr [7]
+
+ Running
+ -------
+
+ Please build and install mtd-utils first.
+ Run single case:
+ cd $INSTALL_DIR/libexec/mtd-utils
+ ./powercut_fsck_mount.sh
+ ./random_corrupted_fsck.sh
+ ./cycle_mount_fsck_check.sh
+ ./build_fs_from_dir.sh
+ Run all cases: sh $INSTALL_DIR/libexec/mtd-utils/ubifs_tools_run_all.sh
+
+ References
+ ----------
+
+ [1] https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/tests/README
+ [2] https://bugzilla.kernel.org/show_bug.cgi?id=218924
+ [3] https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git/tree/ltp/fsstress.c
+ [4] https://github.com/linux-test-project/ltp/blob/master/testcases/kernel/fs/fsstress/fsstress.c
+ [5] https://github.com/torvalds/linux/blob/master/security/keys/keyctl.c
+ [6] https://github.com/google/fscryptctl
+ [7] https://github.com/philips/attr/tree/master
--
2.13.6
Initialize UBIFS image from a given directory, then check whether the
fs content in mounted UBIFS is consistent with the original directory.
Both UBI volume and file are chosen as storage mediums to test.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
tests/ubifs_tools-tests/lib/common.sh.in | 8 +
.../mkfs_tests/build_fs_from_dir.sh.in | 174 +++++++++++++++++++++
5 files changed, 187 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh.in
diff --git a/.gitignore b/.gitignore
index 68fff365..ffbde6e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,7 @@ tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh
+tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 35c99bb6..13974090 100644
--- a/configure.ac
+++ b/configure.ac
@@ -297,7 +297,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
- tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh])
+ tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh
+ tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 1d3d0f29..f4d8a073 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -6,7 +6,8 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh \
tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh \
- tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh
+ tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh \
+ tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh
test_DATA += \
tests/ubifs_tools-tests/images/good.gz \
diff --git a/tests/ubifs_tools-tests/lib/common.sh.in b/tests/ubifs_tools-tests/lib/common.sh.in
index a27fe108..8d1b3c03 100755
--- a/tests/ubifs_tools-tests/lib/common.sh.in
+++ b/tests/ubifs_tools-tests/lib/common.sh.in
@@ -8,8 +8,10 @@ UBI_NUM=0
DEV=/dev/ubi0_0
MNT=/mnt/test_file_system
TMP_FILE=/tmp/ubifs_test_file
+TMP_MNT=/tmp/ubifs_tmp_mnt
LOG_FILE=/tmp/ubifs_log
KEY_FILE=/tmp/key
+IMG_FILE=ubifs.img
nandsim_patt="NAND simulator"
mtdram_patt="mtdram test device"
@@ -35,6 +37,8 @@ function cleanup_handler()
{
local ret="$1"
+ umount $TMP_MNT >/dev/null 2>&1 ||:
+ rm -rf $TMP_MNT >/dev/null 2>&1 ||:
if [ "$ret" == "0" ]; then
umount $MNT >/dev/null 2>&1 ||:
modprobe -r ubifs >/dev/null 2>&1 ||:
@@ -45,6 +49,7 @@ function cleanup_handler()
rm -f $TMP_FILE >/dev/null 2>&1 ||:
rm -f $KEY_FILE >/dev/null 2>&1 ||:
rm -f $LOG_FILE >/dev/null 2>&1 ||:
+ rm -f $IMG_FILE >/dev/null 2>&1 ||:
exit 0
else
exit 1
@@ -344,6 +349,9 @@ function cancel_io_err()
if ! [ -d $MNT ]; then
mkdir -p $MNT
fi
+if ! [ -d $TMP_MNT ]; then
+ mkdir -p $TMP_MNT
+fi
modprobe ubi || fatal "common.sh: cannot load ubi"
modprobe ubifs || fatal "common.sh: cannot load ubifs"
diff --git a/tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh.in b/tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh.in
new file mode 100755
index 00000000..0b228562
--- /dev/null
+++ b/tests/ubifs_tools-tests/mkfs_tests/build_fs_from_dir.sh.in
@@ -0,0 +1,174 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# Initialize UBIFS image from a given directory, then check whether the
+# fs content in mounted UBIFS is consistent with the original directory.
+# Both UBI volume and file are chosen as storage mediums to test.
+# Running time: 10min
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+function run_test()
+{
+ local simulator="$1";
+ local size="$2";
+ local peb_size="$3";
+ local page_size="$4";
+ local vid_offs=$page_size;
+ local encryption=$5;
+ local test_medium=$6;
+ local space_fix=$7;
+ local need_fsck=$8;
+ local double_mkfs=$9;
+ local leb_size=$(($(($peb_size*1024))-$page_size));
+ VIDHDR_SZ=64;
+
+ option=""
+ if [[ "$space_fix" == "fix_space" ]]; then
+ option="-F"
+ fi
+ echo "======================================================================"
+ printf "%s" "$simulator: ${size}MiB PEB size ${peb_size}KiB"
+ if [ "$simulator" = "mtdram" ]; then
+ page_size=8
+ leb_size=$(($leb_size-$VIDHDR_SZ))
+ else
+ leb_size=$(($leb_size-$page_size))
+ fi
+ printf " %s" "page size ${page_size}Bytes"
+ printf " $encryption $test_medium $space_fix $need_fsck $double_mkfs\n"
+
+ if [ "$simulator" = "nandsim" ]; then
+ $TESTBINDIR/load_nandsim.sh "$size" "$peb_size" "$page_size" || echo "cannot load nandsim";
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ elif [ "$simulator" = "mtdram" ]; then
+ load_mtdram "$size" "$peb_size" || echo "cannot load mtdram"
+ mtdnum="$(find_mtd_device "$mtdram_patt")"
+ else
+ fatal "$simulator is not supported"
+ fi
+
+ dmesg -c > /dev/null
+ flash_eraseall /dev/mtd$mtdnum
+ modprobe ubi mtd="$mtdnum,$vid_offs" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ option="$option --cipher AES-256-XTS --key $KEY_FILE"
+ fi
+
+ if [[ "$test_medium" == "volume" ]]; then
+ mkfs.ubifs $option -m${page_size} -c 1024 -e $leb_size -f 4 -r $TMP_MNT $DEV
+ if [[ $? != 0 ]]; then
+ fatal "mkfs failed"
+ fi
+ if [[ "$double_mkfs" == "double_format" ]]; then
+ mkfs.ubifs -y $option -m${page_size} -c 1024 -e $leb_size -f 4 -r $TMP_MNT $DEV
+ if [[ $? != 0 ]]; then
+ fatal "mkfs failed"
+ fi
+ fi
+ else
+ mkfs.ubifs $option -m${page_size} -c 1024 -e $leb_size -f 4 -r $TMP_MNT -o $IMG_FILE
+ if [[ $? != 0 ]]; then
+ fatal "mkfs failed"
+ fi
+ ubiupdatevol $DEV $IMG_FILE
+ if [[ $? != 0 ]]; then
+ fatal "ubiupdatevol failed"
+ fi
+ fi
+
+ if [[ "$need_fsck" == "do_fsck" ]]; then
+ fsck.ubifs -a $DEV # 'fsck.ubifs $DEV' is fine too.
+ res=$?
+ if [[ $res != $FSCK_OK ]]
+ then
+ fatal "fsck expects result $FSCK_OK, but $res is returned"
+ fi
+ fi
+
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" != "encrypted" ]]; then
+ # Check filesystem information, skip encrypted image.
+ # fscryptctl is not compatible with fscryptctl in mtd-utils.
+ # See https://github.com/google/fscryptctl/issues/33
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+
+ parse_dir "md5sum"
+ fi
+
+ check_err_msg
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+
+ check_err_msg
+ disable_chkfs
+
+ if [[ "$test_medium" != "volume" ]]; then
+ rm -f $IMG_FILE
+ fi
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r $simulator
+
+ echo "----------------------------------------------------------------------"
+}
+
+check_fsstress
+start_t=$(date +%s)
+echo "Do mkfs+fsck+mount test in kinds of flashes"
+mount -t tmpfs -osize=50m none $TMP_MNT || fatal "cannot mount tmpfs"
+echo 123 > $TMP_MNT/file
+setfattr -n user.xyz -v 123abc $TMP_MNT/file
+fsstress -d $TMP_MNT -l30 -n10 -p4
+# Record filesystem information
+rm -f $TMP_FILE 2>/dev/null
+read_dir $TMP_MNT "md5sum"
+
+# No authentication tests, which needs a specific key from certs directory corresponding to linux source code..
+# See https://patchwork.ozlabs.org/project/linux-mtd/cover/[email protected]/
+for simulator in "mtdram" "nandsim"; do
+ for encryption in "encrypted" "noencrypted"; do
+ for test_medium in "volume" "file"; do
+ for space_fix in "fix_space" "nofix_space"; do
+ for need_fsck in "do_fsck" "no_fsck"; do
+ for double_mkfs in "double_format" "format_once"; do
+ run_test "$simulator" "64" "64" "2048" $encryption $test_medium $space_fix $need_fsck $double_mkfs
+ run_test "$simulator" "128" "128" "2048" $encryption $test_medium $space_fix $need_fsck $double_mkfs
+ run_test "$simulator" "512" "512" "2048" $encryption $test_medium $space_fix $need_fsck $double_mkfs
+ run_test "$simulator" "1024" "512" "2048" $encryption $test_medium $space_fix $need_fsck $double_mkfs
+ done
+ done
+ done
+ done
+ done
+done
+
+umount $TMP_MNT
+rm -f $TMP_FILE 2>/dev/null
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
For kinds of inconsistent UBIFS images(which can simulate corruptions
caused by some potentional UBIFS bug), check the result of fsck.
This testcase mainly checks whether the behavior is in expected after
repairing specific inconsistent UBIFS image.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../fsck_tests/fsck_bad_image.sh.in | 355 +++++++++++++++++++++
4 files changed, 360 insertions(+), 2 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh.in
diff --git a/.gitignore b/.gitignore
index 203f01f0..68fff365 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,6 +120,7 @@ tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
+tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 545fcc41..35c99bb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -296,7 +296,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh
- tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh])
+ tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
+ tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index 7ef873ce..1d3d0f29 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -5,7 +5,8 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_powercut_mount_fsck.sh \
- tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh
+ tests/ubifs_tools-tests/fsck_tests/random_corrupted_fsck.sh \
+ tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh
test_DATA += \
tests/ubifs_tools-tests/images/good.gz \
diff --git a/tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh.in b/tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh.in
new file mode 100755
index 00000000..2ba9f789
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/fsck_bad_image.sh.in
@@ -0,0 +1,355 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description: Tests whether all inconsistent UBIFS images can be fixed
+# as expected.
+# Origin UBIFS image content:
+# /
+# ├── corrupt_file (xattr - user.corrupt:123, 2K data)
+# ├── dir
+# │ ├── block_dev
+# │ ├── char_dev
+# │ ├── dir
+# │ └── file (content: '123')
+# ├── hardl_corrupt_file => corrupt_file
+# └── softl_corrupt_file -> corrupt_file
+#
+# Running time: 2min
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+CORRUPT_FILE=corrupt_file
+CORRUPT_FILE_INUM=INO_65
+XATTR_NAME="user.corrupt"
+XATTR_VAL=123
+CORRUPT_FILE_MD5=7d2f953e91033c743ab6a801d5ee6e15
+SOFT_LINK_FILE=softl_corrupt_file
+HARD_LINK_FILE=hardl_corrupt_file
+DIR=dir
+BLOCK_DEV=block_dev
+CHAR_DEV=char_dev
+FILE=file
+FILE_INUM=INO_72
+FILE_MD5=ba1f2511fc30423bdbb183fe33f3dd0f
+LOST_FOUND="lost+found"
+
+function fsck_image()
+{
+ local img_type=$1;
+ local img=$2;
+ local fsck_mode=$3;
+ local file_exist=$4;
+ local file_nochange=$5
+ local file_xattr_exist=$6;
+ local hard_link_exist=$7;
+ local hard_link_no_change=$8;
+ local hard_link_xattr_exist=$9;
+ local soft_link_exist=${10};
+ local dir_exist=${11};
+ local dir_file_no_change=${12};
+ local lost_found=${13};
+
+ echo "======================================================================"
+ echo "fsck $img_type, success_fsck_mode:$fsck_mode file_exist:$file_exist file_nochange:$file_nochange file_xattr_exist:$file_xattr_exist hard_link_exist:$hard_link_exist hard_link_no_change:$hard_link_no_change:hard_link_xattr_exist $hard_link_xattr_exist:soft_link_exist:$soft_link_exist dir_exist:$dir_exist $lost_found"
+
+ load_mtdram 1 16 || echo "cannot load mtdram"
+ mtdnum="$(find_mtd_device "$mtdram_patt")"
+
+ dmesg -c > /dev/null
+ gzip -f -k -d $TESTBINDIR/${img}.gz || fatal "gzip failed"
+ flash_eraseall /dev/mtd$mtdnum
+ dd if=$TESTBINDIR/$img of=/dev/mtd$mtdnum bs=1M
+ rm -f $TESTBINDIR/$img
+ modprobe ubi mtd="$mtdnum,0" || fatal "modprobe ubi fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+
+ fsck.ubifs -a $DEV
+ let "ret=$?&~$FSCK_NONDESTRUCT"
+ if [[ $ret != $FSCK_OK ]]; then
+ if [[ "$fsck_mode" == "safe" ]]; then
+ fatal "The image should be fixed in $fsck_mode mode, but it fails"
+ fi
+
+ fsck.ubifs -y $DEV
+ let "ret=$?&~$FSCK_NONDESTRUCT"
+ if [[ $ret != $FSCK_OK ]]; then
+ if [[ "$fsck_mode" == "danger_default" ]]; then
+ fatal "The image should be fixed in $fsck_mode mode, but it fails"
+ fi
+
+ fsck.ubifs -yb $DEV
+ let "ret=$?&~$FSCK_NONDESTRUCT"
+ if [[ $ret != $FSCK_OK ]]; then
+ if [[ "$fsck_mode" == "danger_rebuild" ]]; then
+ fatal "The image should be fixed in $fsck_mode mode, but it fails"
+ fi
+
+ echo "fsck failed is expected, skip"
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r mtdram
+ echo "----------------------------------------------------------------------"
+
+ return;
+ elif [[ "$fsck_mode" != "danger_rebuild" ]]; then
+ fatal "The image should not be fixed in $fsck_mode mode, but it succeeds"
+ fi
+ elif [[ "$fsck_mode" != "danger_default" ]]; then
+ fatal "The image should not be fixed in $fsck_mode mode, but it succeeds"
+ fi
+ elif [[ "$fsck_mode" != "safe" ]]; then
+ fatal "The image should not be fixed in $fsck_mode mode, but it succeeds"
+ fi
+
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "mount failed $ret"
+ fi
+
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "cannot access all files $ret"
+ fi
+
+ if [[ $file_exist == 1 ]]; then
+ if ! [ -f $MNT/$CORRUPT_FILE ]; then
+ fatal "$MNT/$CORRUPT_FILE is lost"
+ fi
+ else
+ if [ -f $MNT/$CORRUPT_FILE ]; then
+ fatal "$MNT/$CORRUPT_FILE should not exist"
+ fi
+ fi
+
+ md5_after=`md5sum $MNT/$CORRUPT_FILE 2>/dev/null | awk '{print $1}'`
+ if [[ $file_nochange == 1 ]]; then
+ if [[ $CORRUPT_FILE_MD5 != $md5_after ]]; then
+ fatal "content changed for $MNT/$CORRUPT_FILE"
+ fi
+ else
+ if [[ $CORRUPT_FILE_MD5 == $md5_after ]]; then
+ fatal "content not changed for $MNT/$CORRUPT_FILE"
+ fi
+ fi
+
+ xattr=`getfattr -n $XATTR_NAME $MNT/$CORRUPT_FILE 2>/dev/null | grep $XATTR_NAME | awk -F '=' '{ print $2 }'`
+ if [[ $file_xattr_exist == 1 ]]; then
+ if ! [[ "$xattr" =~ "$XATTR_VAL" ]]; then
+ fatal "wrong xattr $xattr for $MNT/$CORRUPT_DENT_NAME"
+ fi
+ else
+ if [[ "$xattr" =~ "$XATTR_VAL" ]]; then
+ fatal "xattr $xattr for $MNT/$CORRUPT_DENT_NAME should not exist"
+ fi
+ fi
+
+ if [[ $hard_link_exist == 1 ]]; then
+ if ! [ -f $MNT/$HARD_LINK_FILE ]; then
+ fatal "$MNT/$HARD_LINK_FILE should is lost"
+ fi
+ else
+ if [ -f $MNT/$HARD_LINK_FILE ]; then
+ fatal "$MNT/$HARD_LINK_FILE should not exist"
+ fi
+ fi
+
+ md5_after=`md5sum $MNT/$HARD_LINK_FILE 2>/dev/null | awk '{print $1}'`
+ if [[ $hard_link_no_change == 1 ]]; then
+ if [[ $CORRUPT_FILE_MD5 != $md5_after ]]; then
+ fatal "content changed for $MNT/$HARD_LINK_FILE"
+ fi
+ else
+ if [[ $CORRUPT_FILE_MD5 == $md5_after ]]; then
+ fatal "content not changed for $MNT/$HARD_LINK_FILE"
+ fi
+ fi
+
+ xattr=`getfattr -n $XATTR_NAME $MNT/$HARD_LINK_FILE 2>/dev/null | grep $XATTR_NAME | awk -F '=' '{ print $2 }'`
+ if [[ $hard_link_xattr_exist == 1 ]]; then
+ if ! [[ "$xattr" =~ "$XATTR_VAL" ]]; then
+ fatal "wrong xattr $xattr for $MNT/$HARD_LINK_FILE"
+ fi
+ else
+ if [[ "$xattr" =~ "$XATTR_VAL" ]]; then
+ fatal "xattr $xattr for $MNT/$HARD_LINK_FILE should not exist"
+ fi
+ fi
+
+ link=`stat -c %N $MNT/$SOFT_LINK_FILE 2>/dev/null | grep $SOFT_LINK_FILE | grep $CORRUPT_FILE`
+ if [[ $soft_link_exist == 1 ]]; then
+ if [[ "$link" == "" ]]; then
+ fatal "$MNT/$SOFT_LINK_FILE is lost"
+ fi
+ else
+ if [[ "$link" != "" ]]; then
+ fatal "$MNT/$SOFT_LINK_FILE should not exist"
+ fi
+ fi
+
+ if [[ $dir_exist == 1 ]]; then
+ if ! [ -d $MNT/$DIR ]; then
+ fatal "$MNT/$DIR is lost"
+ fi
+ if ! [ -d $MNT/$DIR/$DIR ]; then
+ fatal "$MNT/$DIR/$DIR is lost"
+ fi
+ if ! [ -f $MNT/$DIR/$FILE ]; then
+ fatal "$MNT/$DIR/$FILE is lost"
+ fi
+ f_md5=`md5sum $MNT/$DIR/$FILE 2>/dev/null | awk '{print $1}'`
+ if [[ $dir_file_no_change == 1 ]]; then
+ if [[ $FILE_MD5 != $f_md5 ]]; then
+ fatal "content changed for $MNT/$DIR/$FILE"
+ fi
+ else
+ if [[ $FILE_MD5 == $f_md5 ]]; then
+ fatal "content not changed for $MNT/$DIR/$FILE"
+ fi
+ fi
+ if ! [ -b $MNT/$DIR/$BLOCK_DEV ]; then
+ fatal "$MNT/$DIR/$BLOCK_DEV is lost"
+ fi
+ major=`stat -c %t $MNT/$DIR/$BLOCK_DEV`
+ minor=`stat -c %T $MNT/$DIR/$BLOCK_DEV`
+ if [[ $major != 1 ]] || [[ $minor != 2 ]]; then
+ echo "major/minor changed for $MNT/$DIR/$BLOCK_DEV"
+ fi
+ if ! [ -c $MNT/$DIR/$CHAR_DEV ]; then
+ fatal "$MNT/$DIR/$CHAR_DEV is lost"
+ fi
+ major=`stat -c %t $MNT/$DIR/$CHAR_DEV`
+ minor=`stat -c %T $MNT/$DIR/$CHAR_DEV`
+ if [[ $major != 0 ]] || [[ $minor != 1 ]]; then
+ echo "major/minor changed for $MNT/$DIR/$CHAR_DEV"
+ fi
+ else
+ if [ -d $MNT/$DIR ]; then
+ fatal "$MNT/$DIR should not exist"
+ fi
+ fi
+
+ if [[ "$lost_found" == "no lost+found" ]]; then
+ if [ -d $MNT/$LOST_FOUND ]; then
+ fatal "$MNT/$LOST_FOUND should not exist"
+ fi
+ elif [[ "$lost_found" == "lost+found is regular" ]]; then
+ if ! [ -f $MNT/$LOST_FOUND ]; then
+ fatal "$MNT/$LOST_FOUND is not regular file"
+ fi
+ else
+ if ! [ -d $MNT/$LOST_FOUND ]; then
+ fatal "$MNT/$LOST_FOUND is lost"
+ fi
+
+ if ! [ -f $MNT/$LOST_FOUND/${FILE_INUM}_0 ]; then
+ fatal "$MNT/$LOST_FOUND/${FILE_INUM}_0 is lost"
+ fi
+ if [[ "$lost_found" == "lost+found has one" ]]; then
+ f_md5=`md5sum $MNT/$LOST_FOUND/${FILE_INUM}_0 2>/dev/null | awk '{print $1}'`
+ if [[ $FILE_MD5 != $f_md5 ]]; then
+ fatal "content changed for $MNT/$LOST_FOUND/${FILE_INUM}_0"
+ fi
+ elif [[ "$lost_found" == "lost+found has two" ]]; then
+ f_md5=`md5sum $MNT/$LOST_FOUND/${CORRUPT_FILE_INUM}_0 2>/dev/null | awk '{print $1}'`
+ if [[ $CORRUPT_FILE_MD5 != $f_md5 ]]; then
+ fatal "content changed for $MNT/$LOST_FOUND/${CORRUPT_FILE_INUM}_0"
+ fi
+ f_md5=`md5sum $MNT/$LOST_FOUND/${FILE_INUM}_0 2>/dev/null | awk '{print $1}'`
+ if [[ $FILE_MD5 != $f_md5 ]]; then
+ fatal "content changed for $MNT/$LOST_FOUND/${FILE_INUM}_0"
+ fi
+ else
+ if ! [ -f $MNT/$LOST_FOUND/${FILE_INUM}_1 ]; then
+ fatal "$MNT/$LOST_FOUND/${FILE_INUM}_1 is lost"
+ fi
+ f_md5=`md5sum $MNT/$LOST_FOUND/${FILE_INUM}_1 2>/dev/null | awk '{print $1}'`
+ if [[ $FILE_MD5 != $f_md5 ]]; then
+ fatal "content changed for $MNT/$LOST_FOUND/${FILE_INUM}_1"
+ fi
+ fi
+ fi
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+
+ disable_chkfs
+
+ check_err_msg
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe -r mtdram
+
+ echo "----------------------------------------------------------------------"
+}
+
+start_t=$(date +%s)
+echo "Do inconsistent UBIFS images fscking test"
+fsck_image "good image" good "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad sb fanout image" sb_fanout "none" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad sb fmt_version image" sb_fmt_version "none" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad sb leb_size image" sb_leb_size "none" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad sb log_lebs image" sb_log_lebs "none" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad sb min_io_size image" sb_min_io_size "none" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master highest_inum image" master_highest_inum "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master lpt image" master_lpt "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master tnc image" master_tnc "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master total_dead image" master_total_dead "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master total_dirty image" master_total_dirty "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad master total_free image" master_total_free "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "corrupted journal log area image" journal_log "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "corrupted journal bud area image" journal_bud "danger_default" 1 0 1 1 0 1 1 1 0 "no lost+found"
+fsck_image "bad orphan node image" orphan_node "danger_default" 0 0 0 0 0 0 1 1 1 "no lost+found"
+fsck_image "bad lpt dirty image" lpt_dirty "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad lpt lpt_flags image" lpt_flags "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad lpt free image" lpt_free "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad lpt pos image" lpt_pos "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad lprops table dirty image" ltab_dirty "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad lprops table free image" ltab_free "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad index size image" index_size "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc lv0 key image" tnc_lv0_key "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc lv0 len image" tnc_lv0_len "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc lv0 pos image" tnc_lv0_pos "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc non-leaf key image" tnc_noleaf_key "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc non-leaf len image" tnc_noleaf_len "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad tnc non-leaf pos image" tnc_noleaf_pos "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "corrupted leb for file data image" corrupted_data_leb "danger_default" 1 0 1 1 0 1 1 1 1 "no lost+found"
+fsck_image "corrupted leb for TNC image" corrupted_idx_leb "danger_rebuild" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad inode data image" inode_data "danger_default" 1 0 1 1 0 1 1 1 1 "no lost+found"
+fsck_image "bad inode mode image" inode_mode "danger_default" 0 0 0 0 0 0 1 1 1 "no lost+found"
+fsck_image "bad inode nlink image" inode_nlink "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad inode size image" inode_size "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad inode xattr_cnt image" inode_xcnt "safe" 1 1 1 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad softlink inode mode image" soft_link_inode_mode "danger_default" 1 1 1 1 1 1 0 1 1 "no lost+found"
+fsck_image "bad softlink inode data_len image" soft_link_data_len "danger_default" 1 1 1 1 1 1 0 1 1 "no lost+found"
+fsck_image "bad dentry key image" dentry_key "danger_default" 0 0 0 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad dentry nlen image" dentry_nlen "danger_default" 0 0 0 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad dentry type image" dentry_type "danger_default" 0 0 0 1 1 1 1 1 1 "no lost+found"
+fsck_image "bad xattr inode flags image" xinode_flags "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr inode key image" xinode_key "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr inode mode image" xinode_mode "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr dentry key image" xentry_key "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr dentry nlen image" xentry_nlen "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr dentry type image" xentry_type "danger_default" 1 1 0 1 1 0 1 1 1 "no lost+found"
+fsck_image "bad xattr host image" xent_host "danger_default" 0 0 0 0 0 0 1 1 1 "no lost+found"
+fsck_image "dir has too many dentry image" dir_many_dentry "danger_default" 1 1 1 0 0 0 1 1 1 "no lost+found"
+fsck_image "bad dir image" dir_lost "danger_default" 1 1 1 1 1 1 1 0 1 "lost+found has one"
+fsck_image "bad dir and duplicated file name in lost+found image" dir_lost_duplicated "danger_default" 1 1 1 1 1 1 1 0 1 "lost+found has duplicated files"
+fsck_image "bad dir and lost+found image" dir_lost_not_recover "danger_default" 1 1 1 1 1 1 1 0 1 "lost+found is regular"
+fsck_image "bad root dir image" root_dir "danger_default" 0 0 0 0 0 0 0 0 1 "lost+found has two"
+fsck_image "empty TNC image" empty_tnc "danger_rebuild" 0 0 0 0 0 0 0 0 1 "no lost+found"
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
--
2.13.6
Inject memory/io fault while doing fsck for corrupted UBIFS images.
This testcase mainly checks whether fsck.ubifs has problems (eg.
UAF, null-ptr-def, etc.) in random error paths. Besides, it provides
a similar way to simulate powercut during fsck, and checks whether
the fsck.ubifs can fix an UBIFS image after many rounds interrupted
by kinds of errors.
Signed-off-by: Zhihao Cheng <[email protected]>
---
.gitignore | 1 +
configure.ac | 3 +-
tests/ubifs_tools-tests/Makemodule.am | 3 +-
.../cycle_corrupted_fsck_fault_inject.sh.in | 225 +++++++++++++++++++++
tests/ubifs_tools-tests/lib/common.sh.in | 3 +-
5 files changed, 232 insertions(+), 3 deletions(-)
create mode 100755 tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh.in
diff --git a/.gitignore b/.gitignore
index b63b9868..ea99a614 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@ tests/ubifs_tools-tests/lib/common.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
+tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
#
# Files generated by autotools
diff --git a/configure.ac b/configure.ac
index 99ce86c8..ed8a5f12 100644
--- a/configure.ac
+++ b/configure.ac
@@ -293,7 +293,8 @@ AC_CONFIG_FILES([tests/fs-tests/fs_help_all.sh
tests/ubifs_tools-tests/lib/common.sh
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh
- tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh])
+ tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
+ tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh])
AC_OUTPUT([Makefile])
diff --git a/tests/ubifs_tools-tests/Makemodule.am b/tests/ubifs_tools-tests/Makemodule.am
index d54514da..932a2bcb 100644
--- a/tests/ubifs_tools-tests/Makemodule.am
+++ b/tests/ubifs_tools-tests/Makemodule.am
@@ -2,4 +2,5 @@ test_SCRIPTS += \
tests/ubifs_tools-tests/lib/common.sh \
tests/ubifs_tools-tests/fsck_tests/authentication_refuse.sh \
tests/ubifs_tools-tests/fsck_tests/cycle_mount_fsck_check.sh \
- tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh
+ tests/ubifs_tools-tests/fsck_tests/powercut_fsck_mount.sh \
+ tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh
diff --git a/tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh.in b/tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh.in
new file mode 100755
index 00000000..2073fecf
--- /dev/null
+++ b/tests/ubifs_tools-tests/fsck_tests/cycle_corrupted_fsck_fault_inject.sh.in
@@ -0,0 +1,225 @@
+#!/bin/sh
+# Copyright (c), 2024, Huawei Technologies Co, Ltd.
+# Author: Zhihao Cheng <[email protected]>
+#
+# Test Description:
+# For many kinds of flash, do following things
+# 1. mount UBIFS
+# 2. fsstress && unmount
+# 3. inject corruption into UBIFS image randomly
+# 3. fsck UBIFS && inject kinds of errors(memory, io)
+# 4. check UBIFS mounting result
+# Running time: 15min
+
+TESTBINDIR=@TESTBINDIR@
+source $TESTBINDIR/common.sh
+
+function run_test()
+{
+ local simulator="$1";
+ local size="$2";
+ local peb_size="$3";
+ local page_size="$4";
+ local encryption=$5;
+
+ echo "======================================================================"
+ printf "%s" "$simulator: ${size}MiB PEB size ${peb_size}KiB"
+ if [ "$simulator" = "nandsim" ]; then
+ printf " %s" "page size ${page_size}Bytes"
+ fi
+ printf " $encryption\n"
+
+ if [ "$simulator" = "nandsim" ]; then
+ $TESTBINDIR/load_nandsim.sh "$size" "$peb_size" "$page_size" || echo "cannot load nandsim";
+ mtdnum="$(find_mtd_device "$nandsim_patt")"
+ elif [ "$simulator" = "mtdram" ]; then
+ load_mtdram "$size" "$peb_size" || echo "cannot load mtdram"
+ mtdnum="$(find_mtd_device "$mtdram_patt")"
+ else
+ fatal "$simulator is not supported"
+ fi
+
+ flash_eraseall /dev/mtd$mtdnum
+ modprobe ubi mtd="$mtdnum,$page_size" || fatal "modprobe ubi fail"
+ ubimkvol -N vol_test -m -n 0 /dev/ubi$UBI_NUM || fatal "mkvol fail"
+ modprobe ubifs || fatal "modprobe ubifs fail"
+ mount_ubifs $DEV $MNT || fatal "mount ubifs fail"
+ if [[ "$encryption" == "encrypted" ]]; then
+ encryption_gen_key
+ encryption_set_key $MNT
+ fi
+
+ fsstress -d $MNT -l0 -p4 -n10000 &
+
+ sleep $((RANDOM % 20))
+
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ while [ $? -eq 0 ]
+ do
+ killall -9 fsstress > /dev/null 2>&1
+ sleep 1
+ ps -e | grep -w fsstress > /dev/null 2>&1
+ done
+
+ while true
+ do
+ res=`mount | grep "$MNT"`
+ if [[ "$res" == "" ]]
+ then
+ break;
+ fi
+ umount $MNT
+ sleep 0.1
+ done
+
+ # inject corruption
+ times=$((RANDOM % 10))
+ let times=$times+10
+ i=0
+ tot_peb=`cat /sys/class/ubi/ubi$UBI_NUM/total_eraseblocks`;
+
+ modprobe -r ubifs
+ modprobe -r ubi # Stop wear-leveling & erasing worker
+ while [[ $i -lt $times ]]
+ do
+ let i=$i+1;
+ peb=$((RANDOM % $tot_peb));
+ pg=`expr $peb_size \* 1024`;
+ peb_off=`expr $pg \* $peb`
+ pages=`expr $pg / $page_size`;
+ pg=`expr $pages - 2`;
+ pg=$((RANDOM % $pg));
+ pg_off=`expr $pg + 2`;
+ pg_start=`expr $pages \* $peb`;
+ pg=`expr $pg_start + $pg_off`;
+ vid_pg=`expr $pg_start + 1`;
+ dd if=/dev/mtd$mtdnum of=$TMP_FILE bs=$page_size skip=$vid_pg count=1 2>/dev/null;
+ content=`cat $TMP_FILE | grep UBI!`; # vid header magic
+ if [[ "$content" == "" ]]; then
+ # Skip free PEB, otherwise LEB data could be overwritten in UBIFS
+ continue;
+ fi
+ if [[ $((RANDOM % 2)) == 0 ]]; then
+ # Corrupts 1 page
+ dd if=/dev/urandom of=/dev/mtd$mtdnum bs=$page_size seek=$pg count=1;
+ else
+ # Erase 1 LEB, TNC points to an unmapped area
+ flash_erase /dev/mtd$mtdnum $peb_off 1
+ fi
+ done
+ rm -f $TMP_FILE 2>/dev/null
+ sync
+
+ skip=0
+ modprobe ubi mtd="$mtdnum,$page_size"
+ ret=$?
+ if [[ $ret != 0 ]]
+ then
+ skip=1
+ echo "UBI layout volume is corrupted, skip"
+ fi
+
+ if [[ $skip == 0 ]]; then
+ modprobe ubifs || fatal "modprobe ubifs2 fail"
+ dmesg -c > /dev/null
+
+ round=0
+ while [[ $round -lt 50 ]];
+ do
+ let round=$round+1
+ inject_mem=0
+
+ fsck.ubifs -yb $DEV 2>&1 > $LOG_FILE &
+ pid=$!
+ if [[ $((RANDOM % 2)) == 0 ]]; then
+ inject_mem_err $pid
+ inject_mem=1
+ fi
+ inject_io_err
+ wait $pid
+ cat $LOG_FILE
+ if [[ $inject_mem == 1 ]]; then
+ cancel_mem_err
+ fi
+ cancel_io_err
+
+ # UBI could become ro-mode, reload it
+ modprobe -r ubifs
+ modprobe -r ubi
+ modprobe ubi mtd="$mtdnum,$page_size" || fatal "modprobe ubi2 fail"
+ modprobe ubifs || fatal "modprobe ubifs3 fail"
+ done
+
+ fsck.ubifs -yb $DEV 2>&1 > $LOG_FILE
+ res=$?
+ cat $LOG_FILE
+ let "ret=$res&~$FSCK_NONDESTRUCT"
+ if [[ $ret != $FSCK_OK ]]
+ then
+ # Skip superblock error
+ log=`cat $LOG_FILE | grep "bad node at LEB 0:"`
+ if [[ "$log" != "" ]]
+ then
+ skip=1
+ echo "SB is corrupted, skip fsck & mounting"
+ else
+ fatal "fsck fail $res"
+ fi
+ fi
+
+ if [[ $skip == 0 ]]; then
+ enable_chkfs
+
+ mount_ubifs $DEV $MNT "noauthentication" "noatime"
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "mount fail $res"
+ fi
+
+ if [[ "$encryption" == "encrypted" ]]; then
+ # Ignore the encrypting error, root dir could be
+ # corrupted, the new root dir cannot be
+ # encrypted because it is not empty.
+ encryption_set_key $MNT 1
+ fi
+
+ du -sh $MNT > /dev/null # Make sure all files are accessible
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ fatal "Cannot access all files"
+ fi
+ # check_err_msg is not suggested in this testcase, because
+ # ubi_io_read(triggered by wear_leveling_worker -> ubi_eba_copy_leb)
+ # could print stack if ecc uncorrectable errors are detected.
+
+ umount $MNT
+ res=$?
+ if [[ $res != 0 ]]
+ then
+ fatal "unmount fail $res"
+ fi
+ fi
+
+ modprobe -r ubifs
+ modprobe -r ubi
+ fi
+ modprobe -r $simulator
+
+ echo "----------------------------------------------------------------------"
+}
+
+check_fsstress
+start_t=$(date +%s)
+echo "Do corrruption+cycle_fsck_fault_injection test in kinds of flashes"
+for simulator in "mtdram" "nandsim"; do
+ for encryption in "encrypted" "noencrypted"; do
+ run_test "$simulator" "16" "16" "512" $encryption
+ run_test "$simulator" "256" "128" "2048" $encryption
+ run_test "$simulator" "1024" "512" "2048" $encryption
+ done
+done
+end_t=$(date +%s)
+time_cost=$(( end_t - start_t ))
+echo "Success, cost $time_cost seconds"
+exit 0
diff --git a/tests/ubifs_tools-tests/lib/common.sh.in b/tests/ubifs_tools-tests/lib/common.sh.in
index 5a07ebcd..a27fe108 100755
--- a/tests/ubifs_tools-tests/lib/common.sh.in
+++ b/tests/ubifs_tools-tests/lib/common.sh.in
@@ -234,12 +234,13 @@ function encryption_gen_key()
function encryption_set_key()
{
mnt=$1
+ ignore_err=$2
# https://github.com/google/fscryptctl
key=$(fscryptctl add_key $mnt < $KEY_FILE)
fscryptctl set_policy $key $mnt
#fscryptctl get_policy $mnt
ret=$?
- if [[ $ret != 0 ]]; then
+ if [[ $ret != 0 && $ignore_err != 1 ]]; then
fatal "set encryption policy failed"
fi
}
--
2.13.6