| 0/0 |
2026/05/22 00:53 |
flow |
assessment-security |
1h01m
Results: map[DenialOfService:true Explanation:**Analysis of the Bug**
The bug is a heap out-of-bounds read and write (and a `VM_BUG_ON` crash if
`CONFIG_DEBUG_VM` is enabled) in `ocfs2_write_end_inline`.
**Root Cause:**
When writing to an ocfs2 file with inline data, `ocfs2_write_begin_nolock`
checks if the write fits within the inline data limits by calling
`ocfs2_size_fits_inline_data`. This function verifies that the new file size
does not exceed `di->id2.i_data.id_count` (the maximum inline data size recorded
in the on-disk inode).
If `id_count` is corrupted to a large value (e.g., `65535`), the filesystem
allows the inline write to proceed for large offsets. Subsequently,
`ocfs2_write_end_inline` calls `memcpy_from_folio` to copy data from the page
cache folio to the inode's buffer head:
```c
memcpy_from_folio(di->id2.i_data.id_data + pos, wc->w_target_folio, pos,
*copied);
```
Here, `pos` is used as the offset within the folio. If `pos` is larger than the
folio size (typically 4096 bytes), `memcpy_from_folio` reads out-of-bounds from
the folio and writes out-of-bounds into the buffer head data, corrupting
adjacent heap memory.
**Bypassing Validation:**
Under normal circumstances, a corrupted `id_count` is caught by
`ocfs2_validate_inode_block` when the inode is read from disk. The validation
strictly ensures that `id_count` is less than or equal to the block size minus
headers (at most ~3896 bytes). If validation fails, the inode is rejected, and
the filesystem is marked read-only.
However, the validation is completely bypassed if the inode block is *already*
present in the block device's page cache and marked as uptodate. The syzkaller
fuzzer triggers this by opening the underlying block device (e.g., `/dev/loop0`)
and directly writing a crafted inode block with `id_count = 65535` into it. When
ocfs2 later accesses this inode, `ocfs2_read_blocks` finds the buffer already
uptodate in the cache and skips the disk read and the associated validation
(`ocfs2_validate_inode_block`). This is a classic "buffer cache poisoning"
technique.
**Security Impact:**
- **Exploitable / Denial of Service:** Yes. The bug leads to a massive heap
out-of-bounds write (up to 64KB) which can corrupt critical kernel structures,
leading to a system crash or potentially arbitrary code execution.
- **Unprivileged / User Namespace:** False. Poisoning the buffer cache requires
direct write access to the underlying block device (e.g., `/dev/loop0`), which
strictly requires `root` privileges or `CAP_SYS_ADMIN`. The `ocfs2` filesystem
also cannot be mounted within an unprivileged user namespace.
- **Malicious Filesystem / Peripheral Trigger:** False. If a user plugs in a USB
drive containing a malicious ocfs2 image with a corrupted `id_count`, the
initial read from the disk will correctly trigger `ocfs2_validate_inode_block`.
The validation will fail, the inode will be rejected, and the bug will not be
triggered. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/22 00:53 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/22 00:53 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/764fd855c858ee25cb6be7e576fd58f5f79d6336]
|
| 3/1 |
2026/05/22 00:54 |
action |
kernel-builder |
28m
Results: map[KernelObj:/app/workdir/cache/build/7bae3679c9ad43e5615090878fbd0959438c35b8]
|
| 4/1 |
2026/05/22 01:23 |
action |
codesearch-prepare |
20m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/22 01:44 |
agent |
expert |
10m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction:
You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by greeping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Don't make assumptions; verify them with source code access. Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completly wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control tranfers, that's also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitabability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidetial Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
loop0: detected capacity change from 0 to 32768
=======================================================
WARNING: The mand mount option has been deprecated and
and is ignored by this kernel. Remove the mand
option from the mount to silence this warning.
=======================================================
JBD2: Ignoring recovery information on journal
ocfs2: Mounting device (7,0) on (node local, slot 0) with ordered data mode.
(syz.0.0,5320,0):ocfs2_check_set_options:1244 ERROR: Invalid heartbeat mount options
loop0: detected capacity change from 32768 to 32767
OCFS2: ERROR (device loop0): int ocfs2_validate_inode_block(struct super_block *, struct buffer_head *): Invalid dinode #17058: signature = οΏ½|οΏ½
On-disk corruption discovered. Please run fsck.ocfs2 once the filesystem is unmounted.
OCFS2: File system is now read-only.
(syz.0.0,5320,0):ocfs2_assign_bh:2417 ERROR: status = -30
(syz.0.0,5320,0):ocfs2_inode_lock_full_nested:2512 ERROR: status = -30
(syz.0.0,5320,0):ocfs2_reflink_inodes_lock:4747 ERROR: status = -30
------------[ cut here ]------------
kernel BUG at ./include/linux/highmem.h:577!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 5320 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:memcpy_from_folio include/linux/highmem.h:577 [inline]
RIP: 0010:ocfs2_write_end_inline fs/ocfs2/aops.c:1917 [inline]
RIP: 0010:ocfs2_write_end_nolock+0x1760/0x18c0 fs/ocfs2/aops.c:1951
Code: 90 0f 0b e8 e2 dc 06 fe 90 0f 0b e8 da dc 06 fe 48 8b 7c 24 10 48 c7 c6 60 97 12 8c e8 09 1a 69 fd 90 0f 0b e8 c1 dc 06 fe 90 <0f> 0b 65 44 8b 25 aa fb 7a 0f bf 07 00 00 00 44 89 e6 e8 e9 e0 06
RSP: 0018:ffffc9000e44f1e0 EFLAGS: 00010283
RAX: ffffffff83bef47f RBX: 0000000000000000 RCX: 0000000000100000
RDX: ffffc9000eea2000 RSI: 0000000000003791 RDI: 0000000000003792
RBP: ffffc9000e44f350 R08: ffffea0000471c87 R09: 1ffffd400008e390
R10: dffffc0000000000 R11: fffff9400008e391 R12: 0000000000001000
R13: 0000000000002000 R14: 0000000000001000 R15: 0000000000000000
FS: 00007fd4bfe296c0(0000) GS:ffff88808c881000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc3837909c0 CR3: 000000000e4c7000 CR4: 0000000000352ef0
Call Trace:
<TASK>
ocfs2_write_end+0x58/0x90 fs/ocfs2/aops.c:2061
generic_perform_write+0x620/0x8f0 mm/filemap.c:4346
ocfs2_file_write_iter+0x1663/0x1e70 fs/ocfs2/file.c:2476
iter_file_splice_write+0x9a1/0x10f0 fs/splice.c:736
do_splice_from fs/splice.c:936 [inline]
direct_splice_actor+0x101/0x160 fs/splice.c:1159
splice_direct_to_actor+0x53a/0xc70 fs/splice.c:1103
do_splice_direct_actor fs/splice.c:1202 [inline]
do_splice_direct+0x195/0x290 fs/splice.c:1228
vfs_copy_file_range+0xb02/0x1370 fs/read_write.c:1634
__do_sys_copy_file_range fs/read_write.c:1683 [inline]
__se_sys_copy_file_range+0x2fb/0x480 fs/read_write.c:1650
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd4bef9cdd9
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd4bfe28fe8 EFLAGS: 00000246 ORIG_RAX: 0000000000000146
RAX: ffffffffffffffda RBX: 00007fd4bf215fa0 RCX: 00007fd4bef9cdd9
RDX: 0000000000000004 RSI: 00002000000001c0 RDI: 0000000000000005
RBP: 00007fd4bf032d69 R08: ffffffffa003e45b R09: 0700000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd4bf216038 R14: 00007fd4bf215fa0 R15: 00007ffea2828fd8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:memcpy_from_folio include/linux/highmem.h:577 [inline]
RIP: 0010:ocfs2_write_end_inline fs/ocfs2/aops.c:1917 [inline]
RIP: 0010:ocfs2_write_end_nolock+0x1760/0x18c0 fs/ocfs2/aops.c:1951
Code: 90 0f 0b e8 e2 dc 06 fe 90 0f 0b e8 da dc 06 fe 48 8b 7c 24 10 48 c7 c6 60 97 12 8c e8 09 1a 69 fd 90 0f 0b e8 c1 dc 06 fe 90 <0f> 0b 65 44 8b 25 aa fb 7a 0f bf 07 00 00 00 44 89 e6 e8 e9 e0 06
RSP: 0018:ffffc9000e44f1e0 EFLAGS: 00010283
RAX: ffffffff83bef47f RBX: 0000000000000000 RCX: 0000000000100000
RDX: ffffc9000eea2000 RSI: 0000000000003791 RDI: 0000000000003792
RBP: ffffc9000e44f350 R08: ffffea0000471c87 R09: 1ffffd400008e390
R10: dffffc0000000000 R11: fffff9400008e391 R12: 0000000000001000
R13: 0000000000002000 R14: 0000000000001000 R15: 0000000000000000
FS: 00007fd4bfe296c0(0000) GS:ffff88808c881000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc3837909c0 CR3: 000000000e4c7000 CR4: 0000000000352ef0
Reply: **Analysis of the Bug**
The bug is a heap out-of-bounds read and write (and a `VM_BUG_ON` crash if `CONFIG_DEBUG_VM` is enabled) in `ocfs2_write_end_inline`.
**Root Cause:**
When writing to an ocfs2 file with inline data, `ocfs2_write_begin_nolock` checks if the write fits within the inline data limits by calling `ocfs2_size_fits_inline_data`. This function verifies that the new file size does not exceed `di->id2.i_data.id_count` (the maximum inline data size recorded in the on-disk inode).
If `id_count` is corrupted to a large value (e.g., `65535`), the filesystem allows the inline write to proceed for large offsets. Subsequently, `ocfs2_write_end_inline` calls `memcpy_from_folio` to copy data from the page cache folio to the inode's buffer head:
```c
memcpy_from_folio(di->id2.i_data.id_data + pos, wc->w_target_folio, pos, *copied);
```
Here, `pos` is used as the offset within the folio. If `pos` is larger than the folio size (typically 4096 bytes), `memcpy_from_folio` reads out-of-bounds from the folio and writes out-of-bounds into the buffer head data, corrupting adjacent heap memory.
**Bypassing Validation:**
Under normal circumstances, a corrupted `id_count` is caught by `ocfs2_validate_inode_block` when the inode is read from disk. The validation strictly ensures that `id_count` is less than or equal to the block size minus headers (at most ~3896 bytes). If validation fails, the inode is rejected, and the filesystem is marked read-only.
However, the validation is completely bypassed if the inode block is *already* present in the block device's page cache and marked as uptodate. The syzkaller fuzzer triggers this by opening the underlying block device (e.g., `/dev/loop0`) and directly writing a crafted inode block with `id_count = 65535` into it. When ocfs2 later accesses this inode, `ocfs2_read_blocks` finds the buffer already uptodate in the cache and skips the disk read and the associated validation (`ocfs2_validate_inode_block`). This is a classic "buffer cache poisoning" technique.
**Security Impact:**
- **Exploitable / Denial of Service:** Yes. The bug leads to a massive heap out-of-bounds write (up to 64KB) which can corrupt critical kernel structures, leading to a system crash or potentially arbitrary code execution.
- **Unprivileged / User Namespace:** False. Poisoning the buffer cache requires direct write access to the underlying block device (e.g., `/dev/loop0`), which strictly requires `root` privileges or `CAP_SYS_ADMIN`. The `ocfs2` filesystem also cannot be mounted within an unprivileged user namespace.
- **Malicious Filesystem / Peripheral Trigger:** False. If a user plugs in a USB drive containing a malicious ocfs2 image with a corrupted `id_count`, the initial read from the disk will correctly trigger `ocfs2_validate_inode_block`. The validation will fail, the inode will be rejected, and the bug will not be triggered.
|
| 6/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 7377
output: 42
thoughts: 140
Thoughts: **Analyzing a Kernel Bug**
Okay, so I've got a kernel BUG on my hands, a real head-scratcher. The report pinpoints the issue at `include/linux/highmem.h:577`, specifically inside `memcpy_from_folio`. This is interesting because the call stack reveals `ocfs2_write_end_inline` in `fs/ocfs2/aops.c:1917` is the direct caller, which is, in turn, called from `ocfs2_write_end_nolock` in `fs/ocfs2/aops.c:1951`. Hmm, `memcpy_from_folio` failing like this is usually a memory-related issue, a bad pointer, an unmapped page, or perhaps a race condition with memory access. The fact that it's in a file system context with the OCFS2 suggests it is related to how the shared storage is being handled. I need to take a look at the code at `include/linux/highmem.h:577` to see what checks are happening around the `memcpy` call. Specifically, I'll pay attention to the validity checks on the source and destination pointers or memory mapping. Let's see if we can deduce what is triggering the panic.
|
| 7/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/highmem.h IncludeLines:true Name:memcpy_from_folio]
Results: map[SourceCode: 567: /**
568: * memcpy_from_folio - Copy a range of bytes from a folio.
569: * @to: The memory to copy to.
570: * @folio: The folio to read from.
571: * @offset: The first byte in the folio to read.
572: * @len: The number of bytes to copy.
573: */
574: static inline void memcpy_from_folio(char *to, struct folio *folio,
575: size_t offset, size_t len)
576: {
577: VM_BUG_ON(offset + len > folio_size(folio));
578:
579: do {
580: const char *from = kmap_local_folio(folio, offset);
581: size_t chunk = len;
582:
583: if (folio_test_partial_kmap(folio) &&
584: chunk > PAGE_SIZE - offset_in_page(offset))
585: chunk = PAGE_SIZE - offset_in_page(offset);
586: memcpy(to, from, chunk);
587: kunmap_local(from);
588:
589: to += chunk;
590: offset += chunk;
591: len -= chunk;
592: } while (len > 0);
593: }
SourceFile:include/linux/highmem.h]
|
| 8/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 8269
output: 21
thoughts: 123
Thoughts: **Debugging a Potential VM Issue**
Okay, so I've hit a potential issue. The code flagged a `VM_BUG_ON` condition, specifically `VM_BUG_ON(offset + len > folio_size(folio));` right at line 577. This screams a potential memory management problem. The immediate question is, is this a hard stop, or is it just a debug warning? I know that `VM_BUG_ON`'s behavior is often tied to the `CONFIG_DEBUG_VM` configuration option. I need to figure out if it's going to halt execution and require immediate attention, or if it might be compiled out entirely without `CONFIG_DEBUG_VM`. Itβs also possible it's just a less severe warning, and maybe something I can check later. Now I recall that this macro, `VM_BUG_ON`, is typically defined somewhere in either `include/linux/build_bug.h` or `include/linux/mmdebug.h`, so I should start there to determine its actual impact. I need to understand its behavior fully to evaluate the severity of this bug.
|
| 9/2 |
2026/05/22 01:44 |
tool |
grepper |
0m
Args: map[Expression:#define VM_BUG_ON]
Results: map[Output:include/linux/mmdebug.h=18=void vma_iter_dump_tree(const struct vma_iterator *vmi);
--
include/linux/mmdebug.h-20-#ifdef CONFIG_DEBUG_VM
include/linux/mmdebug.h:21:#define VM_BUG_ON(cond) BUG_ON(cond)
include/linux/mmdebug.h:22:#define VM_BUG_ON_PAGE(cond, page) \
include/linux/mmdebug.h-23- do { \
--
include/linux/mmdebug.h-28- } while (0)
include/linux/mmdebug.h:29:#define VM_BUG_ON_FOLIO(cond, folio) \
include/linux/mmdebug.h-30- do { \
--
include/linux/mmdebug.h-35- } while (0)
include/linux/mmdebug.h:36:#define VM_BUG_ON_VMA(cond, vma) \
include/linux/mmdebug.h-37- do { \
--
include/linux/mmdebug.h-42- } while (0)
include/linux/mmdebug.h:43:#define VM_BUG_ON_MM(cond, mm) \
include/linux/mmdebug.h-44- do { \
--
include/linux/mmdebug.h-126-#else
include/linux/mmdebug.h:127:#define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond)
include/linux/mmdebug.h:128:#define VM_BUG_ON_PAGE(cond, page) VM_BUG_ON(cond)
include/linux/mmdebug.h:129:#define VM_BUG_ON_FOLIO(cond, folio) VM_BUG_ON(cond)
include/linux/mmdebug.h:130:#define VM_BUG_ON_VMA(cond, vma) VM_BUG_ON(cond)
include/linux/mmdebug.h:131:#define VM_BUG_ON_MM(cond, mm) VM_BUG_ON(cond)
include/linux/mmdebug.h-132-#define VM_WARN_ON(cond) BUILD_BUG_ON_INVALID(cond)
--
include/linux/mmdebug.h-157-#ifdef CONFIG_DEBUG_VM_PGFLAGS
include/linux/mmdebug.h:158:#define VM_BUG_ON_PGFLAGS(cond, page) VM_BUG_ON_PAGE(cond, page)
include/linux/mmdebug.h-159-#else
include/linux/mmdebug.h:160:#define VM_BUG_ON_PGFLAGS(cond, page) BUILD_BUG_ON_INVALID(cond)
include/linux/mmdebug.h-161-#endif
--
tools/testing/vma/vma_internal.h=52=typedef __bitwise unsigned int vm_fault_t;
--
tools/testing/vma/vma_internal.h-56-#define VM_WARN_ON_VMG(_expr, _vmg) (WARN_ON(_expr))
tools/testing/vma/vma_internal.h:57:#define VM_BUG_ON(_expr) (BUG_ON(_expr))
tools/testing/vma/vma_internal.h:58:#define VM_BUG_ON_VMA(_expr, _vma) (BUG_ON(_expr))
tools/testing/vma/vma_internal.h-59-
]
|
| 10/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 9522
output: 43
thoughts: 147
Thoughts: **My Analysis of a Potential VM Bug**
Okay, so I'm looking at this `VM_BUG_ON` macro and how it behaves. The first thing that jumps out is that when `CONFIG_DEBUG_VM` is disabled, this thing effectively becomes `BUILD_BUG_ON_INVALID(cond)`. In other words, in a standard, production build, it won't actually generate any executable code. No runtime check, no nothing. Essentially, the compiler will catch it, if at all, at build time.
But hold on a second... That's a problem. What happens if the `offset + len` calculation exceeds the actual size of the folio? If `VM_BUG_ON` isn't active, then this bounds check doesn't happen. That means we have a potential for a serious out-of-bounds read in the `memcpy` call that could be happening later. We could be reading past the end of the `folio`'s memory. Or, even worse, `kunmap_local` could get called with an invalid address, leading to memory corruption or crashes. And of course we could map something wrong if we aren't careful.
I need to dig into `kmap_local_folio` now. I need to understand exactly how the memory mapping is handled in that function and see if there are implicit assumptions or checks that are missing. This feels like a classic case of a bug that will only rear its ugly head in specific, difficult-to-reproduce scenarios. This bears further investigation.
|
| 11/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/highmem.h IncludeLines:true Name:kmap_local_folio]
Results: map[SourceCode: 196: static inline void *kmap_local_folio(const struct folio *folio, size_t offset)
197: {
198: return folio_address(folio) + offset;
199: }
SourceFile:include/linux/highmem-internal.h]
|
| 12/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 10131
output: 20
thoughts: 21
Thoughts: Here's my take on the thought process, as if it were my own:
**Variable Definition Contingency**
Okay, so I'm digging into this code, and I've hit a snag. It looks like `kmap_local_folio` has different definitions depending on whether `CONFIG_HIGHMEM` is enabled. That's a classic preprocessor conditional. It's not inherently bad, but it does mean I need to be careful about which path I'm following and how it impacts the rest of my analysis. I need to figure out the implications of this divergence quickly. Does it affect the calling conventions? The returned value? Or just the internal implementation details? Time to trace the usage and see what the downstream consequences are.
|
| 13/2 |
2026/05/22 01:44 |
tool |
grepper |
0m
Args: map[Expression:kmap_local_folio]
Results: map[Output:
Full output is too long, showing 500 out of 1194 lines.
Use more precise expression if possible.
[Documentation/mm/highmem.rst=52=list shows them in order of preference of use.
Documentation/mm/highmem.rst-53-
Documentation/mm/highmem.rst:54:* kmap_local_page(), kmap_local_folio() - These functions are used to create
Documentation/mm/highmem.rst-55- short term mappings. They can be invoked from any context (including
--
Documentation/mm/highmem.rst-77-
Documentation/mm/highmem.rst:78: kmap_local_page(), as well as kmap_local_folio() always returns valid virtual
Documentation/mm/highmem.rst-79- kernel addresses and it is assumed that kunmap_local() will never fail.
--
Documentation/mm/highmem.rst-96- pages in the same thread the address will be used and prefer
Documentation/mm/highmem.rst:97: kmap_local_page() or kmap_local_folio().
Documentation/mm/highmem.rst-98-
--
arch/arm/mm/flush.c=199=void __flush_dcache_folio(struct address_space *mapping, struct folio *folio)
--
arch/arm/mm/flush.c-212- for (i = 0; i < folio_nr_pages(folio); i++) {
arch/arm/mm/flush.c:213: void *addr = kmap_local_folio(folio,
arch/arm/mm/flush.c-214- i * PAGE_SIZE);
--
arch/csky/abiv2/cacheflush.c=10=void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma,
--
arch/csky/abiv2/cacheflush.c-28- for (i = 0; i < folio_nr_pages(folio); i++) {
arch/csky/abiv2/cacheflush.c:29: unsigned long addr = (unsigned long) kmap_local_folio(folio,
arch/csky/abiv2/cacheflush.c-30- i * PAGE_SIZE);
--
arch/mips/mm/cache.c=146=void __update_cache(unsigned long address, pte_t pte)
--
arch/mips/mm/cache.c-162- for (i = 0; i < folio_nr_pages(folio); i++) {
arch/mips/mm/cache.c:163: addr = (unsigned long)kmap_local_folio(folio, i);
arch/mips/mm/cache.c-164-
--
arch/powerpc/mm/cacheflush.c=151=void flush_dcache_icache_folio(struct folio *folio)
--
arch/powerpc/mm/cacheflush.c-163- for (i = 0; i < nr; i++) {
arch/powerpc/mm/cacheflush.c:164: void *start = kmap_local_folio(folio, i * PAGE_SIZE);
arch/powerpc/mm/cacheflush.c-165-
--
arch/xtensa/mm/cache.c=216=void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma,
--
arch/xtensa/mm/cache.c-255- for (i = 0; i < nr; i++) {
arch/xtensa/mm/cache.c:256: void *paddr = kmap_local_folio(folio, i * PAGE_SIZE);
arch/xtensa/mm/cache.c-257- __flush_dcache_page((unsigned long)paddr);
--
fs/afs/dir.c=231=static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file)
--
fs/afs/dir.c-286- /* NUL-terminate a symlink. */
fs/afs/dir.c:287: char *symlink = kmap_local_folio(folioq_folio(dvnode->directory, 0), 0);
fs/afs/dir.c-288-
--
fs/afs/dir_edit.c=111=static union afs_xdr_dir_block *afs_dir_get_block(struct afs_dir_iter *iter, size_t block)
--
fs/afs/dir_edit.c-147- iter->fpos = fpos;
fs/afs/dir_edit.c:148: return kmap_local_folio(folio, blpos - fpos);
fs/afs/dir_edit.c-149- }
--
fs/afs/dir_search.c=67=union afs_xdr_dir_block *afs_dir_find_block(struct afs_dir_iter *iter, size_t block)
--
fs/afs/dir_search.c-104- iter->fpos = fpos;
fs/afs/dir_search.c:105: iter->block = kmap_local_folio(folio, blpos - fpos);
fs/afs/dir_search.c-106- return iter->block;
--
fs/afs/inode.c=28=void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op)
--
fs/afs/inode.c-38- vnode->directory_size = dsize;
fs/afs/inode.c:39: p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
fs/afs/inode.c-40- memcpy(p, op->create.symlink, size);
--
fs/afs/inode.c=54=const char *afs_get_link(struct dentry *dentry, struct inode *inode,
--
fs/afs/inode.c-88- folio_get(folio);
fs/afs/inode.c:89: content = kmap_local_folio(folio, 0);
fs/afs/inode.c-90- set_delayed_call(callback, afs_put_link, content);
--
fs/btrfs/inode.c=458=static int insert_inline_extent(struct btrfs_trans_handle *trans,
--
fs/btrfs/inode.c-521- if (compress_type != BTRFS_COMPRESS_NONE) {
fs/btrfs/inode.c:522: kaddr = kmap_local_folio(compressed_folio, 0);
fs/btrfs/inode.c-523- write_extent_buffer(leaf, kaddr, ptr, compressed_size);
--
fs/btrfs/inode.c-533- btrfs_set_file_extent_compression(leaf, ei, 0);
fs/btrfs/inode.c:534: kaddr = kmap_local_folio(folio, 0);
fs/btrfs/inode.c-535- write_extent_buffer(leaf, kaddr, ptr, size);
--
fs/btrfs/inode.c=7170=static int read_inline_extent(struct btrfs_path *path, struct folio *folio)
--
fs/btrfs/inode.c-7188- btrfs_file_extent_ram_bytes(path->nodes[0], fi));
fs/btrfs/inode.c:7189: kaddr = kmap_local_folio(folio, 0);
fs/btrfs/inode.c-7190- read_extent_buffer(path->nodes[0], kaddr,
--
fs/btrfs/inode.c=9876=ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
--
fs/btrfs/inode.c-9993- }
fs/btrfs/inode.c:9994: kaddr = kmap_local_folio(folio, 0);
fs/btrfs/inode.c-9995- ret = copy_from_iter(kaddr, bytes, from);
--
fs/btrfs/lzo.c=176=static int copy_compressed_data_to_bio(struct btrfs_fs_info *fs_info,
--
fs/btrfs/lzo.c-210- /* Write the segment header first. */
fs/btrfs/lzo.c:211: kaddr = kmap_local_folio(*out_folio, offset_in_folio(*out_folio, *total_out));
fs/btrfs/lzo.c-212- put_unaligned_le32(compressed_size, kaddr);
--
fs/btrfs/lzo.c-236-
fs/btrfs/lzo.c:237: kaddr = kmap_local_folio(*out_folio, foffset);
fs/btrfs/lzo.c-238- memcpy(kaddr, compressed_data + *total_out - copy_start, copy_len);
--
fs/btrfs/lzo.c=260=int lzo_compress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/lzo.c-308- ASSERT(in_len);
fs/btrfs/lzo.c:309: data_in = kmap_local_folio(folio_in, offset_in_folio(folio_in, cur_in));
fs/btrfs/lzo.c-310- ret = lzo1x_1_compress(data_in, in_len, workspace->cbuf, &out_len,
--
fs/btrfs/lzo.c-347- /* Store the size of all chunks of compressed data */
fs/btrfs/lzo.c:348: sizes_ptr = kmap_local_folio(bio_first_folio_all(bio), 0);
fs/btrfs/lzo.c-349- put_unaligned_le32(total_out, sizes_ptr);
--
fs/btrfs/lzo.c=413=int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/lzo.c-435- ASSERT(folio_size(fi.folio) == btrfs_min_folio_size(fs_info));
fs/btrfs/lzo.c:436: kaddr = kmap_local_folio(fi.folio, 0);
fs/btrfs/lzo.c-437- len_in = get_unaligned_le32(kaddr);
--
fs/btrfs/lzo.c-474- ASSERT(cur_folio);
fs/btrfs/lzo.c:475: kaddr = kmap_local_folio(cur_folio, 0);
fs/btrfs/lzo.c-476- seg_len = get_unaligned_le32(kaddr + offset_in_folio(cur_folio, cur_in));
--
fs/btrfs/verity.c=288=static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
--
fs/btrfs/verity.c-366- if (dest_folio)
fs/btrfs/verity.c:367: kaddr = kmap_local_folio(dest_folio, 0);
fs/btrfs/verity.c-368-
--
fs/btrfs/zlib.c=114=static int copy_data_into_buffer(struct address_space *mapping,
--
fs/btrfs/zlib.c-137-
fs/btrfs/zlib.c:138: data_in = kmap_local_folio(folio, offset);
fs/btrfs/zlib.c-139- memcpy(workspace->buf + cur - filepos, data_in, copy_length);
--
fs/btrfs/zlib.c=147=int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/zlib.c-220- cur_len = btrfs_calc_input_length(in_folio, orig_end, start);
fs/btrfs/zlib.c:221: data_in = kmap_local_folio(in_folio,
fs/btrfs/zlib.c-222- offset_in_folio(in_folio, start));
--
fs/btrfs/zlib.c=338=int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/zlib.c-357-
fs/btrfs/zlib.c:358: data_in = kmap_local_folio(fi.folio, 0);
fs/btrfs/zlib.c-359- workspace->strm.next_in = data_in;
--
fs/btrfs/zlib.c-418- ASSERT(folio_size(fi.folio) == min_folio_size);
fs/btrfs/zlib.c:419: data_in = kmap_local_folio(fi.folio, 0);
fs/btrfs/zlib.c-420- workspace->strm.next_in = data_in;
--
fs/btrfs/zstd.c=398=int zstd_compress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/zstd.c-434- goto out;
fs/btrfs/zstd.c:435: workspace->in_buf.src = kmap_local_folio(in_folio, offset_in_folio(in_folio, start));
fs/btrfs/zstd.c-436- workspace->in_buf.pos = 0;
--
fs/btrfs/zstd.c-512- goto out;
fs/btrfs/zstd.c:513: workspace->in_buf.src = kmap_local_folio(in_folio,
fs/btrfs/zstd.c-514- offset_in_folio(in_folio, cur));
--
fs/btrfs/zstd.c=583=int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
--
fs/btrfs/zstd.c-613-
fs/btrfs/zstd.c:614: workspace->in_buf.src = kmap_local_folio(fi.folio, 0);
fs/btrfs/zstd.c-615- workspace->in_buf.pos = 0;
--
fs/btrfs/zstd.c-663- ASSERT(fi.folio);
fs/btrfs/zstd.c:664: workspace->in_buf.src = kmap_local_folio(fi.folio, 0);
fs/btrfs/zstd.c-665- workspace->in_buf.pos = 0;
--
fs/ceph/dir.c=131=__dcache_find_get_entry(struct dentry *parent, u64 idx,
--
fs/ceph/dir.c-154- folio_unlock(cache_ctl->folio);
fs/ceph/dir.c:155: cache_ctl->dentries = kmap_local_folio(cache_ctl->folio, 0);
fs/ceph/dir.c-156- }
--
fs/ceph/inode.c=1953=static int fill_readdir_cache(struct inode *dir, struct dentry *dn,
--
fs/ceph/inode.c-1981- folio_unlock(ctl->folio);
fs/ceph/inode.c:1982: ctl->dentries = kmap_local_folio(ctl->folio, 0);
fs/ceph/inode.c-1983- if (idx == 0)
--
fs/cramfs/inode.c=821=static int cramfs_read_folio(struct file *file, struct folio *folio)
--
fs/cramfs/inode.c-830- bytes_filled = 0;
fs/cramfs/inode.c:831: pgdata = kmap_local_folio(folio, 0);
fs/cramfs/inode.c-832-
--
fs/ecryptfs/crypto.c=416=int ecryptfs_decrypt_page(struct folio *folio)
--
fs/ecryptfs/crypto.c-430- lower_offset = lower_offset_for_page(crypt_stat, folio);
fs/ecryptfs/crypto.c:431: page_virt = kmap_local_folio(folio, 0);
fs/ecryptfs/crypto.c-432- rc = ecryptfs_read_lower(page_virt, lower_offset, PAGE_SIZE,
--
fs/ecryptfs/mmap.c=91=ecryptfs_copy_up_encrypted_with_header(struct folio *folio,
--
fs/ecryptfs/mmap.c-109-
fs/ecryptfs/mmap.c:110: page_virt = kmap_local_folio(folio, 0);
fs/ecryptfs/mmap.c-111- memset(page_virt, 0, PAGE_SIZE);
--
fs/ecryptfs/read_write.c=57=int ecryptfs_write_lower_page_segment(struct inode *ecryptfs_inode,
--
fs/ecryptfs/read_write.c-65- offset = (loff_t)folio_for_lower->index * PAGE_SIZE + offset_in_page;
fs/ecryptfs/read_write.c:66: virt = kmap_local_folio(folio_for_lower, 0);
fs/ecryptfs/read_write.c-67- rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size);
--
fs/ecryptfs/read_write.c=92=int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset,
--
fs/ecryptfs/read_write.c-142- folio_lock(ecryptfs_folio);
fs/ecryptfs/read_write.c:143: ecryptfs_page_virt = kmap_local_folio(ecryptfs_folio, 0);
fs/ecryptfs/read_write.c-144-
--
fs/ecryptfs/read_write.c=246=int ecryptfs_read_lower_page_segment(struct folio *folio_for_ecryptfs,
--
fs/ecryptfs/read_write.c-255- offset = (loff_t)page_index * PAGE_SIZE + offset_in_page;
fs/ecryptfs/read_write.c:256: virt = kmap_local_folio(folio_for_ecryptfs, 0);
fs/ecryptfs/read_write.c-257- rc = ecryptfs_read_lower(virt, offset, size, ecryptfs_inode);
--
fs/ext2/dir.c=100=static bool ext2_check_folio(struct folio *folio, int quiet, char *kaddr)
--
fs/ext2/dir.c-183- * Calls to ext2_get_folio()/folio_release_kmap() must be nested according
fs/ext2/dir.c:184: * to the rules documented in kmap_local_folio()/kunmap_local().
fs/ext2/dir.c-185- *
--
fs/ext2/dir.c=190=static void *ext2_get_folio(struct inode *dir, unsigned long n,
--
fs/ext2/dir.c-198- return ERR_CAST(folio);
fs/ext2/dir.c:199: kaddr = kmap_local_folio(folio, 0);
fs/ext2/dir.c-200- if (unlikely(!folio_test_checked(folio))) {
--
fs/ext2/dir.c=258=ext2_readdir(struct file *file, struct dir_context *ctx)
--
fs/ext2/dir.c-336- * NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested
fs/ext2/dir.c:337: * according to the rules documented in kmap_local_folio()/kunmap_local().
fs/ext2/dir.c-338- *
--
fs/ext2/dir.c=343=struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir,
--
fs/ext2/dir.c-406- * NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested
fs/ext2/dir.c:407: * according to the rules documented in kmap_local_folio()/kunmap_local().
fs/ext2/dir.c-408- *
--
fs/ext2/dir.c=618=int ext2_make_empty(struct inode *inode, struct inode *parent)
--
fs/ext2/dir.c-633- }
fs/ext2/dir.c:634: kaddr = kmap_local_folio(folio, 0);
fs/ext2/dir.c-635- memset(kaddr, 0, chunk_size);
--
fs/ext4/inline.c=503=static int ext4_read_inline_folio(struct inode *inode, struct folio *folio)
--
fs/ext4/inline.c-533-
fs/ext4/inline.c:534: kaddr = kmap_local_folio(folio, 0);
fs/ext4/inline.c-535- ret = ext4_read_inline_data(inode, kaddr, len, &iloc);
--
fs/ext4/inline.c=794=int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
--
fs/ext4/inline.c-823-
fs/ext4/inline.c:824: kaddr = kmap_local_folio(folio, 0);
fs/ext4/inline.c-825- ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
--
fs/fuse/dev.c=1125=static int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
--
fs/fuse/dev.c-1163- if (folio) {
fs/fuse/dev.c:1164: void *mapaddr = kmap_local_folio(folio, offset);
fs/fuse/dev.c-1165- void *buf = mapaddr;
--
fs/fuse/ioctl.c=217=long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
--
fs/fuse/ioctl.c-367-
fs/fuse/ioctl.c:368: vaddr = kmap_local_folio(ap.folios[0], 0);
fs/fuse/ioctl.c-369- err = fuse_copy_ioctl_iovec(fm->fc, iov_page, vaddr,
--
fs/gfs2/bmap.c=54=static int gfs2_unstuffer_folio(struct gfs2_inode *ip, struct buffer_head *dibh,
--
fs/gfs2/bmap.c-59- if (!folio_test_uptodate(folio)) {
fs/gfs2/bmap.c:60: void *kaddr = kmap_local_folio(folio, 0);
fs/gfs2/bmap.c-61- u64 dsize = i_size_read(inode);
--
fs/gfs2/lops.c=416=static bool gfs2_jhead_folio_search(struct gfs2_jdesc *jd,
--
fs/gfs2/lops.c-426- VM_BUG_ON_FOLIO(folio_test_large(folio), folio);
fs/gfs2/lops.c:427: kaddr = kmap_local_folio(folio, 0);
fs/gfs2/lops.c-428- for (offset = 0; offset < PAGE_SIZE; offset += sdp->sd_sb.sb_bsize) {
--
fs/gfs2/lops.c=614=static void gfs2_check_magic(struct buffer_head *bh)
--
fs/gfs2/lops.c-618- clear_buffer_escaped(bh);
fs/gfs2/lops.c:619: ptr = kmap_local_folio(bh->b_folio, bh_offset(bh));
fs/gfs2/lops.c-620- if (*ptr == cpu_to_be32(GFS2_MAGIC))
--
fs/hfs/btree.c=19=struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp keycmp)
--
fs/hfs/btree.c-114- /* Load the header */
fs/hfs/btree.c:115: head = (struct hfs_btree_header_rec *)(kmap_local_folio(folio, 0) +
fs/hfs/btree.c-116- sizeof(struct hfs_bnode_desc));
--
fs/hostfs/hostfs_kern.c=399=static int hostfs_writepages(struct address_space *mapping,
--
fs/hostfs/hostfs_kern.c-415-
fs/hostfs/hostfs_kern.c:416: buffer = kmap_local_folio(folio, 0);
fs/hostfs/hostfs_kern.c-417- ret = write_file(HOSTFS_I(inode)->fd, &pos, buffer, count);
--
fs/hostfs/hostfs_kern.c=429=static int hostfs_read_folio(struct file *file, struct folio *folio)
--
fs/hostfs/hostfs_kern.c-434-
fs/hostfs/hostfs_kern.c:435: buffer = kmap_local_folio(folio, 0);
fs/hostfs/hostfs_kern.c-436- bytes_read = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer,
--
fs/hostfs/hostfs_kern.c=462=static int hostfs_write_end(const struct kiocb *iocb,
--
fs/hostfs/hostfs_kern.c-471-
fs/hostfs/hostfs_kern.c:472: buffer = kmap_local_folio(folio, from);
fs/hostfs/hostfs_kern.c-473- err = write_file(FILE_HOSTFS_I(iocb->ki_filp)->fd, &pos, buffer, copied);
--
fs/iomap/buffered-io.c=1054=static bool iomap_write_end_inline(const struct iomap_iter *iter,
--
fs/iomap/buffered-io.c-1066- flush_dcache_folio(folio);
fs/iomap/buffered-io.c:1067: addr = kmap_local_folio(folio, pos);
fs/iomap/buffered-io.c-1068- memcpy(iomap_inline_data(iomap, pos), addr, copied);
--
fs/jbd2/commit.c=329=static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh)
--
fs/jbd2/commit.c-333-
fs/jbd2/commit.c:334: addr = kmap_local_folio(bh->b_folio, bh_offset(bh));
fs/jbd2/commit.c-335- checksum = crc32_be(crc32_sum, addr, bh->b_size);
--
fs/jbd2/commit.c=349=static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
--
fs/jbd2/commit.c-360- seq = cpu_to_be32(sequence);
fs/jbd2/commit.c:361: addr = kmap_local_folio(bh->b_folio, bh_offset(bh));
fs/jbd2/commit.c-362- csum32 = jbd2_chksum(j->j_csum_seed, (__u8 *)&seq, sizeof(seq));
--
fs/jbd2/journal.c=325=int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
--
fs/jbd2/journal.c-369- new_offset = offset_in_folio(new_folio, bh_in->b_data);
fs/jbd2/journal.c:370: mapped_data = kmap_local_folio(new_folio, new_offset);
fs/jbd2/journal.c-371- /*
--
fs/jbd2/transaction.c=918=static void jbd2_freeze_jh_data(struct journal_head *jh)
--
fs/jbd2/transaction.c-923- J_EXPECT_JH(jh, buffer_uptodate(bh), "Possible IO failure.\n");
fs/jbd2/transaction.c:924: source = kmap_local_folio(bh->b_folio, bh_offset(bh));
fs/jbd2/transaction.c-925- /* Fire data frozen trigger just before we copy the data */
--
fs/jffs2/file.c=84=static int jffs2_do_readpage_nolock(struct inode *inode, struct folio *folio)
--
fs/jffs2/file.c-95-
fs/jffs2/file.c:96: kaddr = kmap_local_folio(folio, 0);
fs/jffs2/file.c-97- ret = jffs2_read_inode_range(c, f, kaddr, folio->index << PAGE_SHIFT,
--
fs/jffs2/file.c=243=static int jffs2_write_end(const struct kiocb *iocb,
--
fs/jffs2/file.c-297-
fs/jffs2/file.c:298: buf = kmap_local_folio(folio, aligned_start);
fs/jffs2/file.c-299- ret = jffs2_write_inode_range(c, f, ri, buf,
--
fs/jffs2/gc.c=1164=static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb,
--
fs/jffs2/gc.c-1337-
fs/jffs2/gc.c:1338: pg_ptr = kmap_local_folio(folio, 0);
fs/jffs2/gc.c-1339- mutex_lock(&f->sem);
--
fs/minix/dir.c=67=static void *dir_get_folio(struct inode *dir, unsigned long n,
--
fs/minix/dir.c-74- *foliop = folio;
fs/minix/dir.c:75: return kmap_local_folio(folio, 0);
fs/minix/dir.c-76-}
--
fs/minix/dir.c=310=int minix_make_empty(struct inode *inode, struct inode *dir)
--
fs/minix/dir.c-324-
fs/minix/dir.c:325: kaddr = kmap_local_folio(folio, 0);
fs/minix/dir.c-326- memset(kaddr, 0, folio_size(folio));
--
fs/nfs/dir.c=202=static void nfs_readdir_folio_init_array(struct folio *folio, u64 last_cookie,
--
fs/nfs/dir.c-206-
fs/nfs/dir.c:207: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-208- array->change_attr = change_attr;
--
fs/nfs/dir.c=220=static void nfs_readdir_clear_array(struct folio *folio)
--
fs/nfs/dir.c-224-
fs/nfs/dir.c:225: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-226- for (i = 0; i < array->size; i++)
--
fs/nfs/dir.c=310=static int nfs_readdir_folio_array_append(struct folio *folio,
--
fs/nfs/dir.c-320-
fs/nfs/dir.c:321: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-322- if (!name)
--
fs/nfs/dir.c=367=static bool nfs_readdir_folio_validate(struct folio *folio, u64 last_cookie,
--
fs/nfs/dir.c-369-{
fs/nfs/dir.c:370: struct nfs_cache_array *array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-371- int ret = true;
--
fs/nfs/dir.c=412=static u64 nfs_readdir_folio_last_cookie(struct folio *folio)
--
fs/nfs/dir.c-416-
fs/nfs/dir.c:417: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-418- ret = array->last_cookie;
--
fs/nfs/dir.c=423=static bool nfs_readdir_folio_needs_filling(struct folio *folio)
--
fs/nfs/dir.c-427-
fs/nfs/dir.c:428: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-429- ret = !nfs_readdir_array_is_full(array);
--
fs/nfs/dir.c=434=static void nfs_readdir_folio_set_eof(struct folio *folio)
--
fs/nfs/dir.c-437-
fs/nfs/dir.c:438: array = kmap_local_folio(folio, 0);
fs/nfs/dir.c-439- nfs_readdir_array_set_eof(array);
--
fs/nfs/dir.c=564=static int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
--
fs/nfs/dir.c-568-
fs/nfs/dir.c:569: array = kmap_local_folio(desc->folio, 0);
fs/nfs/dir.c-570-
--
fs/nfs/dir.c=1084=static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
--
fs/nfs/dir.c-1091-
fs/nfs/dir.c:1092: array = kmap_local_folio(desc->folio, 0);
fs/nfs/dir.c-1093- for (i = desc->cache_entry_index; i < array->size; i++) {
--
fs/nilfs2/alloc.c=581=int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
--
fs/nilfs2/alloc.c-611- doff = nilfs_palloc_group_desc_offset(inode, group, desc_bh);
fs/nilfs2/alloc.c:612: desc = kmap_local_folio(desc_bh->b_folio, doff);
fs/nilfs2/alloc.c-613- n = nilfs_palloc_rest_groups_in_desc_block(inode, group,
--
fs/nilfs2/alloc.c-631- */
fs/nilfs2/alloc.c:632: desc = kmap_local_folio(desc_bh->b_folio, doff);
fs/nilfs2/alloc.c-633-
fs/nilfs2/alloc.c-634- boff = nilfs_palloc_bitmap_offset(bitmap_bh);
fs/nilfs2/alloc.c:635: bitmap = kmap_local_folio(bitmap_bh->b_folio, boff);
fs/nilfs2/alloc.c-636- pos = nilfs_palloc_find_available_slot(
--
fs/nilfs2/alloc.c=691=void nilfs_palloc_commit_free_entry(struct inode *inode,
--
fs/nilfs2/alloc.c-701- doff = nilfs_palloc_group_desc_offset(inode, group, req->pr_desc_bh);
fs/nilfs2/alloc.c:702: desc = kmap_local_folio(req->pr_desc_bh->b_folio, doff);
fs/nilfs2/alloc.c-703-
fs/nilfs2/alloc.c-704- boff = nilfs_palloc_bitmap_offset(req->pr_bitmap_bh);
fs/nilfs2/alloc.c:705: bitmap = kmap_local_folio(req->pr_bitmap_bh->b_folio, boff);
fs/nilfs2/alloc.c-706- lock = nilfs_mdt_bgl_lock(inode, group);
--
fs/nilfs2/alloc.c=732=void nilfs_palloc_abort_alloc_entry(struct inode *inode,
--
fs/nilfs2/alloc.c-742- doff = nilfs_palloc_group_desc_offset(inode, group, req->pr_desc_bh);
fs/nilfs2/alloc.c:743: desc = kmap_local_folio(req->pr_desc_bh->b_folio, doff);
fs/nilfs2/alloc.c-744-
fs/nilfs2/alloc.c-745- boff = nilfs_palloc_bitmap_offset(req->pr_bitmap_bh);
fs/nilfs2/alloc.c:746: bitmap = kmap_local_folio(req->pr_bitmap_bh->b_folio, boff);
fs/nilfs2/alloc.c-747- lock = nilfs_mdt_bgl_lock(inode, group);
--
fs/nilfs2/alloc.c=821=int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems)
--
fs/nilfs2/alloc.c-854- boff = nilfs_palloc_bitmap_offset(bitmap_bh);
fs/nilfs2/alloc.c:855: bitmap = kmap_local_folio(bitmap_bh->b_folio, boff);
fs/nilfs2/alloc.c-856- lock = nilfs_mdt_bgl_lock(inode, group);
--
fs/nilfs2/alloc.c-914- doff = nilfs_palloc_group_desc_offset(inode, group, desc_bh);
fs/nilfs2/alloc.c:915: desc = kmap_local_folio(desc_bh->b_folio, doff);
fs/nilfs2/alloc.c-916- nfree = nilfs_palloc_group_desc_add_entries(desc, lock, n);
--
fs/nilfs2/cpfile.c=69=nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile,
--
fs/nilfs2/cpfile.c-75-
fs/nilfs2/cpfile.c:76: cp = kmap_local_folio(bh->b_folio,
fs/nilfs2/cpfile.c-77- offset_in_folio(bh->b_folio, bh->b_data));
--
fs/nilfs2/cpfile.c=85=nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,
--
fs/nilfs2/cpfile.c-91-
fs/nilfs2/cpfile.c:92: cp = kmap_local_folio(bh->b_folio,
fs/nilfs2/cpfile.c-93- offset_in_folio(bh->b_folio, bh->b_data));
--
fs/nilfs2/cpfile.c=245=int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno,
--
fs/nilfs2/cpfile.c-264- offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh);
fs/nilfs2/cpfile.c:265: cp = kmap_local_folio(cp_bh->b_folio, offset);
fs/nilfs2/cpfile.c-266- if (nilfs_checkpoint_invalid(cp)) {
--
fs/nilfs2/cpfile.c=314=int nilfs_cpfile_create_checkpoint(struct inode *cpfile, __u64 cno)
--
fs/nilfs2/cpfile.c-334- offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh);
fs/nilfs2/cpfile.c:335: cp = kmap_local_folio(cp_bh->b_folio, offset);
fs/nilfs2/cpfile.c-336- if (nilfs_checkpoint_invalid(cp)) {
--
fs/nilfs2/cpfile.c-343-
fs/nilfs2/cpfile.c:344: header = kmap_local_folio(header_bh->b_folio, 0);
fs/nilfs2/cpfile.c-345- le64_add_cpu(&header->ch_ncheckpoints, 1);
--
]
]
|
| 14/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 21312
output: 45
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into the Kernel's Highmem Implementation**
Right, so I'm tracking down the definition of `kmap_local_folio`. My immediate instinct, based on experience, is to find it in `include/linux/highmem-internal.h`. That's where I'd expect to see the low-level, internal details of the kernel's high memory management routines. Time to take a closer look at `kmap_local_folio` and its internal workings. Let's see how it's defined and how it interacts with the rest of the highmem infrastructure. I'm especially interested in the implementation details.
|
| 15/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/highmem-internal.h IncludeLines:true Name:kmap_local_folio]
Results: map[SourceCode: 196: static inline void *kmap_local_folio(const struct folio *folio, size_t offset)
197: {
198: return folio_address(folio) + offset;
199: }
SourceFile:include/linux/highmem-internal.h]
|
| 16/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 21647
output: 48
thoughts: 96
Thoughts: **Analyzing a potential memory access issue in OCFS2**
Okay, hold on a second. I'm looking at this `folio_address` function, and it's basically just giving me the address of the page, via `page_address(&folio->page)`. Nothing unexpected there. But, I'm starting to think about potential buffer overflows when using `memcpy_from_folio`. If `offset + len` is greater than the size of the `folio`, then the `memcpy` call will attempt to read beyond the allocated memory for the folio, a classic out-of-bounds read. This is definitely something to investigate further. Let me trace where `memcpy_from_folio` is actually being used in the code to get a handle on the contexts it appears in. Ah, it looks like it's called from `ocfs2_write_end_inline` at `fs/ocfs2/aops.c:1917`. Okay, so it looks like inline writes might be where this could surface. Now that I know where the call is, I can examine its usage in that context for where this can happen.
|
| 17/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_end_inline]
Results: map[SourceCode:1905: static void ocfs2_write_end_inline(struct inode *inode, loff_t pos,
1906: unsigned len, unsigned *copied,
1907: struct ocfs2_dinode *di,
1908: struct ocfs2_write_ctxt *wc)
1909: {
1910: if (unlikely(*copied < len)) {
1911: if (!folio_test_uptodate(wc->w_target_folio)) {
1912: *copied = 0;
1913: return;
1914: }
1915: }
1916:
1917: memcpy_from_folio(di->id2.i_data.id_data + pos, wc->w_target_folio,
1918: pos, *copied);
1919:
1920: trace_ocfs2_write_end_inline(
1921: (unsigned long long)OCFS2_I(inode)->ip_blkno,
1922: (unsigned long long)pos, *copied,
1923: le16_to_cpu(di->id2.i_data.id_count),
1924: le16_to_cpu(di->i_dyn_features));
1925: }
SourceFile:fs/ocfs2/aops.c]
|
| 18/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 22423
output: 49
thoughts: 280
Thoughts: **Analyzing the Data Copy Operation**
Okay, so I'm looking at this `memcpy_from_folio` function call. Let's break down the arguments. It seems like `to` is `di->id2.i_data.id_data + pos`, `folio` is `wc->w_target_folio`, `offset` is `pos`, and `len` is `*copied`. Got it.
Now, if `pos + *copied` is greater than the size of the folio, we hit a `VM_BUG_ON`, which is good, that's a check. But hang on, `di` is a `struct ocfs2_dinode *`. This means `di->id2.i_data.id_data` points to an inline data array within the inode block. I need to know the size of this `id_data` array. Okay, I see it, it's determined by `le16_to_cpu(di->id2.i_data.id_count)`.
Here's the problem: if `pos + *copied` exceeds the size of `id_data`, this `memcpy` will write out of bounds of the *inode block itself*! This is a serious potential vulnerability. And `di` is a pointer to the inode block buffer! This is where things can go wrong. I need to dig deeper into the code, starting with `ocfs2_write_end_nolock`. Let's see how this is handled further up the call stack.
|
| 19/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_end_nolock]
Results: map[SourceCode:1927: int ocfs2_write_end_nolock(struct address_space *mapping, loff_t pos,
1928: unsigned len, unsigned copied, void *fsdata)
1929: {
1930: int i, ret;
1931: size_t from, to, start = pos & (PAGE_SIZE - 1);
1932: struct inode *inode = mapping->host;
1933: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
1934: struct ocfs2_write_ctxt *wc = fsdata;
1935: struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
1936: handle_t *handle = wc->w_handle;
1937:
1938: BUG_ON(!list_empty(&wc->w_unwritten_list));
1939:
1940: if (handle) {
1941: ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode),
1942: wc->w_di_bh, OCFS2_JOURNAL_ACCESS_WRITE);
1943: if (ret) {
1944: copied = ret;
1945: mlog_errno(ret);
1946: goto out;
1947: }
1948: }
1949:
1950: if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
1951: ocfs2_write_end_inline(inode, pos, len, &copied, di, wc);
1952: goto out_write_size;
1953: }
1954:
1955: if (unlikely(copied < len) && wc->w_target_folio) {
1956: loff_t new_isize;
1957:
1958: if (!folio_test_uptodate(wc->w_target_folio))
1959: copied = 0;
1960:
1961: new_isize = max_t(loff_t, i_size_read(inode), pos + copied);
1962: if (new_isize > folio_pos(wc->w_target_folio))
1963: ocfs2_zero_new_buffers(wc->w_target_folio, start+copied,
1964: start+len);
1965: else {
1966: /*
1967: * When folio is fully beyond new isize (data copy
1968: * failed), do not bother zeroing the folio. Invalidate
1969: * it instead so that writeback does not get confused
1970: * put page & buffer dirty bits into inconsistent
1971: * state.
1972: */
1973: block_invalidate_folio(wc->w_target_folio, 0,
1974: folio_size(wc->w_target_folio));
1975: }
1976: }
1977: if (wc->w_target_folio)
1978: flush_dcache_folio(wc->w_target_folio);
1979:
1980: for (i = 0; i < wc->w_num_folios; i++) {
1981: struct folio *folio = wc->w_folios[i];
1982:
1983: /* This is the direct io target folio */
1984: if (folio == NULL)
1985: continue;
1986:
1987: if (folio == wc->w_target_folio) {
1988: from = wc->w_target_from;
1989: to = wc->w_target_to;
1990:
1991: BUG_ON(from > folio_size(folio) ||
1992: to > folio_size(folio) ||
1993: to < from);
1994: } else {
1995: /*
1996: * Pages adjacent to the target (if any) imply
1997: * a hole-filling write in which case we want
1998: * to flush their entire range.
1999: */
2000: from = 0;
2001: to = folio_size(folio);
2002: }
2003:
2004: if (folio_buffers(folio)) {
2005: if (handle && ocfs2_should_order_data(inode)) {
2006: loff_t start_byte = folio_pos(folio) + from;
2007: loff_t length = to - from;
2008: ocfs2_jbd2_inode_add_write(handle, inode,
2009: start_byte, length);
2010: }
2011: block_commit_write(folio, from, to);
2012: }
2013: }
2014:
2015: out_write_size:
2016: /* Direct io do not update i_size here. */
2017: if (wc->w_type != OCFS2_WRITE_DIRECT) {
2018: pos += copied;
2019: if (pos > i_size_read(inode)) {
2020: i_size_write(inode, pos);
2021: mark_inode_dirty(inode);
2022: }
2023: inode->i_blocks = ocfs2_inode_sector_count(inode);
2024: di->i_size = cpu_to_le64((u64)i_size_read(inode));
2025: inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
2026: di->i_mtime = di->i_ctime = cpu_to_le64(inode_get_mtime_sec(inode));
2027: di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode));
2028: if (handle)
2029: ocfs2_update_inode_fsync_trans(handle, inode, 1);
2030: }
2031: if (handle)
2032: ocfs2_journal_dirty(handle, wc->w_di_bh);
2033:
2034: out:
2035: /* unlock pages before dealloc since it needs acquiring j_trans_barrier
2036: * lock, or it will cause a deadlock since journal commit threads holds
2037: * this lock and will ask for the page lock when flushing the data.
2038: * put it here to preserve the unlock order.
2039: */
2040: ocfs2_unlock_folios(wc);
2041:
2042: if (handle)
2043: ocfs2_commit_trans(osb, handle);
2044:
2045: ocfs2_run_deallocs(osb, &wc->w_dealloc);
2046:
2047: brelse(wc->w_di_bh);
2048: kfree(wc);
2049:
2050: return copied;
2051: }
SourceFile:fs/ocfs2/aops.c]
|
| 20/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 25143
output: 46
thoughts: 226
Thoughts: **Decoding the Memory Copy Operation**
Okay, let's break this down. First, `memcpy_from_folio` β the name is a bit misleading. It's actually copying *from* the folio *to* the inline data. Got it. So the call is structured as `(destination, source_folio, offset_within_source, length)`. That makes the arguments clear: `di->id2.i_data.id_data + pos` is the destination, `wc->w_target_folio` is the source folio, `pos` is the offset *within* the source folio, and `*copied` is the number of bytes to copy.
Right, so the `VM_BUG_ON` condition is basically ensuring we don't read beyond the end of the folio. Specifically, `offset + len > folio_size(folio)`. Given our arguments, that's equivalent to `pos + *copied > folio_size(wc->w_target_folio)`. Makes sense.
Now, a critical point: what *is* `pos`? Is this the absolute file position, or an offset within the folio? This is crucial. I need to trace it. `pos` is passed from `ocfs2_write_end`, which gets it from `generic_perform_write`. That's where the definition lives! Let's just double-check the arguments being passed around. Looks like in `generic_perform_write`, `pos` *is* the absolute file position. We've got to confirm this with the preceding functions, though. I'll need to check the calling context of `ocfs2_write_end` and `generic_perform_write` to be absolutely sure.
|
| 21/2 |
2026/05/22 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_end]
Results: map[SourceCode:2053: static int ocfs2_write_end(const struct kiocb *iocb,
2054: struct address_space *mapping,
2055: loff_t pos, unsigned len, unsigned copied,
2056: struct folio *folio, void *fsdata)
2057: {
2058: int ret;
2059: struct inode *inode = mapping->host;
2060:
2061: ret = ocfs2_write_end_nolock(mapping, pos, len, copied, fsdata);
2062:
2063: up_write(&OCFS2_I(inode)->ip_alloc_sem);
2064: ocfs2_inode_unlock(inode, 1);
2065:
2066: return ret;
2067: }
SourceFile:fs/ocfs2/aops.c]
|
| 22/2 |
2026/05/22 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 26046
output: 46
thoughts: 518
Thoughts: **Debugging a Mysterious `VM_BUG_ON` in OCFS2**
Okay, so I'm staring at this `VM_BUG_ON` and scratching my head. The code seems straightforward enough, but the bug report is making me think. The error is happening in `memcpy_from_folio`, and it looks like it's complaining about an invalid offset. I know that `pos` represents the absolute file position, and `memcpy_from_folio` is expecting an offset within the folio. Now, I see that the code is passing `pos` as the offset. I was expecting this to work just fine if `pos` is less than or equal to the `folio_size`.
But then it hits me: the code is only using `ocfs2_write_end_inline` for inline data. This means the entire file is supposed to fit within the inode block. The maximum size of inline data should be limited by the block size, usually 4KB or less. If the block size is 4KB, then inline data is at most 4KB. So, if `pos` is at most 4KB, `pos + *copied` should be less than or equal to `folio_size(folio)`, right? It should never trigger the `VM_BUG_ON`.
But then I see the bug report, and the clues begin to fall into place. It mentions "detected capacity change" and "on-disk corruption." Could it be that the file system is messed up, and the value of `pos` is very large? But wait, the `pos` value comes from `generic_perform_write`, so that would represent the write position, right? I am thinking that this `pos` might be larger if we are writing to a large offset. But if the file *has* inline data, shouldn't `ocfs2_write_begin` have converted it to extents if `pos + len` exceeds the inline data size? This is the core of the issue. I think I need to look into `ocfs2_write_begin`.
|
| 23/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_begin]
Results: map[SourceCode:1861: static int ocfs2_write_begin(const struct kiocb *iocb,
1862: struct address_space *mapping,
1863: loff_t pos, unsigned len,
1864: struct folio **foliop, void **fsdata)
1865: {
1866: int ret;
1867: struct buffer_head *di_bh = NULL;
1868: struct inode *inode = mapping->host;
1869:
1870: ret = ocfs2_inode_lock(inode, &di_bh, 1);
1871: if (ret) {
1872: mlog_errno(ret);
1873: return ret;
1874: }
1875:
1876: /*
1877: * Take alloc sem here to prevent concurrent lookups. That way
1878: * the mapping, zeroing and tree manipulation within
1879: * ocfs2_write() will be safe against ->read_folio(). This
1880: * should also serve to lock out allocation from a shared
1881: * writeable region.
1882: */
1883: down_write(&OCFS2_I(inode)->ip_alloc_sem);
1884:
1885: ret = ocfs2_write_begin_nolock(mapping, pos, len, OCFS2_WRITE_BUFFER,
1886: foliop, fsdata, di_bh, NULL);
1887: if (ret) {
1888: mlog_errno(ret);
1889: goto out_fail;
1890: }
1891:
1892: brelse(di_bh);
1893:
1894: return 0;
1895:
1896: out_fail:
1897: up_write(&OCFS2_I(inode)->ip_alloc_sem);
1898:
1899: brelse(di_bh);
1900: ocfs2_inode_unlock(inode, 1);
1901:
1902: return ret;
1903: }
SourceFile:fs/ocfs2/aops.c]
|
| 24/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 27685
output: 49
thoughts: 16
Thoughts: Okay, here's my attempt at summarizing the thought process, as though I were an expert:
**Diving into `ocfs2_write_begin_nolock`**
Alright, time to dig into `ocfs2_write_begin_nolock`. I'm immediately interested in the "nolock" part. That suggests we're dealing with a path for writing that's aiming for efficiency, probably trading off some concurrency guarantees for speed. I need to understand exactly what level of locking is being bypassed here and the implications of that decision. Is this for a specific type of write? Maybe related to some sort of pre-allocated region or a particular file system optimization?
My first thought is to understand the context. Where is this function called from? What's the typical workflow leading up to this point? I need to trace the call paths to get a sense of the broader system behavior that relies on `ocfs2_write_begin_nolock`. I suspect there will be checks for file system state, maybe inode flags or some kind of write intent information. If itβs *nolock*, thereβs definitely a high chance that the code will have a very precise understanding of the data being written.
I'll be looking closely at any data structures involved, specifically the inode, the buffer head, and any associated meta-data. What information is assumed to be valid or consistent *without* holding a lock? Is there a carefully coordinated sequence of writes being orchestrated at a higher level that makes the no-lock approach safe? Any potential race conditions? I need to analyze how the crash consistency model is affected. How are journal entries or other persistent data structures handled to guarantee atomicity of the operation, even if the system were to crash during this "nolock" write?
Finally, the code itself. I'll need to dissect the logic. What specific operations are performed here? What kind of data is being modified, and what happens if the operation fails at any point? Are there any subtle assumptions baked into the implementation? Any opportunities for optimization or potential areas of concern? This should be a good starting point.
|
| 25/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_begin_nolock]
Results: map[SourceCode:1625: int ocfs2_write_begin_nolock(struct address_space *mapping,
1626: loff_t pos, unsigned len, ocfs2_write_type_t type,
1627: struct folio **foliop, void **fsdata,
1628: struct buffer_head *di_bh, struct folio *mmap_folio)
1629: {
1630: int ret, cluster_of_pages, credits = OCFS2_INODE_UPDATE_CREDITS;
1631: unsigned int clusters_to_alloc, extents_to_split, clusters_need = 0;
1632: struct ocfs2_write_ctxt *wc;
1633: struct inode *inode = mapping->host;
1634: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
1635: struct ocfs2_dinode *di;
1636: struct ocfs2_alloc_context *data_ac = NULL;
1637: struct ocfs2_alloc_context *meta_ac = NULL;
1638: handle_t *handle;
1639: struct ocfs2_extent_tree et;
1640: int try_free = 1, ret1;
1641:
1642: try_again:
1643: ret = ocfs2_alloc_write_ctxt(&wc, osb, pos, len, type, di_bh);
1644: if (ret) {
1645: mlog_errno(ret);
1646: return ret;
1647: }
1648:
1649: if (ocfs2_supports_inline_data(osb)) {
1650: ret = ocfs2_try_to_write_inline_data(mapping, inode, pos, len,
1651: mmap_folio, wc);
1652: if (ret == 1) {
1653: ret = 0;
1654: goto success;
1655: }
1656: if (ret < 0) {
1657: mlog_errno(ret);
1658: goto out;
1659: }
1660: }
1661:
1662: /* Direct io change i_size late, should not zero tail here. */
1663: if (type != OCFS2_WRITE_DIRECT) {
1664: if (ocfs2_sparse_alloc(osb))
1665: ret = ocfs2_zero_tail(inode, di_bh, pos);
1666: else
1667: ret = ocfs2_expand_nonsparse_inode(inode, di_bh, pos,
1668: len, wc);
1669: if (ret) {
1670: mlog_errno(ret);
1671: goto out;
1672: }
1673: }
1674:
1675: ret = ocfs2_check_range_for_refcount(inode, pos, len);
1676: if (ret < 0) {
1677: mlog_errno(ret);
1678: goto out;
1679: } else if (ret == 1) {
1680: clusters_need = wc->w_clen;
1681: ret = ocfs2_refcount_cow(inode, di_bh,
1682: wc->w_cpos, wc->w_clen, UINT_MAX);
1683: if (ret) {
1684: mlog_errno(ret);
1685: goto out;
1686: }
1687: }
1688:
1689: ret = ocfs2_populate_write_desc(inode, wc, &clusters_to_alloc,
1690: &extents_to_split);
1691: if (ret) {
1692: mlog_errno(ret);
1693: goto out;
1694: }
1695: clusters_need += clusters_to_alloc;
1696:
1697: di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
1698:
1699: trace_ocfs2_write_begin_nolock(
1700: (unsigned long long)OCFS2_I(inode)->ip_blkno,
1701: (long long)i_size_read(inode),
1702: le32_to_cpu(di->i_clusters),
1703: pos, len, type, mmap_folio,
1704: clusters_to_alloc, extents_to_split);
1705:
1706: /*
1707: * We set w_target_from, w_target_to here so that
1708: * ocfs2_write_end() knows which range in the target page to
1709: * write out. An allocation requires that we write the entire
1710: * cluster range.
1711: */
1712: if (clusters_to_alloc || extents_to_split) {
1713: /*
1714: * XXX: We are stretching the limits of
1715: * ocfs2_lock_allocators(). It greatly over-estimates
1716: * the work to be done.
1717: */
1718: ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode),
1719: wc->w_di_bh);
1720: ret = ocfs2_lock_allocators(inode, &et,
1721: clusters_to_alloc, extents_to_split,
1722: &data_ac, &meta_ac);
1723: if (ret) {
1724: mlog_errno(ret);
1725: goto out;
1726: }
1727:
1728: if (data_ac)
1729: data_ac->ac_resv = &OCFS2_I(inode)->ip_la_data_resv;
1730:
1731: credits = ocfs2_calc_extend_credits(inode->i_sb,
1732: &di->id2.i_list);
1733: } else if (type == OCFS2_WRITE_DIRECT)
1734: /* direct write needs not to start trans if no extents alloc. */
1735: goto success;
1736:
1737: /*
1738: * We have to zero sparse allocated clusters, unwritten extent clusters,
1739: * and non-sparse clusters we just extended. For non-sparse writes,
1740: * we know zeros will only be needed in the first and/or last cluster.
1741: */
1742: if (wc->w_clen && (wc->w_desc[0].c_needs_zero ||
1743: wc->w_desc[wc->w_clen - 1].c_needs_zero))
1744: cluster_of_pages = 1;
1745: else
1746: cluster_of_pages = 0;
1747:
1748: ocfs2_set_target_boundaries(osb, wc, pos, len, cluster_of_pages);
1749:
1750: handle = ocfs2_start_trans(osb, credits);
1751: if (IS_ERR(handle)) {
1752: ret = PTR_ERR(handle);
1753: mlog_errno(ret);
1754: goto out;
1755: }
1756:
1757: wc->w_handle = handle;
1758:
1759: if (clusters_to_alloc) {
1760: ret = dquot_alloc_space_nodirty(inode,
1761: ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc));
1762: if (ret)
1763: goto out_commit;
1764: }
1765:
1766: ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), wc->w_di_bh,
1767: OCFS2_JOURNAL_ACCESS_WRITE);
1768: if (ret) {
1769: mlog_errno(ret);
1770: goto out_quota;
1771: }
1772:
1773: /*
1774: * Fill our folio array first. That way we've grabbed enough so
1775: * that we can zero and flush if we error after adding the
1776: * extent.
1777: */
1778: ret = ocfs2_grab_folios_for_write(mapping, wc, wc->w_cpos, pos, len,
1779: cluster_of_pages, mmap_folio);
1780: if (ret) {
1781: /*
1782: * ocfs2_grab_folios_for_write() returns -EAGAIN if it
1783: * could not lock the target folio. In this case, we exit
1784: * with no error and no target folio. This will trigger
1785: * the caller, page_mkwrite(), to re-try the operation.
1786: */
1787: if (type == OCFS2_WRITE_MMAP && ret == -EAGAIN) {
1788: BUG_ON(wc->w_target_folio);
1789: ret = 0;
1790: goto out_quota;
1791: }
1792:
1793: mlog_errno(ret);
1794: goto out_quota;
1795: }
1796:
1797: ret = ocfs2_write_cluster_by_desc(mapping, data_ac, meta_ac, wc, pos,
1798: len);
1799: if (ret) {
1800: mlog_errno(ret);
1801: goto out_quota;
1802: }
1803:
1804: if (data_ac)
1805: ocfs2_free_alloc_context(data_ac);
1806: if (meta_ac)
1807: ocfs2_free_alloc_context(meta_ac);
1808:
1809: success:
1810: if (foliop)
1811: *foliop = wc->w_target_folio;
1812: *fsdata = wc;
1813: return 0;
1814: out_quota:
1815: if (clusters_to_alloc)
1816: dquot_free_space(inode,
1817: ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc));
1818: out_commit:
1819: ocfs2_commit_trans(osb, handle);
1820:
1821: out:
1822: /*
1823: * The mmapped page won't be unlocked in ocfs2_free_write_ctxt(),
1824: * even in case of error here like ENOSPC and ENOMEM. So, we need
1825: * to unlock the target page manually to prevent deadlocks when
1826: * retrying again on ENOSPC, or when returning non-VM_FAULT_LOCKED
1827: * to VM code.
1828: */
1829: if (wc->w_target_locked)
1830: folio_unlock(mmap_folio);
1831:
1832: ocfs2_free_write_ctxt(inode, wc);
1833:
1834: if (data_ac) {
1835: ocfs2_free_alloc_context(data_ac);
1836: data_ac = NULL;
1837: }
1838: if (meta_ac) {
1839: ocfs2_free_alloc_context(meta_ac);
1840: meta_ac = NULL;
1841: }
1842:
1843: if (ret == -ENOSPC && try_free) {
1844: /*
1845: * Try to free some truncate log so that we can have enough
1846: * clusters to allocate.
1847: */
1848: try_free = 0;
1849:
1850: ret1 = ocfs2_try_to_free_truncate_log(osb, clusters_need);
1851: if (ret1 == 1)
1852: goto try_again;
1853:
1854: if (ret1 < 0)
1855: mlog_errno(ret1);
1856: }
1857:
1858: return ret;
1859: }
SourceFile:fs/ocfs2/aops.c]
|
| 26/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 31837
output: 52
thoughts: 117
Thoughts: **Inline Data Optimization in OCFS2 Write Operations**
Okay, let's break this down. We're in `ocfs2_write_begin_nolock`, which means we're about to start a write operation without holding any locks, which is interesting in a clustered filesystem. The initial check immediately grabs my attention: `ocfs2_supports_inline_data(osb)`. This tells me the filesystem instance might support inline data, a common optimization.
So, the next logical step is to see if we *can* utilize this inline data feature. We jump straight to `ocfs2_try_to_write_inline_data`. We're passing a bunch of standard arguments: the `mapping`, the `inode`, the position (`pos`) and length (`len`) of the write, the memory map (`mmap_folio`), and a write context (`wc`). If that function returns `1`, we are going to assume that the write completed inline. We set the `ret` code to `0` and jump to `success`.
|
| 27/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_try_to_write_inline_data]
Results: map[SourceCode:1518: static int ocfs2_try_to_write_inline_data(struct address_space *mapping,
1519: struct inode *inode, loff_t pos, size_t len,
1520: struct folio *mmap_folio, struct ocfs2_write_ctxt *wc)
1521: {
1522: int ret, written = 0;
1523: loff_t end = pos + len;
1524: struct ocfs2_inode_info *oi = OCFS2_I(inode);
1525: struct ocfs2_dinode *di = NULL;
1526:
1527: trace_ocfs2_try_to_write_inline_data((unsigned long long)oi->ip_blkno,
1528: len, (unsigned long long)pos,
1529: oi->ip_dyn_features);
1530:
1531: /*
1532: * Handle inodes which already have inline data 1st.
1533: */
1534: if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
1535: if (mmap_folio == NULL &&
1536: ocfs2_size_fits_inline_data(wc->w_di_bh, end))
1537: goto do_inline_write;
1538:
1539: /*
1540: * The write won't fit - we have to give this inode an
1541: * inline extent list now.
1542: */
1543: ret = ocfs2_convert_inline_data_to_extents(inode, wc->w_di_bh);
1544: if (ret)
1545: mlog_errno(ret);
1546: goto out;
1547: }
1548:
1549: /*
1550: * Check whether the inode can accept inline data.
1551: */
1552: if (oi->ip_clusters != 0 || i_size_read(inode) != 0)
1553: return 0;
1554:
1555: /*
1556: * Check whether the write can fit.
1557: */
1558: di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
1559: if (mmap_folio ||
1560: end > ocfs2_max_inline_data_with_xattr(inode->i_sb, di))
1561: return 0;
1562:
1563: do_inline_write:
1564: ret = ocfs2_write_begin_inline(mapping, inode, wc);
1565: if (ret) {
1566: mlog_errno(ret);
1567: goto out;
1568: }
1569:
1570: /*
1571: * This signals to the caller that the data can be written
1572: * inline.
1573: */
1574: written = 1;
1575: out:
1576: return written ? written : ret;
1577: }
SourceFile:fs/ocfs2/aops.c]
|
| 28/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 33162
output: 50
thoughts: 112
Thoughts: **Inline Data Conundrum**
Right, so I'm digging through this code, specifically looking at `ocfs2_size_fits_inline_data(wc->w_di_bh, end)`. That function is checking if this write I'm looking at, this `end` value, can actually fit within the inline data region of an inode. Makes sense so far. But then a nasty thought creeps in β what happens if there's inode corruption? What if the inode *thinks* inline data is enabled (`oi->ip_dyn_features & OCFS2_INLINE_DATA_FL` is set), but `ocfs2_size_fits_inline_data` is *incorrectly* reporting that a ridiculously large `end` value fits? It's a potential vulnerability; this could lead to all sorts of havoc, potentially overwriting adjacent data or even corrupting the filesystem. Best double-check that `ocfs2_size_fits_inline_data` function now and see exactly what it's doing.
|
| 29/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_size_fits_inline_data]
Results: map[SourceCode:1509: int ocfs2_size_fits_inline_data(struct buffer_head *di_bh, u64 new_size)
1510: {
1511: struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
1512:
1513: if (new_size <= le16_to_cpu(di->id2.i_data.id_count))
1514: return 1;
1515: return 0;
1516: }
SourceFile:fs/ocfs2/aops.c]
|
| 30/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 33729
output: 48
thoughts: 221
Thoughts: **Analyzing a Potential Vulnerability in Inline Data Handling**
Okay, so I'm looking at this code and immediately I see `di->id2.i_data.id_count` being read from disk. The red flag goes up immediately β this value is vulnerable if the disk is corrupt. Since it's a 16-bit integer, a corrupted disk could potentially set `id_count` to its maximum value, 65535.
Now, if `id_count` *is* 65535, `ocfs2_size_fits_inline_data` will, unsurprisingly, return 1 for a `new_size` up to 65535. That seems reasonable on the surface. But then I have to consider the context. `folio_size(wc->w_target_folio)` is typically `PAGE_SIZE`, which is 4096. This is where things get interesting.
Let's say `pos + len` β which is the same as `end` in this code β is, let's say, 5000. 5000 is, of course, less than or equal to 65535, so `ocfs2_try_to_write_inline_data` returns 1. This means the system will then call `ocfs2_write_begin_inline`. That's where I need to dig deeper. I need to understand what happens inside `ocfs2_write_begin_inline` because I suspect this is where the potential vulnerability lies.
|
| 31/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_write_begin_inline]
Results: map[SourceCode:1450: static int ocfs2_write_begin_inline(struct address_space *mapping,
1451: struct inode *inode,
1452: struct ocfs2_write_ctxt *wc)
1453: {
1454: int ret;
1455: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
1456: struct folio *folio;
1457: handle_t *handle;
1458: struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
1459:
1460: handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
1461: if (IS_ERR(handle)) {
1462: ret = PTR_ERR(handle);
1463: mlog_errno(ret);
1464: goto out;
1465: }
1466:
1467: folio = __filemap_get_folio(mapping, 0,
1468: FGP_LOCK | FGP_ACCESSED | FGP_CREAT, GFP_NOFS);
1469: if (IS_ERR(folio)) {
1470: ocfs2_commit_trans(osb, handle);
1471: ret = PTR_ERR(folio);
1472: mlog_errno(ret);
1473: goto out;
1474: }
1475: /*
1476: * If we don't set w_num_folios then this folio won't get unlocked
1477: * and freed on cleanup of the write context.
1478: */
1479: wc->w_target_folio = folio;
1480: wc->w_folios[0] = folio;
1481: wc->w_num_folios = 1;
1482:
1483: ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), wc->w_di_bh,
1484: OCFS2_JOURNAL_ACCESS_WRITE);
1485: if (ret) {
1486: ocfs2_commit_trans(osb, handle);
1487:
1488: mlog_errno(ret);
1489: goto out;
1490: }
1491:
1492: if (!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL))
1493: ocfs2_set_inode_data_inline(inode, di);
1494:
1495: if (!folio_test_uptodate(folio)) {
1496: ret = ocfs2_read_inline_data(inode, folio, wc->w_di_bh);
1497: if (ret) {
1498: ocfs2_commit_trans(osb, handle);
1499:
1500: goto out;
1501: }
1502: }
1503:
1504: wc->w_handle = handle;
1505: out:
1506: return ret;
1507: }
SourceFile:fs/ocfs2/aops.c]
|
| 32/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 35268
output: 47
thoughts: 1071
Thoughts: **Analysis of Potential OCFS2 Vulnerability**
Okay, so I'm digging into this `ocfs2_write_end_inline` function. My initial thought is, based on the code, there's a memcpy potentially going wrong. It gets the `folio` at index 0 in `ocfs2_write_begin_inline`, which makes sense. Then the copy happens with `memcpy_from_folio`. The thing that immediately jumps out is the `pos` value; if `pos` is bigger than a page, `VM_BUG_ON` *should* kick in, but if `CONFIG_DEBUG_VM` is disabled, it does nothing. That's a potential blind spot, so I'll keep that in mind.
Now I look at `kmap_local_folio` and how it's used. It's just adding an offset to the folio's address. So, `from` becomes `folio_address(folio) + pos`. But that `folio` is just *one* page, right? Like, 4096 bytes. If `pos` is, say, 5000, `from` points *outside* the bounds of that page! That's a definite out-of-bounds read from the page cache. That's a nasty bug.
Okay, let's switch gears and look at the destination. The memcpy is writing to `di->id2.i_data.id_data + pos`. `di` is a pointer to buffer head data, which is associated with the inode block. The size of the inode block is either `osb->s_clustersize` or `osb->sb->s_blocksize`. I need to figure out if `pos + *copied` can actually exceed the block size.
That `ocfs2_size_fits_inline_data` check is key. It checks `new_size <= le16_to_cpu(di->id2.i_data.id_count)`. If `id_count` is corrupted and is *huge*, like 65535, `new_size` is similarly unbounded. The block size itself is typically 4096 (or maybe 8192, but typically 4096), so in that scenario, the write goes out of bounds. This is a *heap out-of-bounds write*. Big problem!
Thinking about it more, `di` is actually `wc->w_di_bh->b_data`. `b_data` is allocated by the block layer. Usually, it's a page or part of a page. If we write a bunch of bytes β up to 65535, we're definitely overwriting stuff, either within the same page, crossing page boundaries and triggering a fault, or potentially overwriting other kernel memory if it's kmalloc'ed (though `b_data` is usually in the page cache).
And now I remember `b_data` is actually `page_address(bh->b_page) + bh_offset(bh)`. It's a direct map address. This is the crucial point; writing up to 64KB starting from `b_data` is overwriting *up to 16 pages* in the direct map! This is a *massive* out-of-bounds write. This is a very exploitable vulnerability.
Hold on, I need to check something from the bug report. The report mentions `ocfs2_validate_inode_block` failed. If that validation failed, why did the code continue? I'll need to double-check that part, how is the inode validated to begin with? I need to check `ocfs2_validate_inode_block` carefully, because the failure would have prevented this exploit.
|
| 33/2 |
2026/05/22 01:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/inode.c IncludeLines:true Name:ocfs2_validate_inode_block]
Results: map[SourceCode:1423: int ocfs2_validate_inode_block(struct super_block *sb,
1424: struct buffer_head *bh)
1425: {
1426: int rc;
1427: struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data;
1428:
1429: trace_ocfs2_validate_inode_block((unsigned long long)bh->b_blocknr);
1430:
1431: BUG_ON(!buffer_uptodate(bh));
1432:
1433: /*
1434: * If the ecc fails, we return the error but otherwise
1435: * leave the filesystem running. We know any error is
1436: * local to this block.
1437: */
1438: rc = ocfs2_validate_meta_ecc(sb, bh->b_data, &di->i_check);
1439: if (rc) {
1440: mlog(ML_ERROR, "Checksum failed for dinode %llu\n",
1441: (unsigned long long)bh->b_blocknr);
1442: goto bail;
1443: }
1444:
1445: if ((!di->i_links_count && !di->i_links_count_hi) || !di->i_mode) {
1446: mlog(ML_ERROR, "Invalid dinode #%llu: "
1447: "Corrupt state (nlink = %u or mode = %u) detected!\n",
1448: (unsigned long long)bh->b_blocknr,
1449: ocfs2_read_links_count(di), le16_to_cpu(di->i_mode));
1450: rc = -EFSCORRUPTED;
1451: goto bail;
1452: }
1453: /*
1454: * Errors after here are fatal.
1455: */
1456:
1457: rc = -EINVAL;
1458:
1459: if (!OCFS2_IS_VALID_DINODE(di)) {
1460: rc = ocfs2_error(sb, "Invalid dinode #%llu: signature = %.*s\n",
1461: (unsigned long long)bh->b_blocknr, 7,
1462: di->i_signature);
1463: goto bail;
1464: }
1465:
1466: if (le64_to_cpu(di->i_blkno) != bh->b_blocknr) {
1467: rc = ocfs2_error(sb, "Invalid dinode #%llu: i_blkno is %llu\n",
1468: (unsigned long long)bh->b_blocknr,
1469: (unsigned long long)le64_to_cpu(di->i_blkno));
1470: goto bail;
1471: }
1472:
1473: if (!(le32_to_cpu(di->i_flags) & OCFS2_VALID_FL)) {
1474: rc = ocfs2_error(sb,
1475: "Invalid dinode #%llu: OCFS2_VALID_FL not set\n",
1476: (unsigned long long)bh->b_blocknr);
1477: goto bail;
1478: }
1479:
1480: if (le32_to_cpu(di->i_fs_generation) !=
1481: OCFS2_SB(sb)->fs_generation) {
1482: rc = ocfs2_error(sb,
1483: "Invalid dinode #%llu: fs_generation is %u\n",
1484: (unsigned long long)bh->b_blocknr,
1485: le32_to_cpu(di->i_fs_generation));
1486: goto bail;
1487: }
1488:
1489: if (le16_to_cpu(di->i_suballoc_slot) != (u16)OCFS2_INVALID_SLOT &&
1490: (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) {
1491: rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n",
1492: (unsigned long long)bh->b_blocknr,
1493: le16_to_cpu(di->i_suballoc_slot));
1494: goto bail;
1495: }
1496:
1497: if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
1498: struct ocfs2_inline_data *data = &di->id2.i_data;
1499:
1500: if (le32_to_cpu(di->i_clusters)) {
1501: rc = ocfs2_error(sb,
1502: "Invalid dinode %llu: %u clusters\n",
1503: (unsigned long long)bh->b_blocknr,
1504: le32_to_cpu(di->i_clusters));
1505: goto bail;
1506: }
1507:
1508: if (le16_to_cpu(data->id_count) >
1509: ocfs2_max_inline_data_with_xattr(sb, di)) {
1510: rc = ocfs2_error(sb,
1511: "Invalid dinode #%llu: inline data id_count %u exceeds max %d\n",
1512: (unsigned long long)bh->b_blocknr,
1513: le16_to_cpu(data->id_count),
1514: ocfs2_max_inline_data_with_xattr(sb, di));
1515: goto bail;
1516: }
1517:
1518: if (le64_to_cpu(di->i_size) > le16_to_cpu(data->id_count)) {
1519: rc = ocfs2_error(sb,
1520: "Invalid dinode #%llu: inline data i_size %llu exceeds id_count %u\n",
1521: (unsigned long long)bh->b_blocknr,
1522: (unsigned long long)le64_to_cpu(di->i_size),
1523: le16_to_cpu(data->id_count));
1524: goto bail;
1525: }
1526: }
1527:
1528: if (le32_to_cpu(di->i_flags) & OCFS2_CHAIN_FL) {
1529: struct ocfs2_chain_list *cl = &di->id2.i_chain;
1530: u16 bpc = 1 << (OCFS2_SB(sb)->s_clustersize_bits -
1531: sb->s_blocksize_bits);
1532:
1533: if (le16_to_cpu(cl->cl_count) != ocfs2_chain_recs_per_inode(sb)) {
1534: rc = ocfs2_error(sb, "Invalid dinode %llu: chain list count %u\n",
1535: (unsigned long long)bh->b_blocknr,
1536: le16_to_cpu(cl->cl_count));
1537: goto bail;
1538: }
1539: if (le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
1540: rc = ocfs2_error(sb, "Invalid dinode %llu: chain list index %u\n",
1541: (unsigned long long)bh->b_blocknr,
1542: le16_to_cpu(cl->cl_next_free_rec));
1543: goto bail;
1544: }
1545: if (OCFS2_SB(sb)->bitmap_blkno &&
1546: OCFS2_SB(sb)->bitmap_blkno != le64_to_cpu(di->i_blkno) &&
1547: le16_to_cpu(cl->cl_bpc) != bpc) {
1548: rc = ocfs2_error(sb, "Invalid dinode %llu: bits per cluster %u\n",
1549: (unsigned long long)bh->b_blocknr,
1550: le16_to_cpu(cl->cl_bpc));
1551: goto bail;
1552: }
1553: }
1554:
1555: if ((le16_to_cpu(di->i_dyn_features) & OCFS2_HAS_REFCOUNT_FL) &&
1556: !di->i_refcount_loc) {
1557: rc = ocfs2_error(sb, "Inode #%llu has refcount flag but no i_refcount_loc\n",
1558: (unsigned long long)bh->b_blocknr);
1559: goto bail;
1560: }
1561:
1562: rc = 0;
1563:
1564: bail:
1565: return rc;
1566: }
SourceFile:fs/ocfs2/inode.c]
|
| 34/2 |
2026/05/22 01:45 |
llm |
expert |
0m
Model:
Tokens:
input: 39896
output: 47
thoughts: 1204
Thoughts: **Analyzing an OCFS2 Read-Only Bug During `copy_file_range`**
Okay, so I'm looking at this bug report involving an OCFS2 filesystem going read-only. The key is in the `ocfs2_validate_inode_block` function. It checks `id_count` in the inode data structure against a maximum value, and if it exceeds it, an error is triggered, setting the filesystem to read-only. The question is, how does this interact with a write operation, and why doesn't the write fail? I remember `ocfs2_validate_inode_block` is called when reading an inode block from disk, which causes a read-only filesystem.
The error report shows that the error occurs on dinode #17058, but the `copy_file_range` operation fails. This suggests the validation failure and the write may involve different inodes, or possibly the invalid inode was in memory at some point.
The call trace reveals the culprit: `copy_file_range`. It looks like it is calling `vfs_copy_file_range`, then `do_splice_direct`, and ultimately `generic_perform_write`. That's interesting, because `copy_file_range` can try to reflink files.
So, here's the scenario: `copy_file_range` *first* attempts a reflink. During the reflink attempt, `ocfs2_reflink_inodes_lock` is called, which means it reads an inode. This is where `ocfs2_validate_inode_block` comes in, possibly validating an inode other than the one being written to, and fails, triggering the read-only error. The reflink fails due to `-EROFS` since the filesystem is now read-only. Then the operation falls back to `do_splice_direct` and, finally, `generic_perform_write`.
The real question is: why does `generic_perform_write` even *try* to write if the filesystem is read-only? I need to check the code path in `ocfs2_file_write_iter` and ensure there's a proper check for the read-only status. It seems like the fallback mechanism isn't properly considering the filesystem's read-only state. That's a definite point to investigate further.
|
| 35/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/file.c IncludeLines:true Name:ocfs2_file_write_iter]
Results: map[SourceCode:2372: static ssize_t ocfs2_file_write_iter(struct kiocb *iocb,
2373: struct iov_iter *from)
2374: {
2375: int rw_level;
2376: ssize_t written = 0;
2377: ssize_t ret;
2378: size_t count = iov_iter_count(from);
2379: struct file *file = iocb->ki_filp;
2380: struct inode *inode = file_inode(file);
2381: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
2382: int full_coherency = !(osb->s_mount_opt &
2383: OCFS2_MOUNT_COHERENCY_BUFFERED);
2384: void *saved_ki_complete = NULL;
2385: int append_write = ((iocb->ki_pos + count) >=
2386: i_size_read(inode) ? 1 : 0);
2387: int direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0;
2388: int nowait = iocb->ki_flags & IOCB_NOWAIT ? 1 : 0;
2389:
2390: trace_ocfs2_file_write_iter(inode, file, file->f_path.dentry,
2391: (unsigned long long)OCFS2_I(inode)->ip_blkno,
2392: file->f_path.dentry->d_name.len,
2393: file->f_path.dentry->d_name.name,
2394: (unsigned int)from->nr_segs); /* GRRRRR */
2395:
2396: if (!direct_io && nowait)
2397: return -EOPNOTSUPP;
2398:
2399: if (count == 0)
2400: return 0;
2401:
2402: if (nowait) {
2403: if (!inode_trylock(inode))
2404: return -EAGAIN;
2405: } else
2406: inode_lock(inode);
2407:
2408: ocfs2_iocb_init_rw_locked(iocb);
2409:
2410: /*
2411: * Concurrent O_DIRECT writes are allowed with
2412: * mount_option "coherency=buffered".
2413: * For append write, we must take rw EX.
2414: */
2415: rw_level = (!direct_io || full_coherency || append_write);
2416:
2417: if (nowait)
2418: ret = ocfs2_try_rw_lock(inode, rw_level);
2419: else
2420: ret = ocfs2_rw_lock(inode, rw_level);
2421: if (ret < 0) {
2422: if (ret != -EAGAIN)
2423: mlog_errno(ret);
2424: goto out_mutex;
2425: }
2426:
2427: /*
2428: * O_DIRECT writes with "coherency=full" need to take EX cluster
2429: * inode_lock to guarantee coherency.
2430: */
2431: if (direct_io && full_coherency) {
2432: /*
2433: * We need to take and drop the inode lock to force
2434: * other nodes to drop their caches. Buffered I/O
2435: * already does this in write_begin().
2436: */
2437: if (nowait)
2438: ret = ocfs2_try_inode_lock(inode, NULL, 1);
2439: else
2440: ret = ocfs2_inode_lock(inode, NULL, 1);
2441: if (ret < 0) {
2442: if (ret != -EAGAIN)
2443: mlog_errno(ret);
2444: goto out;
2445: }
2446:
2447: ocfs2_inode_unlock(inode, 1);
2448: }
2449:
2450: ret = generic_write_checks(iocb, from);
2451: if (ret <= 0) {
2452: if (ret)
2453: mlog_errno(ret);
2454: goto out;
2455: }
2456: count = ret;
2457:
2458: ret = ocfs2_prepare_inode_for_write(file, iocb->ki_pos, count, !nowait);
2459: if (ret < 0) {
2460: if (ret != -EAGAIN)
2461: mlog_errno(ret);
2462: goto out;
2463: }
2464:
2465: if (direct_io && !is_sync_kiocb(iocb) &&
2466: ocfs2_is_io_unaligned(inode, count, iocb->ki_pos)) {
2467: /*
2468: * Make it a sync io if it's an unaligned aio.
2469: */
2470: saved_ki_complete = xchg(&iocb->ki_complete, NULL);
2471: }
2472:
2473: /* communicate with ocfs2_dio_end_io */
2474: ocfs2_iocb_set_rw_locked(iocb, rw_level);
2475:
2476: written = __generic_file_write_iter(iocb, from);
2477: /* buffered aio wouldn't have proper lock coverage today */
2478: BUG_ON(written == -EIOCBQUEUED && !direct_io);
2479:
2480: /*
2481: * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io
2482: * function pointer which is called when o_direct io completes so that
2483: * it can unlock our rw lock.
2484: * Unfortunately there are error cases which call end_io and others
2485: * that don't. so we don't have to unlock the rw_lock if either an
2486: * async dio is going to do it in the future or an end_io after an
2487: * error has already done it.
2488: */
2489: if ((written == -EIOCBQUEUED) || (!ocfs2_iocb_is_rw_locked(iocb))) {
2490: rw_level = -1;
2491: }
2492:
2493: if (unlikely(written <= 0))
2494: goto out;
2495:
2496: if (((file->f_flags & O_DSYNC) && !direct_io) ||
2497: IS_SYNC(inode)) {
2498: ret = filemap_fdatawrite_range(file->f_mapping,
2499: iocb->ki_pos - written,
2500: iocb->ki_pos - 1);
2501: if (ret < 0)
2502: written = ret;
2503:
2504: if (!ret) {
2505: ret = jbd2_journal_force_commit(osb->journal->j_journal);
2506: if (ret < 0)
2507: written = ret;
2508: }
2509:
2510: if (!ret)
2511: ret = filemap_fdatawait_range(file->f_mapping,
2512: iocb->ki_pos - written,
2513: iocb->ki_pos - 1);
2514: }
2515:
2516: out:
2517: if (saved_ki_complete)
2518: xchg(&iocb->ki_complete, saved_ki_complete);
2519:
2520: if (rw_level != -1)
2521: ocfs2_rw_unlock(inode, rw_level);
2522:
2523: out_mutex:
2524: inode_unlock(inode);
2525:
2526: if (written)
2527: ret = written;
2528: return ret;
2529: }
SourceFile:fs/ocfs2/file.c]
|
| 36/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 44129
output: 44
thoughts: 487
Thoughts: **Unraveling a Potential Read-Only File System Race Condition**
Okay, let's break this down. My initial thought was about `__generic_file_write_iter` and its call to `generic_perform_write`. I was wondering if `generic_write_checks` could potentially miss a read-only filesystem scenario if the filesystem transitioned to read-only *after* the check. Or, maybe it doesn't even check for it at all?
But then I recalled that `sb->s_flags & SB_RDONLY` *is* checked in `file_start_write`, which happens *before* `vfs_write` or `vfs_copy_file_range`. That's important.
The crux of the matter lies in `vfs_copy_file_range`. If the filesystem gets marked read-only *during* that call, like within `ocfs2_reflink_inodes_lock`, then `do_splice_direct` might still proceed because it doesn't re-check `SB_RDONLY`. Bingo!
Here's the sequence I'm piecing together:
1. `vfs_copy_file_range` is initiated.
2. It calls the file's operation-specific `copy_file_range`, which, in this case, is `ocfs2_copy_file_range`.
3. `ocfs2_copy_file_range` attempts a reflink operation and calls `ocfs2_reflink_inodes_lock`.
4. `ocfs2_reflink_inodes_lock` reads the inode block of either the source or destination.
5. If the inode block is corrupted β let's say `id_count` is excessive, or the signature's wrong β things go south.
6. `ocfs2_validate_inode_block` fails, triggers `ocfs2_error`, which *then* marks the filesystem as read-only.
7. `ocfs2_reflink_inodes_lock` returns an error, such as `-EROFS` or `-EFSCORRUPTED`.
8. My hypothesis is that `ocfs2_copy_file_range` then returns either `-EOPNOTSUPP` or similar, perhaps even `-EXDEV`. And here's where it gets interesting...
If `ocfs2_copy_file_range` returns either `-EOPNOTSUPP` or `-EXDEV`, `vfs_copy_file_range` falls back to `do_splice_direct`. And *that* is the problem! `do_splice_direct` likely *doesn't* re-check the filesystem's read-only status. I need to confirm this. I need to check `vfs_copy_file_range` in `fs/read_write.c`.
|
| 37/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/read_write.c IncludeLines:true Name:vfs_copy_file_range]
Results: map[SourceCode:1549: /*
1550: * copy_file_range() differs from regular file read and write in that it
1551: * specifically allows return partial success. When it does so is up to
1552: * the copy_file_range method.
1553: */
1554: ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
1555: struct file *file_out, loff_t pos_out,
1556: size_t len, unsigned int flags)
1557: {
1558: ssize_t ret;
1559: bool splice = flags & COPY_FILE_SPLICE;
1560: bool samesb = file_inode(file_in)->i_sb == file_inode(file_out)->i_sb;
1561:
1562: if (flags & ~COPY_FILE_SPLICE)
1563: return -EINVAL;
1564:
1565: ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
1566: flags);
1567: if (unlikely(ret))
1568: return ret;
1569:
1570: ret = rw_verify_area(READ, file_in, &pos_in, len);
1571: if (unlikely(ret))
1572: return ret;
1573:
1574: ret = rw_verify_area(WRITE, file_out, &pos_out, len);
1575: if (unlikely(ret))
1576: return ret;
1577:
1578: if (len == 0)
1579: return 0;
1580:
1581: /*
1582: * Make sure return value doesn't overflow in 32bit compat mode. Also
1583: * limit the size for all cases except when calling ->copy_file_range().
1584: */
1585: if (splice || !file_out->f_op->copy_file_range || in_compat_syscall())
1586: len = min_t(size_t, MAX_RW_COUNT, len);
1587:
1588: file_start_write(file_out);
1589:
1590: /*
1591: * Cloning is supported by more file systems, so we implement copy on
1592: * same sb using clone, but for filesystems where both clone and copy
1593: * are supported (e.g. nfs,cifs), we only call the copy method.
1594: */
1595: if (!splice && file_out->f_op->copy_file_range) {
1596: ret = file_out->f_op->copy_file_range(file_in, pos_in,
1597: file_out, pos_out,
1598: len, flags);
1599: } else if (!splice && file_in->f_op->remap_file_range && samesb) {
1600: ret = file_in->f_op->remap_file_range(file_in, pos_in,
1601: file_out, pos_out, len, REMAP_FILE_CAN_SHORTEN);
1602: /* fallback to splice */
1603: if (ret <= 0)
1604: splice = true;
1605: } else if (samesb) {
1606: /* Fallback to splice for same sb copy for backward compat */
1607: splice = true;
1608: }
1609:
1610: file_end_write(file_out);
1611:
1612: if (!splice)
1613: goto done;
1614:
1615: /*
1616: * We can get here for same sb copy of filesystems that do not implement
1617: * ->copy_file_range() in case filesystem does not support clone or in
1618: * case filesystem supports clone but rejected the clone request (e.g.
1619: * because it was not block aligned).
1620: *
1621: * In both cases, fall back to kernel copy so we are able to maintain a
1622: * consistent story about which filesystems support copy_file_range()
1623: * and which filesystems do not, that will allow userspace tools to
1624: * make consistent desicions w.r.t using copy_file_range().
1625: *
1626: * We also get here if caller (e.g. nfsd) requested COPY_FILE_SPLICE
1627: * for server-side-copy between any two sb.
1628: *
1629: * In any case, we call do_splice_direct() and not splice_file_range(),
1630: * without file_start_write() held, to avoid possible deadlocks related
1631: * to splicing from input file, while file_start_write() is held on
1632: * the output file on a different sb.
1633: */
1634: ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0);
1635: done:
1636: if (ret > 0) {
1637: fsnotify_access(file_in);
1638: add_rchar(current, ret);
1639: fsnotify_modify(file_out);
1640: add_wchar(current, ret);
1641: }
1642:
1643: inc_syscr(current);
1644: inc_syscw(current);
1645:
1646: return ret;
1647: }
SourceFile:fs/read_write.c]
|
| 38/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 46932
output: 44
thoughts: 43
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience:
**OCFS2 and File Range Operations**
Wait a second...I was pretty sure `ocfs2` leveraged `copy_file_range` for efficient data movement, but now I'm doubting myself. That implementation detail has been bothering me, and I seem to recall it using `remap_file_range` instead, or maybe it's both in some convoluted configuration? Let me clarify my mental model of that. Time to delve into the `ocfs2_file_operations` structure and confirm this. I need to be sure about this to understand the implications on performance. It's frustrating when you have such a simple question, but the memory fails... I have some checking to do. Let's see what's actually in there!
|
| 39/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/file.c IncludeLines:true Name:ocfs2_fops]
Results: map[SourceCode:2804: /*
2805: * Other than ->lock, keep ocfs2_fops and ocfs2_dops in sync with
2806: * ocfs2_fops_no_plocks and ocfs2_dops_no_plocks!
2807: */
2808: const struct file_operations ocfs2_fops = {
2809: .llseek = ocfs2_file_llseek,
2810: .mmap_prepare = ocfs2_mmap_prepare,
2811: .fsync = ocfs2_sync_file,
2812: .release = ocfs2_file_release,
2813: .open = ocfs2_file_open,
2814: .read_iter = ocfs2_file_read_iter,
2815: .write_iter = ocfs2_file_write_iter,
2816: .unlocked_ioctl = ocfs2_ioctl,
2817: #ifdef CONFIG_COMPAT
2818: .compat_ioctl = ocfs2_compat_ioctl,
2819: #endif
2820: .lock = ocfs2_lock,
2821: .flock = ocfs2_flock,
2822: .splice_read = ocfs2_file_splice_read,
2823: .splice_write = iter_file_splice_write,
2824: .fallocate = ocfs2_fallocate,
2825: .remap_file_range = ocfs2_remap_file_range,
2826: .fop_flags = FOP_ASYNC_LOCK,
2827: .setlease = generic_setlease,
2828: };
SourceFile:fs/ocfs2/file.c]
|
| 40/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 47708
output: 45
thoughts: 615
Thoughts: **OCFS2 and Read-Only Filesystem Handling in `vfs_copy_file_range`**
Okay, so `ocfs2_remap_file_range` is definitely implemented, that much is clear. My initial assumption was that `vfs_copy_file_range` would simply call it. And that's true, it does. Now, if `ocfs2_remap_file_range` returns a value less than or equal to zero, which includes an error, `splice` gets set to `true`, and it falls back to `do_splice_direct`.
Right, now the issue. `ocfs2_remap_file_range` is returning `-EROFS`, which is `-30`. Why? Ah, because `ocfs2_validate_inode_block` failed and marked the filesystem read-only. So, `vfs_copy_file_range` sees that negative return value, and correctly invokes `do_splice_direct`. This is where it gets interesting...
`do_splice_direct` will then call `ocfs2_file_write_iter`. The crucial question here is: does `ocfs2_file_write_iter` check if the filesystem is read-only? I need to verify this carefully. Let me double-check that `ocfs2_file_write_iter`... Okay, it calls `generic_write_checks`. Now, `generic_write_checks` does check for `IS_IMMUTABLE` and `IS_APPEND` flags, but does it cover the `SB_RDONLY` flag? No, it doesn't. The `SB_RDONLY` check is performed in `file_start_write`. That's called by `vfs_copy_file_range` *before* `ocfs2_remap_file_range`. However, here's the problem: when `ocfs2_remap_file_range` marked the filesystem read-only, `file_start_write` was already running! The check didn't happen in the correct order.
So, `do_splice_direct` proceeds, and therefore `ocfs2_file_write_iter` is running. That then calls `__generic_file_write_iter` and then `generic_perform_write`. And `generic_perform_write` in turn calls `a_ops->write_begin`, which is `ocfs2_write_begin`. `ocfs2_write_begin` calls `ocfs2_write_begin_nolock`. Now, within `ocfs2_write_begin_nolock`, it calls `ocfs2_try_to_write_inline_data`. Does *that* check for read-only? Need to verify. Let me look at `ocfs2_write_begin_nolock`... Okay, it calls `ocfs2_alloc_write_ctxt`, then calls `ocfs2_try_to_write_inline_data`. Nope. It doesn't check the read-only flag. Wait! Ah, `ocfs2_start_trans` checks if the filesystem is read-only! Let's check `ocfs2_start_trans`.
|
| 41/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/journal.c IncludeLines:true Name:ocfs2_start_trans]
Results: map[SourceCode: 350: handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
351: {
352: journal_t *journal = osb->journal->j_journal;
353: handle_t *handle;
354:
355: BUG_ON(!osb || !osb->journal->j_journal);
356:
357: if (ocfs2_is_hard_readonly(osb))
358: return ERR_PTR(-EROFS);
359:
360: BUG_ON(osb->journal->j_state == OCFS2_JOURNAL_FREE);
361: BUG_ON(max_buffs <= 0);
362:
363: /* Nested transaction? Just return the handle... */
364: if (journal_current_handle())
365: return jbd2_journal_start(journal, max_buffs);
366:
367: sb_start_intwrite(osb->sb);
368:
369: down_read(&osb->journal->j_trans_barrier);
370:
371: handle = jbd2_journal_start(journal, max_buffs);
372: if (IS_ERR(handle)) {
373: up_read(&osb->journal->j_trans_barrier);
374: sb_end_intwrite(osb->sb);
375:
376: mlog_errno(PTR_ERR(handle));
377:
378: if (is_journal_aborted(journal)) {
379: ocfs2_abort(osb->sb, "Detected aborted journal\n");
380: handle = ERR_PTR(-EROFS);
381: }
382: } else {
383: if (!ocfs2_mount_local(osb))
384: atomic_inc(&(osb->journal->j_num_trans));
385: }
386:
387: return handle;
388: }
SourceFile:fs/ocfs2/journal.c]
|
| 42/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 49703
output: 49
thoughts: 41
Thoughts: Here's my take on the thought process, as if it were my own:
**Transaction Initiation and Read-Only State**
Okay, so I'm looking at `ocfs2_start_trans`. The first thing this function does is check `ocfs2_is_hard_readonly(osb)`. Makes sense. Transactions in OCFS2 need to respect read-only states. A read-only filesystem should not be allowed to perform any modifications. So, my immediate thought is, let's drill down into `ocfs2_is_hard_readonly`. I need to understand how the system determines this read-only state. What triggers it? What are the implications? I'm going to follow the logic within `ocfs2_is_hard_readonly` to get a clearer picture of that part of the process. This seems like a critical checkpoint before initiating any transactions. Understanding the read-only flag is crucial for ensuring data integrity and the correct operation of OCFS2.
|
| 43/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/ocfs2.h IncludeLines:true Name:ocfs2_is_hard_readonly]
Results: map[SourceCode: 661: static inline int ocfs2_is_hard_readonly(struct ocfs2_super *osb)
662: {
663: int ret;
664:
665: spin_lock(&osb->osb_lock);
666: ret = osb->osb_flags & OCFS2_OSB_HARD_RO;
667: spin_unlock(&osb->osb_lock);
668:
669: return ret;
670: }
SourceFile:fs/ocfs2/ocfs2.h]
|
| 44/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 50186
output: 43
thoughts: 46
Thoughts: Okay, let's see...
**Digging into that OCFS2 Error**
Hold on a second. I was under the impression that the `ocfs2_error` function was supposed to set the `OCFS2_OSB_HARD_RO` bit, but now I'm questioning that. I need to be absolutely sure because that's critical for how the filesystem handles errors. It's time to check the source code for `ocfs2_error` and verify exactly what it's setting. Gotta make sure I'm not missing something subtle here that's going to bite me later. Back to the code!
|
| 45/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_error]
Results: map[SourceCode: SourceFile:]
|
| 46/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 50433
output: 20
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm the expert and considering how I'd approach it:
**Digging into an OCFS2 Error**
Alright, the first thing that jumps to my mind is to zero in on the problem using `grep`. I want to quickly sift through logs or potentially code to find instances of `ocfs2_error`. That string, `ocfs2_error`, is pretty specific, and it's a good starting point because it likely flags something significant within the OCFS2 subsystem. It's the kind of keyword that a well-written error logging mechanism would use. My intention here is to cast a wide net initially, hoping to catch any relevant output.
|
| 47/2 |
2026/05/22 01:46 |
tool |
grepper |
0m
Error:
Args: map[Expression:void ocfs2_error]
Results:
|
| 48/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 50634
output: 20
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Debugging OCFS2 Error Handling**
Okay, so I'm diving into an OCFS2 issue, and the obvious first step is to trace the error paths. "ocfs2_error" is a pretty generic term, but it's a good starting point. I need to understand where this particular error is being raised and why. The kernel source code is my playground. I need to locate where `ocfs2_error` is actually *defined* within the source tree. Once I find that definition, I'll be able to see all the places it's being *called*. That will give me a map of all the possible failure points in the OCFS2 code. From there, I'll be able to look at the context, see the conditions that trigger the error, and hopefully identify the root cause of the problem I'm trying to solve. I'll probably start with a `grep` to quickly search the codebase for that exact string. Let's see what that turns up.
|
| 49/2 |
2026/05/22 01:46 |
tool |
grepper |
0m
Args: map[Expression:ocfs2_error\(]
Results: map[Output:fs/ocfs2/alloc.c=877=static int ocfs2_validate_extent_block(struct super_block *sb,
--
fs/ocfs2/alloc.c-904- if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
fs/ocfs2/alloc.c:905: rc = ocfs2_error(sb,
fs/ocfs2/alloc.c-906- "Extent block #%llu has bad signature %.*s\n",
--
fs/ocfs2/alloc.c-912- if (le64_to_cpu(eb->h_blkno) != bh->b_blocknr) {
fs/ocfs2/alloc.c:913: rc = ocfs2_error(sb,
fs/ocfs2/alloc.c-914- "Extent block #%llu has an invalid h_blkno of %llu\n",
--
fs/ocfs2/alloc.c-920- if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) {
fs/ocfs2/alloc.c:921: rc = ocfs2_error(sb,
fs/ocfs2/alloc.c-922- "Extent block #%llu has an invalid h_fs_generation of #%u\n",
--
fs/ocfs2/alloc.c-928- if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) {
fs/ocfs2/alloc.c:929: rc = ocfs2_error(sb,
fs/ocfs2/alloc.c-930- "Extent block #%llu has invalid l_count %u (expected %u)\n",
--
fs/ocfs2/alloc.c-937- if (le16_to_cpu(eb->h_list.l_next_free_rec) > le16_to_cpu(eb->h_list.l_count)) {
fs/ocfs2/alloc.c:938: rc = ocfs2_error(sb,
fs/ocfs2/alloc.c-939- "Extent block #%llu has invalid l_next_free_rec %u (l_count %u)\n",
--
fs/ocfs2/alloc.c=970=int ocfs2_num_free_extents(struct ocfs2_extent_tree *et)
--
fs/ocfs2/alloc.c-992- if (el->l_tree_depth != 0) {
fs/ocfs2/alloc.c:993: retval = ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-994- "Owner %llu has leaf extent block %llu with an invalid l_tree_depth of %u\n",
--
fs/ocfs2/alloc.c=1477=static int ocfs2_find_branch_target(struct ocfs2_extent_tree *et,
--
fs/ocfs2/alloc.c-1492- if (le16_to_cpu(el->l_next_free_rec) == 0) {
fs/ocfs2/alloc.c:1493: status = ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-1494- "Owner %llu has empty extent list (next_free_rec == 0)\n",
--
fs/ocfs2/alloc.c-1500- if (!blkno) {
fs/ocfs2/alloc.c:1501: status = ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-1502- "Owner %llu has extent list where extent # %d has no physical block start\n",
--
fs/ocfs2/alloc.c=1813=static int __ocfs2_find_path(struct ocfs2_caching_info *ci,
--
fs/ocfs2/alloc.c-1827- if (unlikely(le16_to_cpu(el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH)) {
fs/ocfs2/alloc.c:1828: ocfs2_error(ocfs2_metadata_cache_get_super(ci),
fs/ocfs2/alloc.c-1829- "Owner %llu has invalid tree depth %u in extent list\n",
--
fs/ocfs2/alloc.c-1835- if (!el->l_next_free_rec || !el->l_count) {
fs/ocfs2/alloc.c:1836: ocfs2_error(ocfs2_metadata_cache_get_super(ci),
fs/ocfs2/alloc.c-1837- "Owner %llu has empty extent list at depth %u\n"
--
fs/ocfs2/alloc.c-1861- if (blkno == 0) {
fs/ocfs2/alloc.c:1862: ocfs2_error(ocfs2_metadata_cache_get_super(ci),
fs/ocfs2/alloc.c-1863- "Owner %llu has bad blkno in extent list at depth %u (index %d)\n",
--
fs/ocfs2/alloc.c=2130=static int ocfs2_rotate_subtree_right(handle_t *handle,
--
fs/ocfs2/alloc.c-2146- if (left_el->l_next_free_rec != left_el->l_count) {
fs/ocfs2/alloc.c:2147: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-2148- "Inode %llu has non-full interior leaf node %llu (next free = %u)\n",
--
fs/ocfs2/alloc.c=2233=int ocfs2_find_cpos_for_left_leaf(struct super_block *sb,
--
fs/ocfs2/alloc.c-2286- */
fs/ocfs2/alloc.c:2287: ocfs2_error(sb, "Invalid extent tree at extent block %llu\n",
fs/ocfs2/alloc.c-2288- (unsigned long long)blkno);
--
fs/ocfs2/alloc.c=2828=int ocfs2_find_cpos_for_right_leaf(struct super_block *sb,
--
fs/ocfs2/alloc.c-2882- */
fs/ocfs2/alloc.c:2883: ocfs2_error(sb, "Invalid extent tree at extent block %llu\n",
fs/ocfs2/alloc.c-2884- (unsigned long long)blkno);
--
fs/ocfs2/alloc.c=3168=static int ocfs2_rotate_tree_left(handle_t *handle,
--
fs/ocfs2/alloc.c-3218- if (le16_to_cpu(el->l_next_free_rec) == 0) {
fs/ocfs2/alloc.c:3219: ret = ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-3220- "Owner %llu has empty extent block at %llu\n",
--
fs/ocfs2/alloc.c=3971=static void ocfs2_adjust_rightmost_records(handle_t *handle,
--
fs/ocfs2/alloc.c-3989- if (next_free == 0) {
fs/ocfs2/alloc.c:3990: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-3991- "Owner %llu has a bad extent list\n",
--
fs/ocfs2/alloc.c=4372=static int ocfs2_figure_merge_contig_type(struct ocfs2_extent_tree *et,
--
fs/ocfs2/alloc.c-4413- eb = (struct ocfs2_extent_block *)bh->b_data;
fs/ocfs2/alloc.c:4414: status = ocfs2_error(sb,
fs/ocfs2/alloc.c-4415- "Extent block #%llu has an invalid l_next_free_rec of %d. It should have matched the l_count of %d\n",
--
fs/ocfs2/alloc.c-4467- eb = (struct ocfs2_extent_block *)bh->b_data;
fs/ocfs2/alloc.c:4468: status = ocfs2_error(sb,
fs/ocfs2/alloc.c-4469- "Extent block #%llu has an invalid l_next_free_rec of %d\n",
--
fs/ocfs2/alloc.c=4925=static int ocfs2_split_and_insert(handle_t *handle,
--
fs/ocfs2/alloc.c-5022- if (split_index == -1) {
fs/ocfs2/alloc.c:5023: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-5024- "Owner %llu has an extent at cpos %u which can no longer be found\n",
--
fs/ocfs2/alloc.c=5172=int ocfs2_change_extent_flag(handle_t *handle,
--
fs/ocfs2/alloc.c-5202- if (index == -1) {
fs/ocfs2/alloc.c:5203: ocfs2_error(sb,
fs/ocfs2/alloc.c-5204- "Owner %llu has an extent at cpos %u which can no longer be found\n",
--
fs/ocfs2/alloc.c=5260=int ocfs2_mark_extent_written(struct inode *inode,
--
fs/ocfs2/alloc.c-5272- if (!ocfs2_writes_unwritten_extents(OCFS2_SB(inode->i_sb))) {
fs/ocfs2/alloc.c:5273: ocfs2_error(inode->i_sb, "Inode %llu has unwritten extents that are being written to, but the feature bit is not set in the super block\n",
fs/ocfs2/alloc.c-5274- (unsigned long long)OCFS2_I(inode)->ip_blkno);
--
fs/ocfs2/alloc.c=5528=int ocfs2_remove_extent(handle_t *handle,
--
fs/ocfs2/alloc.c-5561- if (index == -1) {
fs/ocfs2/alloc.c:5562: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-5563- "Owner %llu has an extent at cpos %u which can no longer be found\n",
--
fs/ocfs2/alloc.c-5626- if (index == -1) {
fs/ocfs2/alloc.c:5627: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-5628- "Owner %llu: split at cpos %u lost record\n",
--
fs/ocfs2/alloc.c-5642- if (rec_range != trunc_range) {
fs/ocfs2/alloc.c:5643: ocfs2_error(ocfs2_metadata_cache_get_super(et->et_ci),
fs/ocfs2/alloc.c-5644- "Owner %llu: error after split at cpos %u trunc len %u, existing record is (%u,%u)\n",
--
fs/ocfs2/alloc.c=7241=int ocfs2_commit_truncate(struct ocfs2_super *osb,
--
fs/ocfs2/alloc.c-7310- if (le16_to_cpu(el->l_next_free_rec) == 0) {
fs/ocfs2/alloc.c:7311: ocfs2_error(inode->i_sb,
fs/ocfs2/alloc.c-7312- "Inode %llu has empty extent block at %llu\n",
--
fs/ocfs2/alloc.c=7415=int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh,
--
fs/ocfs2/alloc.c-7436- !ocfs2_supports_inline_data(osb)) {
fs/ocfs2/alloc.c:7437: ocfs2_error(inode->i_sb,
fs/ocfs2/alloc.c-7438- "Inline data flags for inode %llu don't agree! Disk: 0x%x, Memory: 0x%x, Superblock: 0x%x\n",
--
fs/ocfs2/aops.c=213=int ocfs2_read_inline_data(struct inode *inode, struct folio *folio,
--
fs/ocfs2/aops.c-219- if (!(le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL)) {
fs/ocfs2/aops.c:220: ocfs2_error(inode->i_sb, "Inode %llu lost inline data flag\n",
fs/ocfs2/aops.c-221- (unsigned long long)OCFS2_I(inode)->ip_blkno);
--
fs/ocfs2/aops.c-228- size > ocfs2_max_inline_data_with_xattr(inode->i_sb, di)) {
fs/ocfs2/aops.c:229: ocfs2_error(inode->i_sb,
fs/ocfs2/aops.c-230- "Inode %llu has with inline data has bad size: %Lu\n",
--
fs/ocfs2/dir.c=476=static int ocfs2_check_dir_trailer(struct inode *dir, struct buffer_head *bh)
--
fs/ocfs2/dir.c-482- if (!OCFS2_IS_VALID_DIR_TRAILER(trailer)) {
fs/ocfs2/dir.c:483: rc = ocfs2_error(dir->i_sb,
fs/ocfs2/dir.c-484- "Invalid dirblock #%llu: signature = %.*s\n",
--
fs/ocfs2/dir.c-489- if (le64_to_cpu(trailer->db_blkno) != bh->b_blocknr) {
fs/ocfs2/dir.c:490: rc = ocfs2_error(dir->i_sb,
fs/ocfs2/dir.c-491- "Directory block #%llu has an invalid db_blkno of %llu\n",
--
fs/ocfs2/dir.c-497- OCFS2_I(dir)->ip_blkno) {
fs/ocfs2/dir.c:498: rc = ocfs2_error(dir->i_sb,
fs/ocfs2/dir.c-499- "Directory block #%llu on dinode #%llu has an invalid parent_dinode of %llu\n",
--
fs/ocfs2/dir.c=581=static int ocfs2_validate_dx_root(struct super_block *sb,
--
fs/ocfs2/dir.c-599- if (!OCFS2_IS_VALID_DX_ROOT(dx_root)) {
fs/ocfs2/dir.c:600: ret = ocfs2_error(sb,
fs/ocfs2/dir.c-601- "Dir Index Root # %llu has bad signature %.*s\n",
--
fs/ocfs2/dir.c-610- if (le16_to_cpu(el->l_count) != ocfs2_extent_recs_per_dx_root(sb)) {
fs/ocfs2/dir.c:611: ret = ocfs2_error(sb,
fs/ocfs2/dir.c-612- "Dir Index Root # %llu has invalid l_count %u (expected %u)\n",
--
fs/ocfs2/dir.c-619- if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
fs/ocfs2/dir.c:620: ret = ocfs2_error(sb,
fs/ocfs2/dir.c-621- "Dir Index Root # %llu has invalid l_next_free_rec %u (l_count %u)\n",
--
fs/ocfs2/dir.c=650=static int ocfs2_validate_dx_leaf(struct super_block *sb,
--
fs/ocfs2/dir.c-666- if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) {
fs/ocfs2/dir.c:667: ret = ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n",
fs/ocfs2/dir.c-668- 7, dx_leaf->dl_signature);
--
fs/ocfs2/dir.c=806=static int ocfs2_dx_dir_lookup_rec(struct inode *inode,
--
fs/ocfs2/dir.c-829- if (el->l_tree_depth) {
fs/ocfs2/dir.c:830: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/dir.c-831- "Inode %llu has non zero tree depth in btree tree block %llu\n",
--
fs/ocfs2/dir.c-848- if (!found) {
fs/ocfs2/dir.c:849: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/dir.c-850- "Inode %llu has no extent record for hash %u in btree (next_free_rec %u)\n",
--
fs/ocfs2/dir.c=3395=static int ocfs2_find_dir_space_id(struct inode *dir, struct buffer_head *di_bh,
--
fs/ocfs2/dir.c-3453- if (!last_de) {
fs/ocfs2/dir.c:3454: ret = ocfs2_error(sb, "Directory entry (#%llu: size=%lld) "
fs/ocfs2/dir.c-3455- "is unexpectedly short",
--
fs/ocfs2/extent_map.c=274=static int ocfs2_last_eb_is_empty(struct inode *inode,
--
fs/ocfs2/extent_map.c-292- if (el->l_tree_depth) {
fs/ocfs2/extent_map.c:293: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-294- "Inode %llu has non zero tree depth in leaf block %llu\n",
--
fs/ocfs2/extent_map.c=396=static int ocfs2_get_clusters_nocache(struct inode *inode,
--
fs/ocfs2/extent_map.c-428- if (el->l_tree_depth) {
fs/ocfs2/extent_map.c:429: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-430- "Inode %llu has non zero tree depth in leaf block %llu\n",
--
fs/ocfs2/extent_map.c-438- if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
fs/ocfs2/extent_map.c:439: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-440- "Inode %llu has an invalid extent (next_free_rec %u, count %u)\n",
--
fs/ocfs2/extent_map.c-473- if (!rec->e_blkno) {
fs/ocfs2/extent_map.c:474: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-475- "Inode %llu has bad extent record (%u, %u, 0)\n",
--
fs/ocfs2/extent_map.c=540=int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
--
fs/ocfs2/extent_map.c-562- if (el->l_tree_depth) {
fs/ocfs2/extent_map.c:563: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-564- "Inode %llu has non zero tree depth in xattr leaf block %llu\n",
--
fs/ocfs2/extent_map.c-581- if (!rec->e_blkno) {
fs/ocfs2/extent_map.c:582: ocfs2_error(inode->i_sb,
fs/ocfs2/extent_map.c-583- "Inode %llu has bad extent record (%u, %u, 0) in xattr\n",
--
fs/ocfs2/inode.c=1423=int ocfs2_validate_inode_block(struct super_block *sb,
--
fs/ocfs2/inode.c-1459- if (!OCFS2_IS_VALID_DINODE(di)) {
fs/ocfs2/inode.c:1460: rc = ocfs2_error(sb, "Invalid dinode #%llu: signature = %.*s\n",
fs/ocfs2/inode.c-1461- (unsigned long long)bh->b_blocknr, 7,
--
fs/ocfs2/inode.c-1466- if (le64_to_cpu(di->i_blkno) != bh->b_blocknr) {
fs/ocfs2/inode.c:1467: rc = ocfs2_error(sb, "Invalid dinode #%llu: i_blkno is %llu\n",
fs/ocfs2/inode.c-1468- (unsigned long long)bh->b_blocknr,
--
fs/ocfs2/inode.c-1473- if (!(le32_to_cpu(di->i_flags) & OCFS2_VALID_FL)) {
fs/ocfs2/inode.c:1474: rc = ocfs2_error(sb,
fs/ocfs2/inode.c-1475- "Invalid dinode #%llu: OCFS2_VALID_FL not set\n",
--
fs/ocfs2/inode.c-1481- OCFS2_SB(sb)->fs_generation) {
fs/ocfs2/inode.c:1482: rc = ocfs2_error(sb,
fs/ocfs2/inode.c-1483- "Invalid dinode #%llu: fs_generation is %u\n",
--
fs/ocfs2/inode.c-1490- (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) {
fs/ocfs2/inode.c:1491: rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n",
fs/ocfs2/inode.c-1492- (unsigned long long)bh->b_blocknr,
--
fs/ocfs2/inode.c-1500- if (le32_to_cpu(di->i_clusters)) {
fs/ocfs2/inode.c:1501: rc = ocfs2_error(sb,
fs/ocfs2/inode.c-1502- "Invalid dinode %llu: %u clusters\n",
--
fs/ocfs2/inode.c-1509- ocfs2_max_inline_data_with_xattr(sb, di)) {
fs/ocfs2/inode.c:1510: rc = ocfs2_error(sb,
fs/ocfs2/inode.c-1511- "Invalid dinode #%llu: inline data id_count %u exceeds max %d\n",
--
fs/ocfs2/inode.c-1518- if (le64_to_cpu(di->i_size) > le16_to_cpu(data->id_count)) {
fs/ocfs2/inode.c:1519: rc = ocfs2_error(sb,
fs/ocfs2/inode.c-1520- "Invalid dinode #%llu: inline data i_size %llu exceeds id_count %u\n",
--
fs/ocfs2/inode.c-1533- if (le16_to_cpu(cl->cl_count) != ocfs2_chain_recs_per_inode(sb)) {
fs/ocfs2/inode.c:1534: rc = ocfs2_error(sb, "Invalid dinode %llu: chain list count %u\n",
fs/ocfs2/inode.c-1535- (unsigned long long)bh->b_blocknr,
--
fs/ocfs2/inode.c-1539- if (le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
fs/ocfs2/inode.c:1540: rc = ocfs2_error(sb, "Invalid dinode %llu: chain list index %u\n",
fs/ocfs2/inode.c-1541- (unsigned long long)bh->b_blocknr,
--
fs/ocfs2/inode.c-1547- le16_to_cpu(cl->cl_bpc) != bpc) {
fs/ocfs2/inode.c:1548: rc = ocfs2_error(sb, "Invalid dinode %llu: bits per cluster %u\n",
fs/ocfs2/inode.c-1549- (unsigned long long)bh->b_blocknr,
--
fs/ocfs2/inode.c-1556- !di->i_refcount_loc) {
fs/ocfs2/inode.c:1557: rc = ocfs2_error(sb, "Inode #%llu has refcount flag but no i_refcount_loc\n",
fs/ocfs2/inode.c-1558- (unsigned long long)bh->b_blocknr);
--
fs/ocfs2/journal.c=578=static void ocfs2_abort_trigger(struct jbd2_buffer_trigger_type *triggers,
--
fs/ocfs2/journal.c-588-
fs/ocfs2/journal.c:589: ocfs2_error(ot->sb,
fs/ocfs2/journal.c-590- "JBD2 has aborted our journal, ocfs2 cannot continue\n");
--
fs/ocfs2/journal.c=652=static int __ocfs2_journal_access(handle_t *handle,
--
fs/ocfs2/journal.c-688- unlock_buffer(bh);
fs/ocfs2/journal.c:689: return ocfs2_error(osb->sb, "A previous attempt to "
fs/ocfs2/journal.c-690- "write this buffer head failed\n");
--
fs/ocfs2/localalloc.c=615=int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
--
fs/ocfs2/localalloc.c-655- ocfs2_local_alloc_count_bits(alloc)) {
fs/ocfs2/localalloc.c:656: status = ocfs2_error(osb->sb, "local alloc inode %llu says it has %u used bits, but a count shows %u\n",
fs/ocfs2/localalloc.c-657- (unsigned long long)le64_to_cpu(alloc->i_blkno),
--
fs/ocfs2/move_extents.c=49=static int __ocfs2_move_extent(handle_t *handle,
--
fs/ocfs2/move_extents.c-92- if (index == -1) {
fs/ocfs2/move_extents.c:93: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/move_extents.c-94- "Inode %llu has an extent at cpos %u which can no longer be found\n",
--
fs/ocfs2/move_extents.c-101- if (ext_flags != rec->e_flags) {
fs/ocfs2/move_extents.c:102: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/move_extents.c-103- "Inode %llu has corrupted extent %d with flags 0x%x at cpos %u\n",
--
fs/ocfs2/namei.c=1205=static int ocfs2_rename(struct mnt_idmap *idmap,
--
fs/ocfs2/namei.c-1576- if (!is_journal_aborted(osb->journal->j_journal)) {
fs/ocfs2/namei.c:1577: ocfs2_error(osb->sb, "new entry %.*s is added, but old entry %.*s "
fs/ocfs2/namei.c-1578- "is not deleted.",
--
fs/ocfs2/namei.c-1588- if (!is_journal_aborted(osb->journal->j_journal)) {
fs/ocfs2/namei.c:1589: ocfs2_error(osb->sb, "new entry %.*s is added, but old entry %.*s "
fs/ocfs2/namei.c-1590- "is not deleted.",
--
fs/ocfs2/quota_local.c=134=static int ocfs2_read_quota_block(struct inode *inode, u64 v_block,
--
fs/ocfs2/quota_local.c-140- if (i_size_read(inode) >> inode->i_sb->s_blocksize_bits <= v_block)
fs/ocfs2/quota_local.c:141: return ocfs2_error(inode->i_sb,
fs/ocfs2/quota_local.c-142- "Quota file %llu is probably corrupted! Requested to read block %Lu but file has size only %Lu\n",
--
fs/ocfs2/refcounttree.c=72=static int ocfs2_validate_refcount_block(struct super_block *sb,
--
fs/ocfs2/refcounttree.c-96- if (!OCFS2_IS_VALID_REFCOUNT_BLOCK(rb)) {
fs/ocfs2/refcounttree.c:97: rc = ocfs2_error(sb,
fs/ocfs2/refcounttree.c-98- "Refcount block #%llu has bad signature %.*s\n",
--
fs/ocfs2/refcounttree.c-104- if (le64_to_cpu(rb->rf_blkno) != bh->b_blocknr) {
fs/ocfs2/refcounttree.c:105: rc = ocfs2_error(sb,
fs/ocfs2/refcounttree.c-106- "Refcount block #%llu has an invalid rf_blkno of %llu\n",
--
fs/ocfs2/refcounttree.c-112- if (le32_to_cpu(rb->rf_fs_generation) != OCFS2_SB(sb)->fs_generation) {
fs/ocfs2/refcounttree.c:113: rc = ocfs2_error(sb,
fs/ocfs2/refcounttree.c-114- "Refcount block #%llu has an invalid rf_fs_generation of #%u\n",
--
fs/ocfs2/refcounttree.c=1057=static int ocfs2_get_refcount_rec(struct ocfs2_caching_info *ci,
--
fs/ocfs2/refcounttree.c-1095- if (el->l_tree_depth) {
fs/ocfs2/refcounttree.c:1096: ret = ocfs2_error(sb,
fs/ocfs2/refcounttree.c-1097- "refcount tree %llu has non zero tree depth in leaf btree tree block %llu\n",
--
fs/ocfs2/refcounttree.c=2330=static int ocfs2_mark_extent_refcounted(struct inode *inode,
--
fs/ocfs2/refcounttree.c-2342- if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb))) {
fs/ocfs2/refcounttree.c:2343: ret = ocfs2_error(inode->i_sb, "Inode %llu want to use refcount tree, but the feature bit is not set in the super block\n",
fs/ocfs2/refcounttree.c-2344- inode->i_ino);
--
fs/ocfs2/refcounttree.c=2513=int ocfs2_prepare_refcount_change_for_del(struct inode *inode,
--
fs/ocfs2/refcounttree.c-2525- if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb))) {
fs/ocfs2/refcounttree.c:2526: ret = ocfs2_error(inode->i_sb, "Inode %llu want to use refcount tree, but the feature bit is not set in the super block\n",
fs/ocfs2/refcounttree.c-2527- inode->i_ino);
--
fs/ocfs2/refcounttree.c=2621=static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
--
fs/ocfs2/refcounttree.c-2650- if (el->l_tree_depth) {
fs/ocfs2/refcounttree.c:2651: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/refcounttree.c-2652- "Inode %llu has non zero tree depth in leaf block %llu\n",
--
fs/ocfs2/refcounttree.c=3047=static int ocfs2_clear_ext_refcount(handle_t *handle,
--
fs/ocfs2/refcounttree.c-3088- if (index == -1) {
fs/ocfs2/refcounttree.c:3089: ret = ocfs2_error(sb,
fs/ocfs2/refcounttree.c-3090- "Inode %llu has an extent at cpos %u which can no longer be found\n",
--
fs/ocfs2/refcounttree.c=3317=static int ocfs2_replace_cow(struct ocfs2_cow_context *context)
--
fs/ocfs2/refcounttree.c-3326- if (!ocfs2_refcount_tree(osb)) {
fs/ocfs2/refcounttree.c:3327: return ocfs2_error(inode->i_sb, "Inode %llu want to use refcount tree, but the feature bit is not set in the super block\n",
fs/ocfs2/refcounttree.c-3328- inode->i_ino);
--
fs/ocfs2/resize.c=265=int ocfs2_group_extend(struct inode * inode, int new_clusters)
--
fs/ocfs2/resize.c-307- if (!OCFS2_IS_VALID_DINODE(fe)) {
fs/ocfs2/resize.c:308: ret = ocfs2_error(main_bm_inode->i_sb,
fs/ocfs2/resize.c-309- "Invalid dinode #%llu\n",
--
fs/ocfs2/slot_map.c=339=static int ocfs2_validate_slot_map_block(struct super_block *sb,
--
fs/ocfs2/slot_map.c-346- if (bh->b_blocknr < OCFS2_SUPER_BLOCK_BLKNO) {
fs/ocfs2/slot_map.c:347: rc = ocfs2_error(sb,
fs/ocfs2/slot_map.c-348- "Invalid Slot Map Buffer Head "
--
fs/ocfs2/suballoc.c=155=do { \
--
fs/ocfs2/suballoc.c-158- else \
fs/ocfs2/suballoc.c:159: return ocfs2_error(sb, fmt, ##__VA_ARGS__); \
fs/ocfs2/suballoc.c-160-} while (0)
--
fs/ocfs2/suballoc.c=438=static int ocfs2_block_group_fill(handle_t *handle,
--
fs/ocfs2/suballoc.c-451- if (((unsigned long long) bg_bh->b_blocknr) != group_blkno) {
fs/ocfs2/suballoc.c:452: status = ocfs2_error(alloc_inode->i_sb,
fs/ocfs2/suballoc.c-453- "group block (%llu) != b_blocknr (%llu)\n",
--
fs/ocfs2/suballoc.c=862=static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb,
--
fs/ocfs2/suballoc.c-902- if (!(fe->i_flags & cpu_to_le32(OCFS2_CHAIN_FL))) {
fs/ocfs2/suballoc.c:903: status = ocfs2_error(alloc_inode->i_sb,
fs/ocfs2/suballoc.c-904- "Invalid chain allocator %llu\n",
--
fs/ocfs2/suballoc.c=1457=int ocfs2_block_group_set_bits(handle_t *handle,
--
fs/ocfs2/suballoc.c-1493- if (le16_to_cpu(bg->bg_free_bits_count) > le16_to_cpu(bg->bg_bits)) {
fs/ocfs2/suballoc.c:1494: return ocfs2_error(alloc_inode->i_sb, "Group descriptor # %llu has bit count %u but claims %u are freed. num_bits %d\n",
fs/ocfs2/suballoc.c-1495- (unsigned long long)le64_to_cpu(bg->bg_blkno),
--
fs/ocfs2/suballoc.c=2041=static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
--
fs/ocfs2/suballoc.c-2066- le32_to_cpu(fe->id1.bitmap1.i_total)) {
fs/ocfs2/suballoc.c:2067: status = ocfs2_error(ac->ac_inode->i_sb,
fs/ocfs2/suballoc.c-2068- "Chain allocator dinode %llu has %u used bits but only %u total\n",
--
fs/ocfs2/suballoc.c-2099- le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
fs/ocfs2/suballoc.c:2100: status = ocfs2_error(ac->ac_inode->i_sb,
fs/ocfs2/suballoc.c-2101- "Chain allocator dinode %llu has invalid next "
--
fs/ocfs2/suballoc.c=2581=static int ocfs2_block_group_clear_bits(handle_t *handle,
--
fs/ocfs2/suballoc.c-2632- spin_unlock(&jh->b_state_lock);
fs/ocfs2/suballoc.c:2633: return ocfs2_error(alloc_inode->i_sb, "Group descriptor # %llu has bit count %u but claims %u are freed. num_bits %d\n",
fs/ocfs2/suballoc.c-2634- (unsigned long long)le64_to_cpu(bg->bg_blkno),
--
fs/ocfs2/suballoc.c=2674=static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle,
--
fs/ocfs2/suballoc.c-2804- if ((count + start_bit) > le16_to_cpu(group->bg_bits)) {
fs/ocfs2/suballoc.c:2805: ocfs2_error(alloc_inode->i_sb,
fs/ocfs2/suballoc.c-2806- "reclaim length (%d) beyands block group length (%d)",
--
fs/ocfs2/suballoc.h=182=u64 ocfs2_which_cluster_group(struct inode *inode, u32 cluster);
--
fs/ocfs2/suballoc.h-184-/*
fs/ocfs2/suballoc.h:185: * By default, ocfs2_read_group_descriptor() calls ocfs2_error() when it
fs/ocfs2/suballoc.h-186- * finds a problem. A caller that wants to check a group descriptor
--
fs/ocfs2/super.c=2473=static int ocfs2_handle_error(struct super_block *sb)
--
fs/ocfs2/super.c-2500-
fs/ocfs2/super.c:2501:int __ocfs2_error(struct super_block *sb, const char *function,
fs/ocfs2/super.c-2502- const char *fmt, ...)
--
fs/ocfs2/super.h=13=__printf(3, 4)
fs/ocfs2/super.h:14:int __ocfs2_error(struct super_block *sb, const char *function,
fs/ocfs2/super.h-15- const char *fmt, ...);
fs/ocfs2/super.h-16-
fs/ocfs2/super.h:17:#define ocfs2_error(sb, fmt, ...) \
fs/ocfs2/super.h:18: __ocfs2_error(sb, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__)
fs/ocfs2/super.h-19-
--
fs/ocfs2/xattr.c=467=static int ocfs2_validate_xattr_block(struct super_block *sb,
--
fs/ocfs2/xattr.c-491- if (!OCFS2_IS_VALID_XATTR_BLOCK(xb)) {
fs/ocfs2/xattr.c:492: return ocfs2_error(sb,
fs/ocfs2/xattr.c-493- "Extended attribute block #%llu has bad signature %.*s\n",
--
fs/ocfs2/xattr.c-498- if (le64_to_cpu(xb->xb_blkno) != bh->b_blocknr) {
fs/ocfs2/xattr.c:499: return ocfs2_error(sb,
fs/ocfs2/xattr.c-500- "Extended attribute block #%llu has an invalid xb_blkno of %llu\n",
--
fs/ocfs2/xattr.c-505- if (le32_to_cpu(xb->xb_fs_generation) != OCFS2_SB(sb)->fs_generation) {
fs/ocfs2/xattr.c:506: return ocfs2_error(sb,
fs/ocfs2/xattr.c-507- "Extended attribute block #%llu has an invalid xb_fs_generation of #%u\n",
--
fs/ocfs2/xattr.c=970=static int ocfs2_xattr_ibody_list(struct inode *inode,
--
fs/ocfs2/xattr.c-989- inline_size < sizeof(struct ocfs2_xattr_header)) {
fs/ocfs2/xattr.c:990: ocfs2_error(inode->i_sb,
fs/ocfs2/xattr.c-991- "Invalid xattr inline size %u in inode %llu\n",
--
fs/ocfs2/xattr.c-1004- if (xattr_count > max_entries) {
fs/ocfs2/xattr.c:1005: ocfs2_error(inode->i_sb,
fs/ocfs2/xattr.c-1006- "xattr entry count %u exceeds maximum %zu in inode %llu\n",
--
fs/ocfs2/xattr.c=1095=static int ocfs2_xattr_find_entry(struct inode *inode, int name_index,
--
fs/ocfs2/xattr.c-1109- if ((void *)entry >= xs->end) {
fs/ocfs2/xattr.c:1110: ocfs2_error(inode->i_sb, "corrupted xattr entries");
fs/ocfs2/xattr.c-1111- return -EFSCORRUPTED;
--
fs/ocfs2/xattr.c-1118- if ((xs->base + name_offset + name_len) > xs->end) {
fs/ocfs2/xattr.c:1119: ocfs2_error(inode->i_sb,
fs/ocfs2/xattr.c-1120- "corrupted xattr entries");
--
fs/ocfs2/xattr.c=3718=static int ocfs2_xattr_get_rec(struct inode *inode,
--
fs/ocfs2/xattr.c-3742- if (el->l_tree_depth) {
fs/ocfs2/xattr.c:3743: ret = ocfs2_error(inode->i_sb,
fs/ocfs2/xattr.c-3744- "Inode %llu has non zero tree depth in xattr tree block %llu\n",
--
fs/ocfs2/xattr.c-3760- if (!e_blkno) {
fs/ocfs2/xattr.c:3761: ret = ocfs2_error(inode->i_sb, "Inode %llu has bad extent record (%u, %u, 0) in xattr\n",
fs/ocfs2/xattr.c-3762- inode->i_ino,
]
|
| 50/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 63405
output: 44
thoughts: 24
Thoughts: Here's a summary of my thought process:
**Diving into the OCFS2 Error Handling**
Okay, I need to get to the bottom of this. My first instinct is to zero in on the error reporting within the OCFS2 filesystem. Let's see... the most likely place to start looking for a core error function would be somewhere in the superblock handling code. I'm going to navigate directly to the `fs/ocfs2/super.c` file. Specifically, I'm going to examine the `__ocfs2_error` function. That name is pretty telling, and it seems like a reasonable place to begin my investigation into how errors are signaled and handled within this particular part of the filesystem. I'm hoping I can glean some insights there.
|
| 51/2 |
2026/05/22 01:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:__ocfs2_error]
Results: map[SourceCode:2501: int __ocfs2_error(struct super_block *sb, const char *function,
2502: const char *fmt, ...)
2503: {
2504: struct va_format vaf;
2505: va_list args;
2506:
2507: va_start(args, fmt);
2508: vaf.fmt = fmt;
2509: vaf.va = &args;
2510:
2511: /* Not using mlog here because we want to show the actual
2512: * function the error came from. */
2513: printk(KERN_CRIT "OCFS2: ERROR (device %s): %s: %pV",
2514: sb->s_id, function, &vaf);
2515:
2516: va_end(args);
2517:
2518: return ocfs2_handle_error(sb);
2519: }
SourceFile:fs/ocfs2/super.c]
|
| 52/2 |
2026/05/22 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 63943
output: 45
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `ocfs2_handle_error`**
Alright, let's get down to brass tacks. My initial thought process is centered on troubleshooting. I see this call and my mind immediately jumps to `ocfs2_handle_error`. Given my experience with OCFS2, I understand this is a critical function, potentially the root of all evil when things go south. I'm going to start by confirming whether this is truly a "handle error", which is usually used when something has gone wrong during some operation that OCFS2 is responsible for handling. This is where I start peeling back the layers - the logging, context, and error codes are all going to become relevant here. I'll need to know: what's calling it? What's the context? What is the error code? Is it related to a specific part of the distributed filesystem like the journal, metadata, or data blocks? Understanding the location of the error is the next piece of this puzzle. I'll be looking for any clues that point toward potential corruption, resource exhaustion, or, possibly, even a lock contention issue.
|
| 53/2 |
2026/05/22 01:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_handle_error]
Results: map[SourceCode:2467: /* Depending on the mount option passed, perform one of the following:
2468: * Put OCFS2 into a readonly state (default)
2469: * Return EIO so that only the process errs
2470: * Fix the error as if fsck.ocfs2 -y
2471: * panic
2472: */
2473: static int ocfs2_handle_error(struct super_block *sb)
2474: {
2475: struct ocfs2_super *osb = OCFS2_SB(sb);
2476: int rv = 0;
2477:
2478: ocfs2_set_osb_flag(osb, OCFS2_OSB_ERROR_FS);
2479: pr_crit("On-disk corruption discovered. "
2480: "Please run fsck.ocfs2 once the filesystem is unmounted.\n");
2481:
2482: if (osb->s_mount_opt & OCFS2_MOUNT_ERRORS_PANIC) {
2483: panic("OCFS2: (device %s): panic forced after error\n",
2484: sb->s_id);
2485: } else if (osb->s_mount_opt & OCFS2_MOUNT_ERRORS_CONT) {
2486: pr_crit("OCFS2: Returning error to the calling process.\n");
2487: rv = -EIO;
2488: } else { /* default option */
2489: rv = -EROFS;
2490: if (sb_rdonly(sb) && ocfs2_emergency_state(osb))
2491: return rv;
2492:
2493: pr_crit("OCFS2: File system is now read-only.\n");
2494: sb->s_flags |= SB_RDONLY;
2495: ocfs2_set_ro_flag(osb, 0);
2496: }
2497:
2498: return rv;
2499: }
SourceFile:fs/ocfs2/super.c]
|
| 54/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 64859
output: 47
thoughts: 49
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in OCFS2 internals:
**Checking the Read-Only Flag**
Right, `ocfs2_set_ro_flag(osb, 0)` is the key. That's how we manipulate the read-only state. Ah-ha, setting it to 0 specifically is how we clear the `OCFS2_OSB_HARD_RO` flag. So, it's removing the hard read-only protection.
Let's quickly verify that the function behaves as expected, so I need to go back and check the definition and operation of `ocfs2_set_ro_flag`. Gotta be precise on these things.
|
| 55/2 |
2026/05/22 01:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_set_ro_flag]
Results: map[SourceCode: 649: static inline void ocfs2_set_ro_flag(struct ocfs2_super *osb,
650: int hard)
651: {
652: spin_lock(&osb->osb_lock);
653: osb->osb_flags &= ~(OCFS2_OSB_SOFT_RO|OCFS2_OSB_HARD_RO);
654: if (hard)
655: osb->osb_flags |= OCFS2_OSB_HARD_RO;
656: else
657: osb->osb_flags |= OCFS2_OSB_SOFT_RO;
658: spin_unlock(&osb->osb_lock);
659: }
SourceFile:fs/ocfs2/ocfs2.h]
|
| 56/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 65352
output: 47
thoughts: 490
Thoughts: **The Read-Only Quandary: A Critical Vulnerability Uncovered**
Okay, so initially I was thinking that `ocfs2_set_ro_flag(osb, 0)` would set the *hard* read-only flag, but it's actually setting the *soft* read-only flag (`OCFS2_OSB_SOFT_RO`). This means `ocfs2_is_hard_readonly` is going to return 0. Consequently, `ocfs2_start_trans` *won't* return `-EROFS` as I initially expected. That's the first oversight.
Now, `ocfs2_start_trans` *does* check `ocfs2_is_hard_readonly(osb)`. But, and this is crucial, `ocfs2_handle_error` sets the `OCFS2_OSB_SOFT_RO` flag. This means `ocfs2_start_trans` will succeed, right? Yes, it will. Then, `ocfs2_write_begin_nolock` will proceed, and `ocfs2_try_to_write_inline_data` will also continue its execution path. Both `ocfs2_write_begin_inline` and `ocfs2_write_end_inline` will be called. That's not good.
And from there, `memcpy_from_folio` will get called with `pos` values reaching up to 65535, and here's the kicker: `memcpy_from_folio` will write *out of bounds* of the buffer head data! That's a major memory corruption vulnerability.
Wait, why does `ocfs2_try_to_write_inline_data` use `ocfs2_size_fits_inline_data`, which reads `id_count` from disk? Because `id_count` supposedly defines the maximum allowed size of the inline data. But, isn't `id_count` validated in `ocfs2_validate_inode_block`? Yes, and if it fails, it returns an error and the inode block is not marked as up-to-date, or the read fails. If `ocfs2_validate_inode_block` fails, how the heck did `ocfs2_write_begin_nolock` even get the buffer head?
`ocfs2_write_begin_nolock` is passed `di_bh` from `ocfs2_write_begin`. `ocfs2_write_begin` obtains `di_bh` from `ocfs2_inode_lock`. I need to review `ocfs2_inode_lock` more closely to be sure how this is possible.
|
| 57/2 |
2026/05/22 01:47 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_inode_lock]
Results: map[SourceCode: SourceFile:]
|
| 58/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 66496
output: 22
thoughts: 42
Thoughts: Okay, here's my take on those thoughts, framed from my perspective, as if I'm working through it:
**Digging into the `ocfs2_inode_lock` Mystery**
Alright, hold on a second. I need to get this straight in my head. I'm working with OCFS2, so I'm trying to figure out where the `ocfs2_inode_lock` function actually *lives*. Was it in `dlmglue.c`? Or, hmm, did it end up residing within `inode.c`? My memory isn't perfect, especially when I'm chasing down a specific detail like this.
Okay, no problem. Time to be efficient. Let's fire up a `grep` and let the system do the heavy lifting for a second, I can quickly pin it down that way. I'll search the source for `ocfs2_inode_lock`. That should quickly reveal where the darn thing is defined and, more importantly, where I can find the full context of its usage. That'll let me move forward with a bit more confidence.
|
| 59/2 |
2026/05/22 01:47 |
tool |
grepper |
0m
Args: map[Expression:int ocfs2_inode_lock]
Results: map[Output:fs/ocfs2/dlmglue.c=368=static void ocfs2_downconvert_on_unlock(struct ocfs2_super *osb,
fs/ocfs2/dlmglue.c-369- struct ocfs2_lock_res *lockres);
fs/ocfs2/dlmglue.c:370:static int ocfs2_inode_lock_update(struct inode *inode,
fs/ocfs2/dlmglue.c-371- struct buffer_head **bh);
--
fs/ocfs2/dlmglue.c=2300=static inline void ocfs2_complete_lock_res_refresh(struct ocfs2_lock_res *lockres,
--
fs/ocfs2/dlmglue.c-2314-/* may or may not return a bh if it went to disk. */
fs/ocfs2/dlmglue.c:2315:static int ocfs2_inode_lock_update(struct inode *inode,
fs/ocfs2/dlmglue.c-2316- struct buffer_head **bh)
--
fs/ocfs2/dlmglue.c=2400=static int ocfs2_assign_bh(struct inode *inode,
--
fs/ocfs2/dlmglue.c-2425- */
fs/ocfs2/dlmglue.c:2426:int ocfs2_inode_lock_full_nested(struct inode *inode,
fs/ocfs2/dlmglue.c-2427- struct buffer_head **ret_bh,
--
fs/ocfs2/dlmglue.c-2546- */
fs/ocfs2/dlmglue.c:2547:int ocfs2_inode_lock_with_folio(struct inode *inode,
fs/ocfs2/dlmglue.c-2548- struct buffer_head **ret_bh, int ex, struct folio *folio)
--
fs/ocfs2/dlmglue.c-2569-
fs/ocfs2/dlmglue.c:2570:int ocfs2_inode_lock_atime(struct inode *inode,
fs/ocfs2/dlmglue.c-2571- struct vfsmount *vfsmnt,
--
fs/ocfs2/dlmglue.c=2615=void ocfs2_inode_unlock(struct inode *inode,
--
fs/ocfs2/dlmglue.c-2671- */
fs/ocfs2/dlmglue.c:2672:int ocfs2_inode_lock_tracker(struct inode *inode,
fs/ocfs2/dlmglue.c-2673- struct buffer_head **ret_bh,
--
fs/ocfs2/dlmglue.h=131=void ocfs2_open_unlock(struct inode *inode);
fs/ocfs2/dlmglue.h:132:int ocfs2_inode_lock_atime(struct inode *inode,
fs/ocfs2/dlmglue.h-133- struct vfsmount *vfsmnt,
fs/ocfs2/dlmglue.h-134- int *level, int wait);
fs/ocfs2/dlmglue.h:135:int ocfs2_inode_lock_full_nested(struct inode *inode,
fs/ocfs2/dlmglue.h-136- struct buffer_head **ret_bh,
--
fs/ocfs2/dlmglue.h-139- int subclass);
fs/ocfs2/dlmglue.h:140:int ocfs2_inode_lock_with_folio(struct inode *inode,
fs/ocfs2/dlmglue.h-141- struct buffer_head **ret_bh, int ex, struct folio *folio);
--
fs/ocfs2/dlmglue.h=195=void ocfs2_set_locking_protocol(void);
--
fs/ocfs2/dlmglue.h-197-/* The _tracker pair is used to avoid cluster recursive locking */
fs/ocfs2/dlmglue.h:198:int ocfs2_inode_lock_tracker(struct inode *inode,
fs/ocfs2/dlmglue.h-199- struct buffer_head **ret_bh,
--
fs/ocfs2/file.c=2188=static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos)
--
fs/ocfs2/file.c-2197-
fs/ocfs2/file.c:2198:static int ocfs2_inode_lock_for_extent_tree(struct inode *inode,
fs/ocfs2/file.c-2199- struct buffer_head **di_bh,
]
|
| 60/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 68029
output: 23
thoughts: 16
Thoughts: Okay, here's my attempt at summarizing those thoughts from an expert's perspective, using a first-person perspective:
**Deep Dive into `ocfs2_inode_lock`**
Alright, let's see... `ocfs2_inode_lock`. Immediately, my brain kicks in, trying to pinpoint its exact nature within the OCFS2 codebase. Is this a macro, or an inline function? That's the first question. Understanding its implementation will dictate how it behaves in terms of performance and compile-time considerations. A macro, being textually replaced, might be faster in certain scenarios, but can also lead to code bloat if used excessively or if the arguments are complex. An inline function could offer better type checking and control, but still incur some overhead. The details, as always, are in the implementation, and I need to determine the scope of this implementation to understand the scope and performance. Gotta look under the hood to see how it's actually defined and used in the broader OCFS2 context.
|
| 61/2 |
2026/05/22 01:47 |
tool |
grepper |
0m
Args: map[Expression:#define ocfs2_inode_lock]
Results: map[Output:fs/ocfs2/dlmglue.h=140=int ocfs2_inode_lock_with_folio(struct inode *inode,
--
fs/ocfs2/dlmglue.h-142-/* Variants without special locking class or flags */
fs/ocfs2/dlmglue.h:143:#define ocfs2_inode_lock_full(i, r, e, f)\
fs/ocfs2/dlmglue.h-144- ocfs2_inode_lock_full_nested(i, r, e, f, OI_LS_NORMAL)
fs/ocfs2/dlmglue.h:145:#define ocfs2_inode_lock_nested(i, b, e, s)\
fs/ocfs2/dlmglue.h-146- ocfs2_inode_lock_full_nested(i, b, e, 0, s)
--
fs/ocfs2/dlmglue.h-148- * those are for very specific cases only. */
fs/ocfs2/dlmglue.h:149:#define ocfs2_inode_lock(i, b, e) ocfs2_inode_lock_full_nested(i, b, e, 0, OI_LS_NORMAL)
fs/ocfs2/dlmglue.h-150-#define ocfs2_try_inode_lock(i, b, e)\
]
|
| 62/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 68638
output: 51
thoughts: 74
Thoughts: **Diving into the OCFS2 Inode Locking Mechanism**
Okay, so I'm looking at this `ocfs2_inode_lock` macro, and it's immediately apparent it's just a shortcut. It's defined as a wrapper around `ocfs2_inode_lock_full_nested`. That's the real meat of the operation. Now, let's drill down into `ocfs2_inode_lock_full_nested` to see what's actually happening under the hood. Time to see what that 0, and `OI_LS_NORMAL` actually mean in the arguments.
|
| 63/2 |
2026/05/22 01:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_inode_lock_full_nested]
Results: map[SourceCode:2422: /*
2423: * returns < 0 error if the callback will never be called, otherwise
2424: * the result of the lock will be communicated via the callback.
2425: */
2426: int ocfs2_inode_lock_full_nested(struct inode *inode,
2427: struct buffer_head **ret_bh,
2428: int ex,
2429: int arg_flags,
2430: int subclass)
2431: {
2432: int status, level, acquired;
2433: u32 dlm_flags;
2434: struct ocfs2_lock_res *lockres = NULL;
2435: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
2436: struct buffer_head *local_bh = NULL;
2437:
2438: mlog(0, "inode %llu, take %s META lock\n",
2439: (unsigned long long)OCFS2_I(inode)->ip_blkno,
2440: ex ? "EXMODE" : "PRMODE");
2441:
2442: status = 0;
2443: acquired = 0;
2444: /* We'll allow faking a readonly metadata lock for
2445: * rodevices. */
2446: if (ocfs2_is_hard_readonly(osb)) {
2447: if (ex)
2448: status = -EROFS;
2449: goto getbh;
2450: }
2451:
2452: if ((arg_flags & OCFS2_META_LOCK_GETBH) ||
2453: ocfs2_mount_local(osb))
2454: goto update;
2455:
2456: if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
2457: ocfs2_wait_for_recovery(osb);
2458:
2459: lockres = &OCFS2_I(inode)->ip_inode_lockres;
2460: level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
2461: dlm_flags = 0;
2462: if (arg_flags & OCFS2_META_LOCK_NOQUEUE)
2463: dlm_flags |= DLM_LKF_NOQUEUE;
2464:
2465: status = __ocfs2_cluster_lock(osb, lockres, level, dlm_flags,
2466: arg_flags, subclass, _RET_IP_);
2467: if (status < 0) {
2468: if (status != -EAGAIN)
2469: mlog_errno(status);
2470: goto bail;
2471: }
2472:
2473: /* Notify the error cleanup path to drop the cluster lock. */
2474: acquired = 1;
2475:
2476: /* We wait twice because a node may have died while we were in
2477: * the lower dlm layers. The second time though, we've
2478: * committed to owning this lock so we don't allow signals to
2479: * abort the operation. */
2480: if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
2481: ocfs2_wait_for_recovery(osb);
2482:
2483: update:
2484: /*
2485: * We only see this flag if we're being called from
2486: * ocfs2_read_locked_inode(). It means we're locking an inode
2487: * which hasn't been populated yet, so clear the refresh flag
2488: * and let the caller handle it.
2489: */
2490: if (inode_state_read_once(inode) & I_NEW) {
2491: status = 0;
2492: if (lockres)
2493: ocfs2_complete_lock_res_refresh(lockres, 0);
2494: goto bail;
2495: }
2496:
2497: /* This is fun. The caller may want a bh back, or it may
2498: * not. ocfs2_inode_lock_update definitely wants one in, but
2499: * may or may not read one, depending on what's in the
2500: * LVB. The result of all of this is that we've *only* gone to
2501: * disk if we have to, so the complexity is worthwhile. */
2502: status = ocfs2_inode_lock_update(inode, &local_bh);
2503: if (status < 0) {
2504: if (status != -ENOENT)
2505: mlog_errno(status);
2506: goto bail;
2507: }
2508: getbh:
2509: if (ret_bh) {
2510: status = ocfs2_assign_bh(inode, ret_bh, local_bh);
2511: if (status < 0) {
2512: mlog_errno(status);
2513: goto bail;
2514: }
2515: }
2516:
2517: bail:
2518: if (status < 0) {
2519: if (ret_bh && (*ret_bh)) {
2520: brelse(*ret_bh);
2521: *ret_bh = NULL;
2522: }
2523: if (acquired)
2524: ocfs2_inode_unlock(inode, ex);
2525: }
2526:
2527: brelse(local_bh);
2528: return status;
2529: }
SourceFile:fs/ocfs2/dlmglue.c]
|
| 64/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 70585
output: 49
thoughts: 43
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm well-versed in OCFS2 internals:
**Diving Deep into Inode Locking**
Right, `ocfs2_inode_lock_update`... that's where things get interesting. I remember that function. Now, a key part of how it works is its interaction with the underlying block device, and specifically, how it *reads* the inode's block. Yep, I see the call β it uses `ocfs2_read_inode_block_full`. That's the function to look at. Now, what's its purpose? Let's refresh my memory on what `ocfs2_inode_lock_update` does and what's dependent on it. So `ocfs2_read_inode_block_full` is called from `ocfs2_inode_lock_update`. Now I can assess the details of the update and where that's called, so I know where to look.
|
| 65/2 |
2026/05/22 01:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_inode_lock_update]
Results: map[SourceCode:2314: /* may or may not return a bh if it went to disk. */
2315: static int ocfs2_inode_lock_update(struct inode *inode,
2316: struct buffer_head **bh)
2317: {
2318: int status = 0;
2319: struct ocfs2_inode_info *oi = OCFS2_I(inode);
2320: struct ocfs2_lock_res *lockres = &oi->ip_inode_lockres;
2321: struct ocfs2_dinode *fe;
2322: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
2323:
2324: if (ocfs2_mount_local(osb))
2325: goto bail;
2326:
2327: spin_lock(&oi->ip_lock);
2328: if (oi->ip_flags & OCFS2_INODE_DELETED) {
2329: mlog(0, "Orphaned inode %llu was deleted while we "
2330: "were waiting on a lock. ip_flags = 0x%x\n",
2331: (unsigned long long)oi->ip_blkno, oi->ip_flags);
2332: spin_unlock(&oi->ip_lock);
2333: status = -ENOENT;
2334: goto bail;
2335: }
2336: spin_unlock(&oi->ip_lock);
2337:
2338: if (!ocfs2_should_refresh_lock_res(lockres))
2339: goto bail;
2340:
2341: /* This will discard any caching information we might have had
2342: * for the inode metadata. */
2343: ocfs2_metadata_cache_purge(INODE_CACHE(inode));
2344:
2345: ocfs2_extent_map_trunc(inode, 0);
2346:
2347: if (ocfs2_meta_lvb_is_trustable(inode, lockres)) {
2348: mlog(0, "Trusting LVB on inode %llu\n",
2349: (unsigned long long)oi->ip_blkno);
2350: status = ocfs2_refresh_inode_from_lvb(inode);
2351: goto bail_refresh;
2352: } else {
2353: /* Boo, we have to go to disk. */
2354: /* read bh, cast, ocfs2_refresh_inode */
2355: status = ocfs2_read_inode_block(inode, bh);
2356: if (status < 0) {
2357: mlog_errno(status);
2358: goto bail_refresh;
2359: }
2360: fe = (struct ocfs2_dinode *) (*bh)->b_data;
2361: if (inode_wrong_type(inode, le16_to_cpu(fe->i_mode))) {
2362: status = -ESTALE;
2363: goto bail_refresh;
2364: }
2365:
2366: /* This is a good chance to make sure we're not
2367: * locking an invalid object. ocfs2_read_inode_block()
2368: * already checked that the inode block is sane.
2369: *
2370: * We bug on a stale inode here because we checked
2371: * above whether it was wiped from disk. The wiping
2372: * node provides a guarantee that we receive that
2373: * message and can mark the inode before dropping any
2374: * locks associated with it. */
2375: mlog_bug_on_msg(inode->i_generation !=
2376: le32_to_cpu(fe->i_generation),
2377: "Invalid dinode %llu disk generation: %u "
2378: "inode->i_generation: %u\n",
2379: (unsigned long long)oi->ip_blkno,
2380: le32_to_cpu(fe->i_generation),
2381: inode->i_generation);
2382: mlog_bug_on_msg(le64_to_cpu(fe->i_dtime) ||
2383: !(fe->i_flags & cpu_to_le32(OCFS2_VALID_FL)),
2384: "Stale dinode %llu dtime: %llu flags: 0x%x\n",
2385: (unsigned long long)oi->ip_blkno,
2386: (unsigned long long)le64_to_cpu(fe->i_dtime),
2387: le32_to_cpu(fe->i_flags));
2388:
2389: ocfs2_refresh_inode(inode, fe);
2390: ocfs2_track_lock_refresh(lockres);
2391: }
2392:
2393: status = 0;
2394: bail_refresh:
2395: ocfs2_complete_lock_res_refresh(lockres, status);
2396: bail:
2397: return status;
2398: }
SourceFile:fs/ocfs2/dlmglue.c]
|
| 66/2 |
2026/05/22 01:47 |
llm |
expert |
0m
Model:
Tokens:
input: 72392
output: 47
thoughts: 238
Thoughts: **Analyzing the Error Flow in `ocfs2_write_begin`**
Okay, let's break this down. I'm seeing that `ocfs2_inode_lock_update` returns a `status`. If `ocfs2_read_inode_block` fails, it's pretty clear β it returns the error code, like `-EFSCORRUPTED` or `-EROFS`. Then `ocfs2_inode_lock_full_nested` propagates that error, and ultimately, `ocfs2_write_begin` should, too. But here's the rub: if `ocfs2_write_begin` *does* return an error, how the heck does the code get to `ocfs2_write_end`? That's a critical path question.
Aha! Now I see it. The key is in the success path! `ocfs2_write_begin` is called, and *it succeeds*, even when there's a potential for an error. Why? Because the `ocfs2_inode_lock_update` function didn't actually *read* from disk! It was able to avoid the potentially error-inducing read because it either used the LVB (that's when `ocfs2_meta_lvb_is_trustable` was true) or maybe when the condition `ocfs2_should_refresh_lock_res` was false. It depends, really, on the locking state.
Consequently, if the inode update didn't involve a disk read, the `local_bh` will be NULL. And then, we get to `ocfs2_assign_bh`. That's where the block header is assigned, and likely where the actual read happens if it hasn't already.
|
| 67/2 |
2026/05/22 01:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_assign_bh]
Results: map[SourceCode:2400: static int ocfs2_assign_bh(struct inode *inode,
2401: struct buffer_head **ret_bh,
2402: struct buffer_head *passed_bh)
2403: {
2404: int status;
2405:
2406: if (passed_bh) {
2407: /* Ok, the update went to disk for us, use the
2408: * returned bh. */
2409: *ret_bh = passed_bh;
2410: get_bh(*ret_bh);
2411:
2412: return 0;
2413: }
2414:
2415: status = ocfs2_read_inode_block(inode, ret_bh);
2416: if (status < 0)
2417: mlog_errno(status);
2418:
2419: return status;
2420: }
SourceFile:fs/ocfs2/dlmglue.c]
|
| 68/2 |
2026/05/22 01:48 |
llm |
expert |
0m
Model:
Tokens:
input: 73358
output: 40
thoughts: 200
Thoughts: **Analyzing the Data Block Assignment Process**
Okay, so I'm tracing through this code, and it's becoming clear how this data block assignment works, specifically when a buffer head isn't initially provided. It starts with `ocfs2_assign_bh`. I see that when `passed_bh` is NULL, it calls `ocfs2_read_inode_block`. This is the first critical step. Now, `ocfs2_read_inode_block` in turn calls `ocfs2_read_inode_block_full`, which makes me check what `ocfs2_validate_inode_block` is doing in the next level. Right, so if that validation *fails*, `ocfs2_read_inode_block` is going to return an error. That means the error propagates up the chain β `ocfs2_assign_bh` returns an error, then `ocfs2_inode_lock_full_nested` does the same, and finally, `ocfs2_write_begin` will fail.
The important thing is how the error handling works. If `ocfs2_write_begin` errors out, I realized that `generic_perform_write` *won't* call `ocfs2_write_end`. That's a crucial detail for ensuring data integrity and proper resource management. I need to make sure I understand the logic flow here. Therefore I should move to checking what happens in `generic_perform_write`.
|
| 69/2 |
2026/05/22 01:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:mm/filemap.c IncludeLines:true Name:generic_perform_write]
Results: map[SourceCode:4297: ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i)
4298: {
4299: struct file *file = iocb->ki_filp;
4300: loff_t pos = iocb->ki_pos;
4301: struct address_space *mapping = file->f_mapping;
4302: const struct address_space_operations *a_ops = mapping->a_ops;
4303: size_t chunk = mapping_max_folio_size(mapping);
4304: long status = 0;
4305: ssize_t written = 0;
4306:
4307: do {
4308: struct folio *folio;
4309: size_t offset; /* Offset into folio */
4310: size_t bytes; /* Bytes to write to folio */
4311: size_t copied; /* Bytes copied from user */
4312: void *fsdata = NULL;
4313:
4314: bytes = iov_iter_count(i);
4315: retry:
4316: offset = pos & (chunk - 1);
4317: bytes = min(chunk - offset, bytes);
4318: balance_dirty_pages_ratelimited(mapping);
4319:
4320: if (fatal_signal_pending(current)) {
4321: status = -EINTR;
4322: break;
4323: }
4324:
4325: status = a_ops->write_begin(iocb, mapping, pos, bytes,
4326: &folio, &fsdata);
4327: if (unlikely(status < 0))
4328: break;
4329:
4330: offset = offset_in_folio(folio, pos);
4331: if (bytes > folio_size(folio) - offset)
4332: bytes = folio_size(folio) - offset;
4333:
4334: if (mapping_writably_mapped(mapping))
4335: flush_dcache_folio(folio);
4336:
4337: /*
4338: * Faults here on mmap()s can recurse into arbitrary
4339: * filesystem code. Lots of locks are held that can
4340: * deadlock. Use an atomic copy to avoid deadlocking
4341: * in page fault handling.
4342: */
4343: copied = copy_folio_from_iter_atomic(folio, offset, bytes, i);
4344: flush_dcache_folio(folio);
4345:
4346: status = a_ops->write_end(iocb, mapping, pos, bytes, copied,
4347: folio, fsdata);
4348: if (unlikely(status != copied)) {
4349: iov_iter_revert(i, copied - max(status, 0L));
4350: if (unlikely(status < 0))
4351: break;
4352: }
4353: cond_resched();
4354:
4355: if (unlikely(status == 0)) {
4356: /*
4357: * A short copy made ->write_end() reject the
4358: * thing entirely. Might be memory poisoning
4359: * halfway through, might be a race with munmap,
4360: * might be severe memory pressure.
4361: */
4362: if (chunk > PAGE_SIZE)
4363: chunk /= 2;
4364: if (copied) {
4365: bytes = copied;
4366: goto retry;
4367: }
4368:
4369: /*
4370: * 'folio' is now unlocked and faults on it can be
4371: * handled. Ensure forward progress by trying to
4372: * fault it in now.
4373: */
4374: if (fault_in_iov_iter_readable(i, bytes) == bytes) {
4375: status = -EFAULT;
4376: break;
4377: }
4378: } else {
4379: pos += status;
4380: written += status;
4381: }
4382: } while (iov_iter_count(i));
4383:
4384: if (!written)
4385: return status;
4386: iocb->ki_pos += written;
4387: return written;
4388: }
SourceFile:mm/filemap.c]
|
| 70/2 |
2026/05/22 01:48 |
llm |
expert |
0m
Model:
Tokens:
input: 75237
output: 49
thoughts: 1514
Thoughts: **Analyzing an OCFS2 Write Scenario After a Failed Reflink**
Okay, so here's what I'm thinking. I'm looking at a situation where a write operation is succeeding after a `vfs_copy_file_range` call that should have failed early due to a reflink operation failing because the filesystem is read-only. That's odd, because the call trace shows `ocfs2_write_end` being called, which suggests `ocfs2_write_begin` must have succeeded. But the error logs show `-EROFS` during the lock acquisition within `ocfs2_reflink_inodes_lock`.
My initial thought was that if `a_ops->write_begin` fails, then `generic_perform_write` breaks, so `a_ops->write_end` *shouldn't* be called. However, since the call trace exists, it means `ocfs2_write_begin` *did* succeed, which also means `ocfs2_inode_lock` (called by the begin) must have succeeded. But the logs explicitly state that `ocfs2_reflink_inodes_lock` failed with `-EROFS` (or more likely `-EFSCORRUPTED`, since `-EROFS` isn't used by the `assign_bh` call). This failure stems from `ocfs2_validate_inode_block` in `ocfs2_reflink_inodes_lock`.
The `vfs_copy_file_range` call leads to `ocfs2_reflink_inodes_lock`, and if that fails, `vfs_copy_file_range` falls back to `do_splice_direct`, which then ends up calling `generic_perform_write`. During `generic_perform_write`, `ocfs2_write_begin` is called. So, how can `ocfs2_inode_lock` succeed when the filesystem is effectively read-only?
I investigated `ocfs2_inode_lock_full_nested`. The code checks for `ocfs2_is_hard_readonly`, but it seems like that returns zero because the filesystem is set to `OCFS2_OSB_SOFT_RO` instead, so it doesn't return `-EROFS` directly in that branch. After that it potentially calls the `__ocfs2_cluster_lock` code, and then to `ocfs2_inode_lock_update`. Then it checks for and calls `ocfs2_assign_bh`, which then calls `ocfs2_read_inode_block`. If `ocfs2_read_inode_block` fails, then `ocfs2_assign_bh` should return `-EFSCORRUPTED`.
If `ocfs2_read_inode_block` fails, the write operation should stop. So how did a *successful* `ocfs2_write_begin` occur?
Then I realized something: `copy_file_range` involves two inodes - a source (`file_in`) and a destination (`file_out`). `ocfs2_reflink_inodes_lock` *locks* both. Could it be that `file_in` is corrupted and `file_out` isn't? If `file_in` is corrupted, the reflink operation fails and triggers a `do_splice_direct` to kick in.
If `file_in` is corrupted, reading from it should fail in `do_splice_direct`. However, `do_splice_direct` calls `splice_direct_to_actor`, and the reading operation should return an error there. So, the corrupted source would not lead to the problem.
What if `file_out` is corrupted, and `file_in` is not? In this case, `ocfs2_reflink_inodes_lock` will fail when locking `file_out`. Then, the `do_splice_direct` will still work because we are reading from the good inode, but the write to the bad inode will trigger the problem again. When writing to `file_out`, `ocfs2_write_begin` calls `ocfs2_inode_lock` on *that* inode. If `file_out` is corrupted, then *that* lock should fail! But it succeeded. Why?
The key might be in `ocfs2_read_inode_block`. It's only going to validate the inode block *if* it's not up-to-date in the buffer cache. If the block is in the buffer cache, it avoids the `ocfs2_validate_inode_block` call. Therefore, it is possible for a read-only filesystem to continue without throwing errors, depending on buffer cache state.
|
| 71/2 |
2026/05/22 01:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/inode.c IncludeLines:true Name:ocfs2_read_inode_block_full]
Results: map[SourceCode:1739: int ocfs2_read_inode_block_full(struct inode *inode, struct buffer_head **bh,
1740: int flags)
1741: {
1742: int rc;
1743: struct buffer_head *tmp = *bh;
1744:
1745: rc = ocfs2_read_blocks(INODE_CACHE(inode), OCFS2_I(inode)->ip_blkno,
1746: 1, &tmp, flags, ocfs2_validate_inode_block);
1747:
1748: if (rc < 0)
1749: make_bad_inode(inode);
1750: /* If ocfs2_read_blocks() got us a new bh, pass it up. */
1751: if (!rc && !*bh)
1752: *bh = tmp;
1753:
1754: return rc;
1755: }
SourceFile:fs/ocfs2/inode.c]
|
| 72/2 |
2026/05/22 01:48 |
llm |
expert |
0m
Model:
Tokens:
input: 78110
output: 49
thoughts: 44
Thoughts: Here's my analysis of the provided snippet, as though these were my own thoughts:
**Analyzing `ocfs2_read_blocks` and Validation**
Okay, so I'm looking at `ocfs2_read_blocks` here. The interesting thing that immediately jumps out at me is this conditional: it checks if the buffer is already up-to-date. If it *is*, it skips the call to the validator. Now, that's potentially significant. It suggests a performance optimization, but also raises some questions about data integrity. I need to dig deeper into the logic to understand the implications of this design. Let's see exactly how this check impacts the block validation process within this function. I need to fully understand if this has any potential to impact consistency of the data.
|
| 73/2 |
2026/05/22 01:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/buffer_head_io.c IncludeLines:true Name:ocfs2_read_blocks]
Results: map[SourceCode: 190: /* Caller must provide a bhs[] with all NULL or non-NULL entries, so it
191: * will be easier to handle read failure.
192: */
193: int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
194: struct buffer_head *bhs[], int flags,
195: int (*validate)(struct super_block *sb,
196: struct buffer_head *bh))
197: {
198: int status = 0;
199: int i, ignore_cache = 0;
200: struct buffer_head *bh;
201: struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
202: int new_bh = 0;
203:
204: trace_ocfs2_read_blocks_begin(ci, (unsigned long long)block, nr, flags);
205:
206: BUG_ON(!ci);
207: BUG_ON((flags & OCFS2_BH_READAHEAD) &&
208: (flags & OCFS2_BH_IGNORE_CACHE));
209:
210: if (bhs == NULL) {
211: status = -EINVAL;
212: mlog_errno(status);
213: goto bail;
214: }
215:
216: if (nr < 0) {
217: mlog(ML_ERROR, "asked to read %d blocks!\n", nr);
218: status = -EINVAL;
219: mlog_errno(status);
220: goto bail;
221: }
222:
223: if (nr == 0) {
224: status = 0;
225: goto bail;
226: }
227:
228: /* Don't put buffer head and re-assign it to NULL if it is allocated
229: * outside since the caller can't be aware of this alternation!
230: */
231: new_bh = (bhs[0] == NULL);
232:
233: ocfs2_metadata_cache_io_lock(ci);
234: for (i = 0 ; i < nr ; i++) {
235: if (bhs[i] == NULL) {
236: bhs[i] = sb_getblk(sb, block++);
237: if (bhs[i] == NULL) {
238: status = -ENOMEM;
239: mlog_errno(status);
240: /* Don't forget to put previous bh! */
241: break;
242: }
243: }
244: bh = bhs[i];
245: ignore_cache = (flags & OCFS2_BH_IGNORE_CACHE);
246:
247: /* There are three read-ahead cases here which we need to
248: * be concerned with. All three assume a buffer has
249: * previously been submitted with OCFS2_BH_READAHEAD
250: * and it hasn't yet completed I/O.
251: *
252: * 1) The current request is sync to disk. This rarely
253: * happens these days, and never when performance
254: * matters - the code can just wait on the buffer
255: * lock and re-submit.
256: *
257: * 2) The current request is cached, but not
258: * readahead. ocfs2_buffer_uptodate() will return
259: * false anyway, so we'll wind up waiting on the
260: * buffer lock to do I/O. We re-check the request
261: * with after getting the lock to avoid a re-submit.
262: *
263: * 3) The current request is readahead (and so must
264: * also be a caching one). We short circuit if the
265: * buffer is locked (under I/O) and if it's in the
266: * uptodate cache. The re-check from #2 catches the
267: * case that the previous read-ahead completes just
268: * before our is-it-in-flight check.
269: */
270:
271: if (!ignore_cache && !ocfs2_buffer_uptodate(ci, bh)) {
272: trace_ocfs2_read_blocks_from_disk(
273: (unsigned long long)bh->b_blocknr,
274: (unsigned long long)ocfs2_metadata_cache_owner(ci));
275: /* We're using ignore_cache here to say
276: * "go to disk" */
277: ignore_cache = 1;
278: }
279:
280: trace_ocfs2_read_blocks_bh((unsigned long long)bh->b_blocknr,
281: ignore_cache, buffer_jbd(bh), buffer_dirty(bh));
282:
283: if (buffer_jbd(bh)) {
284: continue;
285: }
286:
287: if (ignore_cache) {
288: if (buffer_dirty(bh)) {
289: /* This should probably be a BUG, or
290: * at least return an error. */
291: continue;
292: }
293:
294: /* A read-ahead request was made - if the
295: * buffer is already under read-ahead from a
296: * previously submitted request than we are
297: * done here. */
298: if ((flags & OCFS2_BH_READAHEAD)
299: && ocfs2_buffer_read_ahead(ci, bh))
300: continue;
301:
302: lock_buffer(bh);
303: if (buffer_jbd(bh)) {
304: #ifdef CATCH_BH_JBD_RACES
305: mlog(ML_ERROR, "block %llu had the JBD bit set "
306: "while I was in lock_buffer!",
307: (unsigned long long)bh->b_blocknr);
308: BUG();
309: #else
310: unlock_buffer(bh);
311: continue;
312: #endif
313: }
314:
315: /* Re-check ocfs2_buffer_uptodate() as a
316: * previously read-ahead buffer may have
317: * completed I/O while we were waiting for the
318: * buffer lock. */
319: if (!(flags & OCFS2_BH_IGNORE_CACHE)
320: && !(flags & OCFS2_BH_READAHEAD)
321: && ocfs2_buffer_uptodate(ci, bh)) {
322: unlock_buffer(bh);
323: continue;
324: }
325:
326: get_bh(bh); /* for end_buffer_read_sync() */
327: if (validate)
328: set_buffer_needs_validate(bh);
329: bh->b_end_io = end_buffer_read_sync;
330: submit_bh(REQ_OP_READ, bh);
331: continue;
332: }
333: }
334:
335: read_failure:
336: for (i = (nr - 1); i >= 0; i--) {
337: bh = bhs[i];
338:
339: if (!(flags & OCFS2_BH_READAHEAD)) {
340: if (unlikely(status)) {
341: /* Clear the buffers on error including those
342: * ever succeeded in reading
343: */
344: if (new_bh && bh) {
345: /* If middle bh fails, let previous bh
346: * finish its read and then put it to
347: * avoid bh leak
348: */
349: if (!buffer_jbd(bh))
350: wait_on_buffer(bh);
351: put_bh(bh);
352: bhs[i] = NULL;
353: } else if (bh && buffer_uptodate(bh)) {
354: clear_buffer_uptodate(bh);
355: }
356: continue;
357: }
358: /* We know this can't have changed as we hold the
359: * owner sem. Avoid doing any work on the bh if the
360: * journal has it. */
361: if (!buffer_jbd(bh))
362: wait_on_buffer(bh);
363:
364: if (!buffer_uptodate(bh)) {
365: /* Status won't be cleared from here on out,
366: * so we can safely record this and loop back
367: * to cleanup the other buffers. Don't need to
368: * remove the clustered uptodate information
369: * for this bh as it's not marked locally
370: * uptodate. */
371: status = -EIO;
372: clear_buffer_needs_validate(bh);
373: goto read_failure;
374: }
375:
376: if (buffer_needs_validate(bh)) {
377: /* We never set NeedsValidate if the
378: * buffer was held by the journal, so
379: * that better not have changed */
380: BUG_ON(buffer_jbd(bh));
381: clear_buffer_needs_validate(bh);
382: status = validate(sb, bh);
383: if (status)
384: goto read_failure;
385: }
386: }
387:
388: /* Always set the buffer in the cache, even if it was
389: * a forced read, or read-ahead which hasn't yet
390: * completed. */
391: if (bh)
392: ocfs2_set_buffer_uptodate(ci, bh);
393: }
394: ocfs2_metadata_cache_io_unlock(ci);
395:
396: trace_ocfs2_read_blocks_end((unsigned long long)block, nr,
397: flags, ignore_cache);
398:
399: bail:
400:
401: return status;
402: }
SourceFile:fs/ocfs2/buffer_head_io.c]
|
| 74/2 |
2026/05/22 01:48 |
llm |
expert |
0m
Model:
Tokens:
input: 81581
output: 54
thoughts: 1857
Thoughts: **Deconstructing the OCFS2 Corruption Bug**
Okay, so `ocfs2_read_blocks` optimizes by skipping disk reads and validation if a buffer is already in the cache and uptodate. That makes sense. However, the bug report shows a validation failure (`ocfs2_validate_inode_block`) for dinode #17058, leading to a read-only filesystem. This implies the inode *was* validated, but failed. The errors with `-30` on `ocfs2_reflink_inodes_lock` and `ocfs2_inode_lock_full_nested` point to a reflink operation, and a lock contention with dinode #17058. It looks like either `file_in` or `file_out` during the reflink is the corrupted inode.
If the corruption was in `file_out`, `ocfs2_write_begin` would also fail validation, right? Unless it's a *different* inode. Ah, `copy_file_range` is involved! The problem is that it is using a direct I/O operation via `do_splice_direct`, which calls `do_splice_to`. It reads from `file_in` and writes to `file_out`.
If `file_in` is the corrupted inode, reading *should* fail, but it succeeds. `splice_read` is called. For ocfs2, this can be `ocfs2_file_splice_read` or `generic_file_splice_read`. If the inode's data is already cached (e.g., inline data), then `splice_read` might just read from the page cache, bypassing the block read and validation! So `file_in` could be corrupted, and the splice read still "succeeds" because the data is already in memory. Okay, that seems plausible.
So, `file_in` is corrupted, but it reads from the page cache. Then it writes to `file_out`. If `file_out` is the problem, `ocfs2_reflink_inodes_lock` would fail on *write* operations. If `file_out` is valid, but now has the `OCFS2_INLINE_DATA_FL` flag, why does the size check work for large sizes? Unless `file_out` is corrupted as well.
Here's the kicker: `VM_BUG_ON` triggered! This must be because the write operation tried to write more data than the `folio_size`. The `folio_size` is typically 4096. But the code uses a check that ensures `pos + *copied` is <= 4096. For a valid inode the `id_count` is at most `ocfs2_max_inline_data_with_xattr`, which should not exceed 4096, so `pos + *copied <= folio_size(folio)`. But the `VM_BUG_ON` triggered. Thus, `file_out` *must* be corrupted, and its `id_count` must be *greater* than 4096!
However, if `file_out` is corrupted, why didn't `ocfs2_validate_inode_block` fail when it was *read* in `ocfs2_write_begin`? Because the buffer was already in the cache, and uptodate! `ocfs2_read_inode_block` skips validation if the buffer is cached and uptodate!
But if it was in the cache, how did it get there without being validated? Maybe it was recently created. Wait, the syzkaller uses loop devices! What if the *backing file* was modified *after* the inode was read and cached? Since it's a loop device, that's possible. That would explain it! But, the data isn't re-validated!
Wait... could the block size be larger than the page size? If block size is 8192 and the page size is 4096, `id_count` could be up to 8192! `pos + len` could be up to 8192, and `folio_size(folio)` is 4096! That could trigger the `VM_BUG_ON`! Let's check `ocfs2_max_inline_data_with_xattr`.
|
| 75/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/ocfs2.h IncludeLines:true Name:ocfs2_max_inline_data_with_xattr]
Results: map[SourceCode:1252: static inline int ocfs2_max_inline_data_with_xattr(struct super_block *sb,
1253: struct ocfs2_dinode *di)
1254: {
1255: unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
1256:
1257: if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
1258: return sb->s_blocksize -
1259: offsetof(struct ocfs2_dinode, id2.i_data.id_data) -
1260: xattrsize;
1261: else
1262: return sb->s_blocksize -
1263: offsetof(struct ocfs2_dinode, id2.i_data.id_data);
1264: }
SourceFile:fs/ocfs2/ocfs2_fs.h]
|
| 76/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 84737
output: 45
thoughts: 110
Thoughts: **Investigating Filesystem Block Size Limits**
Okay, so I'm thinking about block sizes, specifically `sb->s_blocksize`. On x86, it *can* be up to 4096. But, a crucial point clicks β this value is set during the mount process. Hmm, that leads me to wonder if it can, in practice, exceed the `PAGE_SIZE`. My experience with Linux tells me that, generally, the answer is no, most filesystems are constrained to `PAGE_SIZE` unless they've been specifically architected for large folios. Now, the real question that comes to mind is: does ocfs2, the filesystem I'm focused on, support block sizes greater than `PAGE_SIZE`? Time to dig into the code and start with `ocfs2_fill_super`. That's where I need to look for confirmation of the potential for large block size support.
|
| 77/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_fill_super]
Results: map[SourceCode: 973: static int ocfs2_fill_super(struct super_block *sb, struct fs_context *fc)
974: {
975: struct dentry *root;
976: int status, sector_size;
977: struct mount_options *parsed_options = fc->fs_private;
978: struct inode *inode = NULL;
979: struct ocfs2_super *osb = NULL;
980: struct buffer_head *bh = NULL;
981: char nodestr[12];
982: struct ocfs2_blockcheck_stats stats;
983:
984: trace_ocfs2_fill_super(sb, fc, fc->sb_flags & SB_SILENT);
985:
986: /* probe for superblock */
987: status = ocfs2_sb_probe(sb, &bh, §or_size, &stats);
988: if (status < 0) {
989: mlog(ML_ERROR, "superblock probe failed!\n");
990: goto out;
991: }
992:
993: status = ocfs2_initialize_super(sb, bh, sector_size, &stats);
994: brelse(bh);
995: bh = NULL;
996: if (status < 0)
997: goto out;
998:
999: osb = OCFS2_SB(sb);
1000:
1001: if (!ocfs2_check_set_options(sb, parsed_options)) {
1002: status = -EINVAL;
1003: goto out_super;
1004: }
1005: osb->s_mount_opt = parsed_options->mount_opt;
1006: osb->s_atime_quantum = parsed_options->atime_quantum;
1007: osb->preferred_slot = parsed_options->slot;
1008: osb->osb_commit_interval = parsed_options->commit_interval;
1009:
1010: ocfs2_la_set_sizes(osb, parsed_options->localalloc_opt);
1011: osb->osb_resv_level = parsed_options->resv_level;
1012: osb->osb_dir_resv_level = parsed_options->resv_level;
1013: if (parsed_options->dir_resv_level == -1)
1014: osb->osb_dir_resv_level = parsed_options->resv_level;
1015: else
1016: osb->osb_dir_resv_level = parsed_options->dir_resv_level;
1017:
1018: status = ocfs2_verify_userspace_stack(osb, parsed_options);
1019: if (status)
1020: goto out_super;
1021:
1022: sb->s_magic = OCFS2_SUPER_MAGIC;
1023:
1024: sb->s_flags = (sb->s_flags & ~(SB_POSIXACL | SB_NOSEC)) |
1025: ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) ? SB_POSIXACL : 0);
1026:
1027: /* Hard readonly mode only if: bdev_read_only, SB_RDONLY,
1028: * heartbeat=none */
1029: if (bdev_read_only(sb->s_bdev)) {
1030: if (!sb_rdonly(sb)) {
1031: status = -EACCES;
1032: mlog(ML_ERROR, "Readonly device detected but readonly "
1033: "mount was not specified.\n");
1034: goto out_super;
1035: }
1036:
1037: /* You should not be able to start a local heartbeat
1038: * on a readonly device. */
1039: if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) {
1040: status = -EROFS;
1041: mlog(ML_ERROR, "Local heartbeat specified on readonly "
1042: "device.\n");
1043: goto out_super;
1044: }
1045:
1046: status = ocfs2_check_journals_nolocks(osb);
1047: if (status < 0) {
1048: if (status == -EROFS)
1049: mlog(ML_ERROR, "Recovery required on readonly "
1050: "file system, but write access is "
1051: "unavailable.\n");
1052: goto out_super;
1053: }
1054:
1055: ocfs2_set_ro_flag(osb, 1);
1056:
1057: printk(KERN_NOTICE "ocfs2: Readonly device (%s) detected. "
1058: "Cluster services will not be used for this mount. "
1059: "Recovery will be skipped.\n", osb->dev_str);
1060: }
1061:
1062: if (!ocfs2_is_hard_readonly(osb)) {
1063: if (sb_rdonly(sb))
1064: ocfs2_set_ro_flag(osb, 0);
1065: }
1066:
1067: status = ocfs2_verify_heartbeat(osb);
1068: if (status < 0)
1069: goto out_super;
1070:
1071: osb->osb_debug_root = debugfs_create_dir(osb->uuid_str,
1072: ocfs2_debugfs_root);
1073:
1074: debugfs_create_file("fs_state", S_IFREG|S_IRUSR, osb->osb_debug_root,
1075: osb, &ocfs2_osb_debug_fops);
1076:
1077: if (ocfs2_meta_ecc(osb)) {
1078: ocfs2_initialize_journal_triggers(sb, osb->s_journal_triggers);
1079: ocfs2_blockcheck_stats_debugfs_install( &osb->osb_ecc_stats,
1080: osb->osb_debug_root);
1081: }
1082:
1083: status = ocfs2_mount_volume(sb);
1084: if (status < 0)
1085: goto out_debugfs;
1086:
1087: if (osb->root_inode)
1088: inode = igrab(osb->root_inode);
1089:
1090: if (!inode) {
1091: status = -EIO;
1092: goto out_dismount;
1093: }
1094:
1095: osb->osb_dev_kset = kset_create_and_add(sb->s_id, NULL,
1096: &ocfs2_kset->kobj);
1097: if (!osb->osb_dev_kset) {
1098: status = -ENOMEM;
1099: mlog(ML_ERROR, "Unable to create device kset %s.\n", sb->s_id);
1100: goto out_dismount;
1101: }
1102:
1103: /* Create filecheck sysfs related directories/files at
1104: * /sys/fs/ocfs2/<devname>/filecheck */
1105: if (ocfs2_filecheck_create_sysfs(osb)) {
1106: status = -ENOMEM;
1107: mlog(ML_ERROR, "Unable to create filecheck sysfs directory at "
1108: "/sys/fs/ocfs2/%s/filecheck.\n", sb->s_id);
1109: goto out_dismount;
1110: }
1111:
1112: root = d_make_root(inode);
1113: if (!root) {
1114: status = -ENOMEM;
1115: goto out_dismount;
1116: }
1117:
1118: sb->s_root = root;
1119:
1120: ocfs2_complete_mount_recovery(osb);
1121:
1122: if (ocfs2_mount_local(osb))
1123: snprintf(nodestr, sizeof(nodestr), "local");
1124: else
1125: snprintf(nodestr, sizeof(nodestr), "%u", osb->node_num);
1126:
1127: printk(KERN_INFO "ocfs2: Mounting device (%s) on (node %s, slot %d) "
1128: "with %s data mode.\n",
1129: osb->dev_str, nodestr, osb->slot_num,
1130: osb->s_mount_opt & OCFS2_MOUNT_DATA_WRITEBACK ? "writeback" :
1131: "ordered");
1132:
1133: atomic_set(&osb->vol_state, VOLUME_MOUNTED);
1134: wake_up(&osb->osb_mount_event);
1135:
1136: /* Now we can initialize quotas because we can afford to wait
1137: * for cluster locks recovery now. That also means that truncation
1138: * log recovery can happen but that waits for proper quota setup */
1139: if (!sb_rdonly(sb)) {
1140: status = ocfs2_enable_quotas(osb);
1141: if (status < 0) {
1142: /* We have to err-out specially here because
1143: * s_root is already set */
1144: mlog_errno(status);
1145: atomic_set(&osb->vol_state, VOLUME_DISABLED);
1146: wake_up(&osb->osb_mount_event);
1147: return status;
1148: }
1149: }
1150:
1151: ocfs2_complete_quota_recovery(osb);
1152:
1153: /* Now we wake up again for processes waiting for quotas */
1154: atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
1155: wake_up(&osb->osb_mount_event);
1156:
1157: /* Start this when the mount is almost sure of being successful */
1158: ocfs2_orphan_scan_start(osb);
1159:
1160: return status;
1161:
1162: out_dismount:
1163: atomic_set(&osb->vol_state, VOLUME_DISABLED);
1164: wake_up(&osb->osb_mount_event);
1165: ocfs2_free_replay_slots(osb);
1166: ocfs2_dismount_volume(sb, 1);
1167: goto out;
1168:
1169: out_debugfs:
1170: debugfs_remove_recursive(osb->osb_debug_root);
1171: out_super:
1172: ocfs2_release_system_inodes(osb);
1173: kfree(osb->recovery_map);
1174: ocfs2_delete_osb(osb);
1175: kfree(osb);
1176: out:
1177: mlog_errno(status);
1178:
1179: return status;
1180: }
SourceFile:fs/ocfs2/super.c]
|
| 78/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 88485
output: 45
thoughts: 20
Thoughts: Here's my thought process, as if I were considering this from an expert perspective:
**Initial Superblock Initialization and Block Size**
Okay, so I'm digging into this code, specifically around the OCFS2 superblock initialization. My immediate thought is, "Ah, right, `ocfs2_initialize_super` is the function that actually handles setting the `sb->s_blocksize` value." It's a fundamental part of the mounting process, crucial for everything else that follows. That function dictates the fundamental unit of storage that the file system operates on. It's not something you want to mess up, as a wrong block size will corrupt data immediately. It's a critical, early operation.
|
| 79/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_initialize_super]
Results: map[SourceCode:1944: static int ocfs2_initialize_super(struct super_block *sb,
1945: struct buffer_head *bh,
1946: int sector_size,
1947: struct ocfs2_blockcheck_stats *stats)
1948: {
1949: int status;
1950: int i, cbits, bbits;
1951: struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data;
1952: struct inode *inode = NULL;
1953: struct ocfs2_super *osb;
1954: u64 total_blocks;
1955:
1956: osb = kzalloc_obj(struct ocfs2_super);
1957: if (!osb) {
1958: status = -ENOMEM;
1959: mlog_errno(status);
1960: goto out;
1961: }
1962:
1963: sb->s_fs_info = osb;
1964: sb->s_op = &ocfs2_sops;
1965: set_default_d_op(sb, &ocfs2_dentry_ops);
1966: sb->s_export_op = &ocfs2_export_ops;
1967: sb->s_qcop = &dquot_quotactl_sysfile_ops;
1968: sb->dq_op = &ocfs2_quota_operations;
1969: sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
1970: sb->s_xattr = ocfs2_xattr_handlers;
1971: sb->s_time_gran = 1;
1972: sb->s_flags |= SB_NOATIME;
1973: /* this is needed to support O_LARGEFILE */
1974: cbits = le32_to_cpu(di->id2.i_super.s_clustersize_bits);
1975: bbits = le32_to_cpu(di->id2.i_super.s_blocksize_bits);
1976: sb->s_maxbytes = ocfs2_max_file_offset(bbits, cbits);
1977: super_set_uuid(sb, di->id2.i_super.s_uuid,
1978: sizeof(di->id2.i_super.s_uuid));
1979:
1980: osb->osb_dx_mask = (1 << (cbits - bbits)) - 1;
1981:
1982: for (i = 0; i < 3; i++)
1983: osb->osb_dx_seed[i] = le32_to_cpu(di->id2.i_super.s_dx_seed[i]);
1984: osb->osb_dx_seed[3] = le32_to_cpu(di->id2.i_super.s_uuid_hash);
1985:
1986: osb->sb = sb;
1987: osb->s_sectsize_bits = blksize_bits(sector_size);
1988: BUG_ON(!osb->s_sectsize_bits);
1989:
1990: spin_lock_init(&osb->dc_task_lock);
1991: init_waitqueue_head(&osb->dc_event);
1992: osb->dc_work_sequence = 0;
1993: osb->dc_wake_sequence = 0;
1994: INIT_LIST_HEAD(&osb->blocked_lock_list);
1995: osb->blocked_lock_count = 0;
1996: spin_lock_init(&osb->osb_lock);
1997: spin_lock_init(&osb->osb_xattr_lock);
1998: ocfs2_init_steal_slots(osb);
1999:
2000: mutex_init(&osb->system_file_mutex);
2001:
2002: atomic_set(&osb->alloc_stats.moves, 0);
2003: atomic_set(&osb->alloc_stats.local_data, 0);
2004: atomic_set(&osb->alloc_stats.bitmap_data, 0);
2005: atomic_set(&osb->alloc_stats.bg_allocs, 0);
2006: atomic_set(&osb->alloc_stats.bg_extends, 0);
2007:
2008: /* Copy the blockcheck stats from the superblock probe */
2009: osb->osb_ecc_stats = *stats;
2010:
2011: ocfs2_init_node_maps(osb);
2012:
2013: snprintf(osb->dev_str, sizeof(osb->dev_str), "%u,%u",
2014: MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev));
2015:
2016: osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots);
2017: if (osb->max_slots > OCFS2_MAX_SLOTS || osb->max_slots == 0) {
2018: mlog(ML_ERROR, "Invalid number of node slots (%u)\n",
2019: osb->max_slots);
2020: status = -EINVAL;
2021: goto out;
2022: }
2023:
2024: ocfs2_orphan_scan_init(osb);
2025:
2026: status = ocfs2_recovery_init(osb);
2027: if (status) {
2028: mlog(ML_ERROR, "Unable to initialize recovery state\n");
2029: mlog_errno(status);
2030: goto out;
2031: }
2032:
2033: init_waitqueue_head(&osb->checkpoint_event);
2034:
2035: osb->s_atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM;
2036:
2037: osb->slot_num = OCFS2_INVALID_SLOT;
2038:
2039: osb->s_xattr_inline_size = le16_to_cpu(
2040: di->id2.i_super.s_xattr_inline_size);
2041:
2042: osb->local_alloc_state = OCFS2_LA_UNUSED;
2043: osb->local_alloc_bh = NULL;
2044: INIT_DELAYED_WORK(&osb->la_enable_wq, ocfs2_la_enable_worker);
2045:
2046: init_waitqueue_head(&osb->osb_mount_event);
2047:
2048: ocfs2_resmap_init(osb, &osb->osb_la_resmap);
2049:
2050: osb->vol_label = kmalloc(OCFS2_MAX_VOL_LABEL_LEN, GFP_KERNEL);
2051: if (!osb->vol_label) {
2052: mlog(ML_ERROR, "unable to alloc vol label\n");
2053: status = -ENOMEM;
2054: goto out_recovery_map;
2055: }
2056:
2057: osb->slot_recovery_generations =
2058: kcalloc(osb->max_slots, sizeof(*osb->slot_recovery_generations),
2059: GFP_KERNEL);
2060: if (!osb->slot_recovery_generations) {
2061: status = -ENOMEM;
2062: mlog_errno(status);
2063: goto out_vol_label;
2064: }
2065:
2066: init_waitqueue_head(&osb->osb_wipe_event);
2067: osb->osb_orphan_wipes = kcalloc(osb->max_slots,
2068: sizeof(*osb->osb_orphan_wipes),
2069: GFP_KERNEL);
2070: if (!osb->osb_orphan_wipes) {
2071: status = -ENOMEM;
2072: mlog_errno(status);
2073: goto out_slot_recovery_gen;
2074: }
2075:
2076: osb->osb_rf_lock_tree = RB_ROOT;
2077:
2078: osb->s_feature_compat =
2079: le32_to_cpu(OCFS2_RAW_SB(di)->s_feature_compat);
2080: osb->s_feature_ro_compat =
2081: le32_to_cpu(OCFS2_RAW_SB(di)->s_feature_ro_compat);
2082: osb->s_feature_incompat =
2083: le32_to_cpu(OCFS2_RAW_SB(di)->s_feature_incompat);
2084:
2085: if ((i = OCFS2_HAS_INCOMPAT_FEATURE(osb->sb, ~OCFS2_FEATURE_INCOMPAT_SUPP))) {
2086: mlog(ML_ERROR, "couldn't mount because of unsupported "
2087: "optional features (%x).\n", i);
2088: status = -EINVAL;
2089: goto out_orphan_wipes;
2090: }
2091: if (!sb_rdonly(osb->sb) && (i = OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb, ~OCFS2_FEATURE_RO_COMPAT_SUPP))) {
2092: mlog(ML_ERROR, "couldn't mount RDWR because of "
2093: "unsupported optional features (%x).\n", i);
2094: status = -EINVAL;
2095: goto out_orphan_wipes;
2096: }
2097:
2098: if (ocfs2_clusterinfo_valid(osb)) {
2099: /*
2100: * ci_stack and ci_cluster in ocfs2_cluster_info may not be null
2101: * terminated, so make sure no overflow happens here by using
2102: * memcpy. Destination strings will always be null terminated
2103: * because osb is allocated using kzalloc.
2104: */
2105: osb->osb_stackflags =
2106: OCFS2_RAW_SB(di)->s_cluster_info.ci_stackflags;
2107: memcpy(osb->osb_cluster_stack,
2108: OCFS2_RAW_SB(di)->s_cluster_info.ci_stack,
2109: OCFS2_STACK_LABEL_LEN);
2110: if (strlen(osb->osb_cluster_stack) != OCFS2_STACK_LABEL_LEN) {
2111: mlog(ML_ERROR,
2112: "couldn't mount because of an invalid "
2113: "cluster stack label (%s) \n",
2114: osb->osb_cluster_stack);
2115: status = -EINVAL;
2116: goto out_orphan_wipes;
2117: }
2118: memcpy(osb->osb_cluster_name,
2119: OCFS2_RAW_SB(di)->s_cluster_info.ci_cluster,
2120: OCFS2_CLUSTER_NAME_LEN);
2121: } else {
2122: /* The empty string is identical with classic tools that
2123: * don't know about s_cluster_info. */
2124: osb->osb_cluster_stack[0] = '\0';
2125: }
2126:
2127: osb->s_next_generation = get_random_u32();
2128:
2129: /*
2130: * FIXME
2131: * This should be done in ocfs2_journal_init(), but any inode
2132: * writes back operation will cause the filesystem to crash.
2133: */
2134: status = ocfs2_journal_alloc(osb);
2135: if (status < 0)
2136: goto out_orphan_wipes;
2137:
2138: INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs);
2139: init_llist_head(&osb->dquot_drop_list);
2140:
2141: /* get some pseudo constants for clustersize bits */
2142: osb->s_clustersize_bits =
2143: le32_to_cpu(di->id2.i_super.s_clustersize_bits);
2144: osb->s_clustersize = 1 << osb->s_clustersize_bits;
2145:
2146: if (osb->s_clustersize < OCFS2_MIN_CLUSTERSIZE ||
2147: osb->s_clustersize > OCFS2_MAX_CLUSTERSIZE) {
2148: mlog(ML_ERROR, "Volume has invalid cluster size (%d)\n",
2149: osb->s_clustersize);
2150: status = -EINVAL;
2151: goto out_journal;
2152: }
2153:
2154: total_blocks = ocfs2_clusters_to_blocks(osb->sb,
2155: le32_to_cpu(di->i_clusters));
2156:
2157: status = generic_check_addressable(osb->sb->s_blocksize_bits,
2158: total_blocks);
2159: if (status) {
2160: mlog(ML_ERROR, "Volume too large "
2161: "to mount safely on this system");
2162: status = -EFBIG;
2163: goto out_journal;
2164: }
2165:
2166: if (ocfs2_setup_osb_uuid(osb, di->id2.i_super.s_uuid,
2167: sizeof(di->id2.i_super.s_uuid))) {
2168: mlog(ML_ERROR, "Out of memory trying to setup our uuid.\n");
2169: status = -ENOMEM;
2170: goto out_journal;
2171: }
2172:
2173: strscpy(osb->vol_label, di->id2.i_super.s_label,
2174: OCFS2_MAX_VOL_LABEL_LEN);
2175: osb->root_blkno = le64_to_cpu(di->id2.i_super.s_root_blkno);
2176: osb->system_dir_blkno = le64_to_cpu(di->id2.i_super.s_system_dir_blkno);
2177: osb->first_cluster_group_blkno =
2178: le64_to_cpu(di->id2.i_super.s_first_cluster_group);
2179: osb->fs_generation = le32_to_cpu(di->i_fs_generation);
2180: osb->uuid_hash = le32_to_cpu(di->id2.i_super.s_uuid_hash);
2181: trace_ocfs2_initialize_super(osb->vol_label, osb->uuid_str,
2182: (unsigned long long)osb->root_blkno,
2183: (unsigned long long)osb->system_dir_blkno,
2184: osb->s_clustersize_bits);
2185:
2186: osb->osb_dlm_debug = ocfs2_new_dlm_debug();
2187: if (!osb->osb_dlm_debug) {
2188: status = -ENOMEM;
2189: mlog_errno(status);
2190: goto out_uuid_str;
2191: }
2192:
2193: atomic_set(&osb->vol_state, VOLUME_INIT);
2194:
2195: /* load root, system_dir, and all global system inodes */
2196: status = ocfs2_init_global_system_inodes(osb);
2197: if (status < 0) {
2198: mlog_errno(status);
2199: goto out_dlm_out;
2200: }
2201:
2202: /*
2203: * global bitmap
2204: */
2205: inode = ocfs2_get_system_file_inode(osb, GLOBAL_BITMAP_SYSTEM_INODE,
2206: OCFS2_INVALID_SLOT);
2207: if (!inode) {
2208: status = -EINVAL;
2209: mlog_errno(status);
2210: goto out_system_inodes;
2211: }
2212:
2213: osb->bitmap_blkno = OCFS2_I(inode)->ip_blkno;
2214: osb->osb_clusters_at_boot = OCFS2_I(inode)->ip_clusters;
2215: iput(inode);
2216:
2217: osb->bitmap_cpg = ocfs2_group_bitmap_size(sb, 0,
2218: osb->s_feature_incompat) * 8;
2219:
2220: status = ocfs2_init_slot_info(osb);
2221: if (status < 0) {
2222: mlog_errno(status);
2223: goto out_system_inodes;
2224: }
2225:
2226: osb->ocfs2_wq = alloc_ordered_workqueue("ocfs2_wq", WQ_MEM_RECLAIM);
2227: if (!osb->ocfs2_wq) {
2228: status = -ENOMEM;
2229: mlog_errno(status);
2230: goto out_slot_info;
2231: }
2232:
2233: return status;
2234:
2235: out_slot_info:
2236: ocfs2_free_slot_info(osb);
2237: out_system_inodes:
2238: ocfs2_release_system_inodes(osb);
2239: out_dlm_out:
2240: ocfs2_put_dlm_debug(osb->osb_dlm_debug);
2241: out_uuid_str:
2242: kfree(osb->uuid_str);
2243: out_journal:
2244: kfree(osb->journal);
2245: out_orphan_wipes:
2246: kfree(osb->osb_orphan_wipes);
2247: out_slot_recovery_gen:
2248: kfree(osb->slot_recovery_generations);
2249: out_vol_label:
2250: kfree(osb->vol_label);
2251: out_recovery_map:
2252: kfree(osb->recovery_map);
2253: out:
2254: kfree(osb);
2255: sb->s_fs_info = NULL;
2256: return status;
2257: }
SourceFile:fs/ocfs2/super.c]
|
| 80/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 94282
output: 45
thoughts: 37
Thoughts: Here's my take on the thought process, as requested:
**Deconstructing the Filesystem Probe**
Okay, hold on a second. I'm just looking at this code and something's pinging my radar. Did I just see that `sb->s_blocksize` is being initialized within `ocfs2_sb_probe`? That seems... important. And potentially a point of confusion down the line if something is off.
Right, time to dive into `ocfs2_sb_probe`. Need to refresh my memory on the ins and outs of this function β see exactly how and when that block size gets set. I need to understand the exact sequence of events here. What's driving the blocksize determination? Any potential for overrides or subtle logic that could lead to unexpected values? Got to ensure that the block size, which is obviously fundamental to the filesystem's operation, is being handled correctly during the mount. A solid understanding of `ocfs2_sb_probe` is essential for any future debugging or modifications related to block size handling. Let's make sure I didn't miss anything crucial.
|
| 81/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/super.c IncludeLines:true Name:ocfs2_sb_probe]
Results: map[SourceCode: 725: static int ocfs2_sb_probe(struct super_block *sb,
726: struct buffer_head **bh,
727: int *sector_size,
728: struct ocfs2_blockcheck_stats *stats)
729: {
730: int status, tmpstat;
731: struct ocfs1_vol_disk_hdr *hdr;
732: struct ocfs2_dinode *di;
733: int blksize;
734:
735: *bh = NULL;
736:
737: /* may be > 512 */
738: *sector_size = bdev_logical_block_size(sb->s_bdev);
739: if (*sector_size > OCFS2_MAX_BLOCKSIZE) {
740: mlog(ML_ERROR, "Hardware sector size too large: %d (max=%d)\n",
741: *sector_size, OCFS2_MAX_BLOCKSIZE);
742: status = -EINVAL;
743: goto bail;
744: }
745:
746: /* Can this really happen? */
747: if (*sector_size < OCFS2_MIN_BLOCKSIZE)
748: *sector_size = OCFS2_MIN_BLOCKSIZE;
749:
750: /* check block zero for old format */
751: status = ocfs2_get_sector(sb, bh, 0, *sector_size);
752: if (status < 0) {
753: mlog_errno(status);
754: goto bail;
755: }
756: hdr = (struct ocfs1_vol_disk_hdr *) (*bh)->b_data;
757: if (hdr->major_version == OCFS1_MAJOR_VERSION) {
758: mlog(ML_ERROR, "incompatible version: %u.%u\n",
759: hdr->major_version, hdr->minor_version);
760: status = -EINVAL;
761: }
762: if (memcmp(hdr->signature, OCFS1_VOLUME_SIGNATURE,
763: strlen(OCFS1_VOLUME_SIGNATURE)) == 0) {
764: mlog(ML_ERROR, "incompatible volume signature: %8s\n",
765: hdr->signature);
766: status = -EINVAL;
767: }
768: brelse(*bh);
769: *bh = NULL;
770: if (status < 0) {
771: mlog(ML_ERROR, "This is an ocfs v1 filesystem which must be "
772: "upgraded before mounting with ocfs v2\n");
773: goto bail;
774: }
775:
776: /*
777: * Now check at magic offset for 512, 1024, 2048, 4096
778: * blocksizes. 4096 is the maximum blocksize because it is
779: * the minimum clustersize.
780: */
781: status = -EINVAL;
782: for (blksize = *sector_size;
783: blksize <= OCFS2_MAX_BLOCKSIZE;
784: blksize <<= 1) {
785: tmpstat = ocfs2_get_sector(sb, bh,
786: OCFS2_SUPER_BLOCK_BLKNO,
787: blksize);
788: if (tmpstat < 0) {
789: status = tmpstat;
790: mlog_errno(status);
791: break;
792: }
793: di = (struct ocfs2_dinode *) (*bh)->b_data;
794: memset(stats, 0, sizeof(struct ocfs2_blockcheck_stats));
795: spin_lock_init(&stats->b_lock);
796: tmpstat = ocfs2_verify_volume(di, *bh, blksize, stats);
797: if (tmpstat < 0) {
798: brelse(*bh);
799: *bh = NULL;
800: }
801: if (tmpstat != -EAGAIN) {
802: status = tmpstat;
803: break;
804: }
805: }
806:
807: bail:
808: return status;
809: }
SourceFile:fs/ocfs2/super.c]
|
| 82/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 95963
output: 50
thoughts: 860
Thoughts: **Debugging an OCFS2 Write Issue**
Okay, so I'm looking at this OCFS2 bug report, and it seems like a `VM_BUG_ON` triggered, which is only enabled with `CONFIG_DEBUG_VM`. That points me towards a Syzkaller kernel, which makes sense. The specific issue is within `ocfs2_write_end_nolock`, and the `VM_BUG_ON` condition hinges on `pos + *copied > folio_size(folio)`. That's...interesting.
First things first: I'm reminded that the maximum block size in OCFS2 is `OCFS2_MAX_BLOCKSIZE`, which is 4096. So, if `sb->s_blocksize` is at most 4096, then the inline data maximum (`ocfs2_max_inline_data_with_xattr`) is also at most 4096. This in turn limits `id_count`, `new_size`, `pos + len`, and `pos + *copied` to values less than or equal to 4096. This is important.
So, if that's the case, then `pos + *copied <= folio_size(folio)` SHOULD be true. But it's triggering the `VM_BUG_ON`, so it's *not* true. That's a contradiction. How is `pos + *copied` getting *larger* than 4096?
I need to retrace the logic. If `ocfs2_size_fits_inline_data` is returning true for a `new_size` larger than 4096, that's my starting point. But `ocfs2_size_fits_inline_data` is supposed to check `new_size <= le16_to_cpu(di->id2.i_data.id_count)`. Aha! What if `id_count` is corrupted on disk and greater than 4096, say 65535? Then `ocfs2_size_fits_inline_data` would incorrectly report that it fits!
But, wait...`ocfs2_validate_inode_block` is supposed to check if `id_count <= ocfs2_max_inline_data_with_xattr(sb, di)`. If `id_count` *is* 65535, `ocfs2_validate_inode_block` should fail! And a failure there means the inode block would NOT be marked uptodate. So, something is broken.
Could it be that the inode block *was* uptodate before it was corrupted? If so, how did it get corrupted? `ocfs2_validate_inode_block` is called when reading from disk, so it's checking what's *on* disk.
So, can an inode be created with a large `id_count` in memory and written? I need to look into how the data is set, in particular, I should see how `id_count` is set by `ocfs2_set_inode_data_inline`. I need to go deeper here.
|
| 83/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_set_inode_data_inline]
Results: map[SourceCode:7069: void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di)
7070: {
7071: struct ocfs2_inode_info *oi = OCFS2_I(inode);
7072: struct ocfs2_inline_data *idata = &di->id2.i_data;
7073:
7074: spin_lock(&oi->ip_lock);
7075: oi->ip_dyn_features |= OCFS2_INLINE_DATA_FL;
7076: di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
7077: spin_unlock(&oi->ip_lock);
7078:
7079: /*
7080: * We clear the entire i_data structure here so that all
7081: * fields can be properly initialized.
7082: */
7083: ocfs2_zero_dinode_id2_with_xattr(inode, di);
7084:
7085: idata->id_count = cpu_to_le16(
7086: ocfs2_max_inline_data_with_xattr(inode->i_sb, di));
7087: }
SourceFile:fs/ocfs2/alloc.c]
|
| 84/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 97959
output: 47
thoughts: 614
Thoughts: **Analyzing a Potential File System Corruption Scenario**
Okay, so I'm digging into this code, trying to figure out a potential corruption scenario. I see that `id_count` is limited to `ocfs2_max_inline_data_with_xattr`, which is, at most, 4096. So, large values aren't supposed to be possible during creation. But what if there's a problem, and `ocfs2_validate_inode_block` *fails*, but the error is somehow *ignored*? I need to trace this more carefully.
Let's see: `vfs_copy_file_range` calls `ocfs2_remap_file_range`. Within `ocfs2_remap_file_range`, it calls `ocfs2_reflink_inodes_lock`, which in turn reads the inode block of either `file_in` or `file_out`. Now, imagine the inode block on disk *is* corrupted β say, `id_count` is 65535, a clearly bad value.
The subsequent call to `ocfs2_validate_inode_block` fails, and `ocfs2_error` is called, setting `OCFS2_OSB_SOFT_RO` and marking the filesystem read-only. `ocfs2_read_inode_block_full` then returns `-EFSCORRUPTED`. This propagates: `ocfs2_reflink_inodes_lock` returns `-EFSCORRUPTED`, and so does `ocfs2_remap_file_range`. `vfs_copy_file_range` sees that `ret <= 0`, specifically, the error code `-EFSCORRUPTED`.
Wait a minute, look at that code! It checks if `ret <= 0`. If it's indeed `-EFSCORRUPTED`, the condition is met, so `splice = true`! Then it falls back to `do_splice_direct`.
Now, if `file_in` is the corrupted inode, reading from it would invoke `ocfs2_file_splice_read`. This is key. Does `ocfs2_file_splice_read` actually read the inode block? I need to check the code for `ocfs2_file_splice_read` to be sure. This might be where the corruption rears its head!
|
| 85/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/file.c IncludeLines:true Name:ocfs2_file_splice_read]
Results: map[SourceCode:2614: static ssize_t ocfs2_file_splice_read(struct file *in, loff_t *ppos,
2615: struct pipe_inode_info *pipe,
2616: size_t len, unsigned int flags)
2617: {
2618: struct inode *inode = file_inode(in);
2619: ssize_t ret = 0;
2620: int lock_level = 0;
2621:
2622: trace_ocfs2_file_splice_read(inode, in, in->f_path.dentry,
2623: (unsigned long long)OCFS2_I(inode)->ip_blkno,
2624: in->f_path.dentry->d_name.len,
2625: in->f_path.dentry->d_name.name,
2626: flags);
2627:
2628: /*
2629: * We're fine letting folks race truncates and extending writes with
2630: * read across the cluster, just like they can locally. Hence no
2631: * rw_lock during read.
2632: *
2633: * Take and drop the meta data lock to update inode fields like i_size.
2634: * This allows the checks down below filemap_splice_read() a chance of
2635: * actually working.
2636: */
2637: ret = ocfs2_inode_lock_atime(inode, in->f_path.mnt, &lock_level, 1);
2638: if (ret < 0) {
2639: if (ret != -EAGAIN)
2640: mlog_errno(ret);
2641: goto bail;
2642: }
2643: ocfs2_inode_unlock(inode, lock_level);
2644:
2645: ret = filemap_splice_read(in, ppos, pipe, len, flags);
2646: trace_filemap_splice_read_ret(ret);
2647: bail:
2648: return ret;
2649: }
SourceFile:fs/ocfs2/file.c]
|
| 86/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 99762
output: 47
thoughts: 340
Thoughts: **Analyzing a Potential OCFS2 File Corruption Scenario**
Okay, let's break this down from a file system perspective. I'm seeing a sequence here related to `ocfs2_file_splice_read` and how it interacts with the locking and potential corruption scenarios in OCFS2. My initial thought is that `ocfs2_file_splice_read` calls `ocfs2_inode_lock_atime`. And that function, `ocfs2_inode_lock_atime`, calls `ocfs2_inode_lock_full_nested`. So, we're nesting our locking here.
Now, the crucial point: if the input file (`file_in`) is corrupt, `ocfs2_inode_lock_atime` is going to fail, specifically with an `-EFSCORRUPTED` error. This error then bubbles up through `ocfs2_file_splice_read`, then `do_splice_direct`, and finally `vfs_copy_file_range`, ultimately resulting in `-EFSCORRUPTED` being returned. So, the implication here is that if the *input* file is corrupt, the entire copy operation won't even start. The write to the *output* file (`file_out`) is guaranteed to not happen.
That's the interesting part, because the initial thinking had me in a loop. I started out certain that `file_in` had to be valid, and `file_out` was corrupt. However, if `file_out` were corrupt, I would expect a different error path. Locking operations would fail, specifically `ocfs2_reflink_inodes_lock`. The result is the operation would fall back to `do_splice_direct`. This would *read* from `file_in` (which would be valid, based on the previous error path) successfully.
Here's the problem. It then tries to *write* to `file_out`. During that write process, `do_splice_direct` calls `ocfs2_file_write_iter`. And `ocfs2_file_write_iter`, in turn, is going to call `ocfs2_rw_lock(inode, rw_level)`. So, *that* is the key point to investigate. I need to dig into `ocfs2_rw_lock` to understand exactly what that lock does. This is the place where the corruption may finally surface and be properly detected, or where a write to a corrupted inode could actually happen. We're looking at a concurrency issue here.
|
| 87/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_rw_lock]
Results: map[SourceCode:1764: int ocfs2_rw_lock(struct inode *inode, int write)
1765: {
1766: int status, level;
1767: struct ocfs2_lock_res *lockres;
1768: struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
1769:
1770: mlog(0, "inode %llu take %s RW lock\n",
1771: (unsigned long long)OCFS2_I(inode)->ip_blkno,
1772: write ? "EXMODE" : "PRMODE");
1773:
1774: if (ocfs2_mount_local(osb))
1775: return 0;
1776:
1777: lockres = &OCFS2_I(inode)->ip_rw_lockres;
1778:
1779: level = write ? DLM_LOCK_EX : DLM_LOCK_PR;
1780:
1781: status = ocfs2_cluster_lock(osb, lockres, level, 0, 0);
1782: if (status < 0)
1783: mlog_errno(status);
1784:
1785: return status;
1786: }
SourceFile:fs/ocfs2/dlmglue.c]
|
| 88/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 101098
output: 49
thoughts: 70
Thoughts: Here's my summary, as the expert:
**Delving into OCFS2 Write Operations**
Okay, so I'm looking at how OCFS2 handles writes, and I'm zeroing in on this `ocfs2_rw_lock` function. Now, a key point to remember here is that, at least from what I'm seeing, this isn't the part of the code that's responsible for directly accessing or reading the inode block itself. All it's doing is grabbing that Distributed Lock Manager (DLM) lock. Got it. That's a critical synchronization step, but not the direct I/O.
So, where does the real work happen? Ah, right! Following the execution flow, it looks like `ocfs2_file_write_iter` comes into play next. And it looks like, within that function, we have a call to `ocfs2_prepare_inode_for_write`. This is definitely where I want to focus my attention. Itβs likely in this function where we will find all the block preparation and inode management that is needed prior to an update. Let's dig into `ocfs2_prepare_inode_for_write` and take a closer look there. I suspect we'll find some interesting details regarding the pre-write setup and data handling.
|
| 89/2 |
2026/05/22 01:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/file.c IncludeLines:true Name:ocfs2_prepare_inode_for_write]
Results: map[SourceCode:2257: static int ocfs2_prepare_inode_for_write(struct file *file,
2258: loff_t pos, size_t count, int wait)
2259: {
2260: int ret = 0, meta_level = 0, overwrite_io = 0;
2261: int write_sem = 0;
2262: struct dentry *dentry = file->f_path.dentry;
2263: struct inode *inode = d_inode(dentry);
2264: struct buffer_head *di_bh = NULL;
2265: u32 cpos;
2266: u32 clusters;
2267:
2268: /*
2269: * We start with a read level meta lock and only jump to an ex
2270: * if we need to make modifications here.
2271: */
2272: for(;;) {
2273: ret = ocfs2_inode_lock_for_extent_tree(inode,
2274: &di_bh,
2275: meta_level,
2276: write_sem,
2277: wait);
2278: if (ret < 0) {
2279: if (ret != -EAGAIN)
2280: mlog_errno(ret);
2281: goto out;
2282: }
2283:
2284: /*
2285: * Check if IO will overwrite allocated blocks in case
2286: * IOCB_NOWAIT flag is set.
2287: */
2288: if (!wait && !overwrite_io) {
2289: overwrite_io = 1;
2290:
2291: ret = ocfs2_overwrite_io(inode, di_bh, pos, count);
2292: if (ret < 0) {
2293: if (ret != -EAGAIN)
2294: mlog_errno(ret);
2295: goto out_unlock;
2296: }
2297: }
2298:
2299: /* Clear suid / sgid if necessary. We do this here
2300: * instead of later in the write path because
2301: * remove_suid() calls ->setattr without any hint that
2302: * we may have already done our cluster locking. Since
2303: * ocfs2_setattr() *must* take cluster locks to
2304: * proceed, this will lead us to recursively lock the
2305: * inode. There's also the dinode i_size state which
2306: * can be lost via setattr during extending writes (we
2307: * set inode->i_size at the end of a write. */
2308: if (setattr_should_drop_suidgid(&nop_mnt_idmap, inode)) {
2309: if (meta_level == 0) {
2310: ocfs2_inode_unlock_for_extent_tree(inode,
2311: &di_bh,
2312: meta_level,
2313: write_sem);
2314: meta_level = 1;
2315: continue;
2316: }
2317:
2318: ret = ocfs2_write_remove_suid(inode);
2319: if (ret < 0) {
2320: mlog_errno(ret);
2321: goto out_unlock;
2322: }
2323: }
2324:
2325: ret = ocfs2_check_range_for_refcount(inode, pos, count);
2326: if (ret == 1) {
2327: ocfs2_inode_unlock_for_extent_tree(inode,
2328: &di_bh,
2329: meta_level,
2330: write_sem);
2331: meta_level = 1;
2332: write_sem = 1;
2333: ret = ocfs2_inode_lock_for_extent_tree(inode,
2334: &di_bh,
2335: meta_level,
2336: write_sem,
2337: wait);
2338: if (ret < 0) {
2339: if (ret != -EAGAIN)
2340: mlog_errno(ret);
2341: goto out;
2342: }
2343:
2344: cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
2345: clusters =
2346: ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos;
2347: ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
2348: }
2349:
2350: if (ret < 0) {
2351: if (ret != -EAGAIN)
2352: mlog_errno(ret);
2353: goto out_unlock;
2354: }
2355:
2356: break;
2357: }
2358:
2359: out_unlock:
2360: trace_ocfs2_prepare_inode_for_write(OCFS2_I(inode)->ip_blkno,
2361: pos, count, wait);
2362:
2363: ocfs2_inode_unlock_for_extent_tree(inode,
2364: &di_bh,
2365: meta_level,
2366: write_sem);
2367:
2368: out:
2369: return ret;
2370: }
SourceFile:fs/ocfs2/file.c]
|
| 90/2 |
2026/05/22 01:49 |
llm |
expert |
0m
Model:
Tokens:
input: 103184
output: 50
thoughts: 402
Thoughts: **Debugging a Potential OCFS2 Corruption**
Okay, let's break this down. My initial thought was that a corrupted `file_out` inode was causing this whole chain reaction. We know `ocfs2_prepare_inode_for_write` calls `ocfs2_inode_lock_for_extent_tree`, which then calls the nested locking function `ocfs2_inode_lock_full_nested`. If `ocfs2_inode_lock_full_nested` fails because of a corrupted `file_out`, then `ocfs2_prepare_inode_for_write` returns an error. This error propagates all the way back up to `ocfs2_file_write_iter`, right? And if that happens, we never even get to `__generic_file_write_iter`, which means we never hit `generic_perform_write`, and ultimately `ocfs2_write_begin`. So, if the write operation *never even begins*, then `file_out` *can't* be the corrupted one!
But if *neither* `file_in` *nor* `file_out` are the corrupted inode, then where's the problem? The bug report is providing crucial information:
```
(syz.0.0,5320,0):ocfs2_assign_bh:2417 ERROR: status = -30
(syz.0.0,5320,0):ocfs2_inode_lock_full_nested:2512 ERROR: status = -30
(syz.0.0,5320,0):ocfs2_reflink_inodes_lock:4747 ERROR: status = -30
```
That "-30" status is the key. The log mentions that the issue is inside `ocfs2_reflink_inodes_lock`. And this function locks *two* inodes: `s_inode` and `t_inode`. Where is `ocfs2_reflink_inodes_lock` called? From `ocfs2_reflink`! Okay, let's dive into `ocfs2_reflink_inodes_lock` to understand *why* those locks are failing. It looks like the corruption is related to reflink operation.
|
| 91/2 |
2026/05/22 01:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/refcounttree.c IncludeLines:true Name:ocfs2_reflink_inodes_lock]
Results: map[SourceCode:4697: /* Lock an inode and grab a bh pointing to the inode. */
4698: int ocfs2_reflink_inodes_lock(struct inode *s_inode,
4699: struct buffer_head **bh_s,
4700: struct inode *t_inode,
4701: struct buffer_head **bh_t)
4702: {
4703: struct inode *inode1 = s_inode;
4704: struct inode *inode2 = t_inode;
4705: struct ocfs2_inode_info *oi1;
4706: struct ocfs2_inode_info *oi2;
4707: struct buffer_head *bh1 = NULL;
4708: struct buffer_head *bh2 = NULL;
4709: bool same_inode = (s_inode == t_inode);
4710: bool need_swap = (inode1->i_ino > inode2->i_ino);
4711: int status;
4712:
4713: /* First grab the VFS and rw locks. */
4714: lock_two_nondirectories(s_inode, t_inode);
4715: if (need_swap)
4716: swap(inode1, inode2);
4717:
4718: status = ocfs2_rw_lock(inode1, 1);
4719: if (status) {
4720: mlog_errno(status);
4721: goto out_i1;
4722: }
4723: if (!same_inode) {
4724: status = ocfs2_rw_lock(inode2, 1);
4725: if (status) {
4726: mlog_errno(status);
4727: goto out_i2;
4728: }
4729: }
4730:
4731: /* Now go for the cluster locks */
4732: oi1 = OCFS2_I(inode1);
4733: oi2 = OCFS2_I(inode2);
4734:
4735: trace_ocfs2_double_lock((unsigned long long)oi1->ip_blkno,
4736: (unsigned long long)oi2->ip_blkno);
4737:
4738: /* We always want to lock the one with the lower lockid first. */
4739: if (oi1->ip_blkno > oi2->ip_blkno)
4740: mlog_errno(-ENOLCK);
4741:
4742: /* lock id1 */
4743: status = ocfs2_inode_lock_nested(inode1, &bh1, 1,
4744: OI_LS_REFLINK_TARGET);
4745: if (status < 0) {
4746: if (status != -ENOENT)
4747: mlog_errno(status);
4748: goto out_rw2;
4749: }
4750:
4751: /* lock id2 */
4752: if (!same_inode) {
4753: status = ocfs2_inode_lock_nested(inode2, &bh2, 1,
4754: OI_LS_REFLINK_TARGET);
4755: if (status < 0) {
4756: if (status != -ENOENT)
4757: mlog_errno(status);
4758: goto out_cl1;
4759: }
4760: } else {
4761: bh2 = bh1;
4762: }
4763:
4764: /*
4765: * If we swapped inode order above, we have to swap the buffer heads
4766: * before passing them back to the caller.
4767: */
4768: if (need_swap)
4769: swap(bh1, bh2);
4770: *bh_s = bh1;
4771: *bh_t = bh2;
4772:
4773: trace_ocfs2_double_lock_end(
4774: (unsigned long long)oi1->ip_blkno,
4775: (unsigned long long)oi2->ip_blkno);
4776:
4777: return 0;
4778:
4779: out_cl1:
4780: ocfs2_inode_unlock(inode1, 1);
4781: brelse(bh1);
4782: out_rw2:
4783: ocfs2_rw_unlock(inode2, 1);
4784: out_i2:
4785: ocfs2_rw_unlock(inode1, 1);
4786: out_i1:
4787: unlock_two_nondirectories(s_inode, t_inode);
4788: return status;
4789: }
SourceFile:fs/ocfs2/refcounttree.c]
|
| 92/2 |
2026/05/22 01:50 |
llm |
expert |
0m
Model:
Tokens:
input: 105554
output: 50
thoughts: 1282
Thoughts: **Debugging a Read-Only Filesystem Due to Inode Corruption**
Okay, let's break this down. I see that `ocfs2_reflink_inodes_lock` is failing with `-EROFS`, which is `-30`. But why? The trace points to `-30` originating from `ocfs2_assign_bh`, which is itself a result of `ocfs2_read_inode_block`, which is called by `ocfs2_inode_lock_update` in the `ocfs2_inode_lock_nested` function. If `ocfs2_inode_lock_nested` fails, it implies an inode corruption, and I was confused that the `do_splice_direct` operation could still be happening if it encounters a bad inode.
Wait a minute, could the filesystem *already* be read-only? If it were read-only due to a *previous* error, `ocfs2_inode_lock_nested` *would* return `-EROFS`, but `ocfs2_is_hard_readonly` wouldn't return immediately TRUE, instead `ocfs2_handle_error` would set `OCFS2_OSB_SOFT_RO` (and `ocfs2_is_hard_readonly` *would* return FALSE). Thus, my understanding of how a filesystem goes read-only was incorrect. `ocfs2_read_inode_block` only fails if the filesystem is read-only because of something else.
But that still doesn't explain the `-30`! The error trace clearly shows `-30` flowing through `ocfs2_reflink_inodes_lock` -> `ocfs2_inode_lock_full_nested` -> `ocfs2_assign_bh`. I remember `ocfs2_assign_bh` calls `ocfs2_read_inode_block`, which calls `ocfs2_validate_inode_block`. Aha! `ocfs2_validate_inode_block` *can* return `-30` if it calls `ocfs2_error` which *itself* returns `-EROFS`. Let's see: `ocfs2_validate_inode_block` checks the inode and calls `ocfs2_error` if it's invalid. This happens when the inode's link count is zero *and* the mode is zero, or if the signature is invalid. **Bingo!**
So, the validation of one of the inodes (`file_in` or `file_out`) is failing! It *has* to be returning `-30` from the point of view of `ocfs2_reflink_inodes_lock`. `ocfs2_assign_bh` is returning `-30` because `ocfs2_read_inode_block` is returning `-30` because `ocfs2_validate_inode_block` is returning `-30`!
Now, if `file_in` is the corrupted inode, and `do_splice_direct` *tries* to read from `file_in`, `ocfs2_file_splice_read` will call `ocfs2_inode_lock_atime`. `ocfs2_inode_lock_atime` then calls `ocfs2_inode_lock_full_nested`, which - just like before - calls `ocfs2_assign_bh`! But, if I'm not mistaken, because the `ret_bh` parameter is `NULL` that means `ocfs2_assign_bh` won't be called, so the `atime` lock won't fail with the same error.
The overall picture is that the filesystem is going read-only because a file is corrupted.
|
| 93/2 |
2026/05/22 01:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/dlmglue.c IncludeLines:true Name:ocfs2_inode_lock_atime]
Results: map[SourceCode:2570: int ocfs2_inode_lock_atime(struct inode *inode,
2571: struct vfsmount *vfsmnt,
2572: int *level, int wait)
2573: {
2574: int ret;
2575:
2576: if (wait)
2577: ret = ocfs2_inode_lock(inode, NULL, 0);
2578: else
2579: ret = ocfs2_try_inode_lock(inode, NULL, 0);
2580:
2581: if (ret < 0) {
2582: if (ret != -EAGAIN)
2583: mlog_errno(ret);
2584: return ret;
2585: }
2586:
2587: /*
2588: * If we should update atime, we will get EX lock,
2589: * otherwise we just get PR lock.
2590: */
2591: if (ocfs2_should_update_atime(inode, vfsmnt)) {
2592: struct buffer_head *bh = NULL;
2593:
2594: ocfs2_inode_unlock(inode, 0);
2595: if (wait)
2596: ret = ocfs2_inode_lock(inode, &bh, 1);
2597: else
2598: ret = ocfs2_try_inode_lock(inode, &bh, 1);
2599:
2600: if (ret < 0) {
2601: if (ret != -EAGAIN)
2602: mlog_errno(ret);
2603: return ret;
2604: }
2605: *level = 1;
2606: if (ocfs2_should_update_atime(inode, vfsmnt))
2607: ocfs2_update_inode_atime(inode, bh);
2608: brelse(bh);
2609: } else
2610: *level = 0;
2611:
2612: return ret;
2613: }
SourceFile:fs/ocfs2/dlmglue.c]
|
| 94/2 |
2026/05/22 01:50 |
llm |
expert |
0m
Model:
Tokens:
input: 108338
output: 46
thoughts: 1661
Thoughts: **Deep Dive into OCFS2 Inode Locking and Corruption**
Okay, so I'm looking at this `ocfs2_inode_lock` function, and the first thing that jumps out is that sometimes it's called with `ret_bh = NULL`. Specifically, `ocfs2_inode_lock(inode, NULL, 0)`. This immediately triggers a mental check on how that plays out in `ocfs2_inode_lock_full_nested`.
When `ret_bh` is NULL, it *doesn't* call `ocfs2_assign_bh`. That's key. Because if it doesn't call `ocfs2_assign_bh`, it *also* doesn't call `ocfs2_read_inode_block`. That means `ocfs2_inode_lock_atime` can *succeed* even if the inode on disk is corrupted! Wow, that's a potential vulnerability.
So, here's the scenario: if `file_in` is a corrupted inode, `ocfs2_file_splice_read` could still succeed because of the `ret_bh = NULL` path. Then, `do_splice_direct` will read from `file_in`. But, the read happens from the page cache, which is fine because the corruption is on disk. Now it writes to `file_out`. If `file_out` isn't corrupted, `ocfs2_write_begin` works.
But hold on, if `file_out` *isn't* corrupted, it's `id_count` is valid (and <= 4096). So `ocfs2_size_fits_inline_data` will return false if `new_size > 4096`. This would mean `pos + *copied` would also be <= 4096. No `VM_BUG_ON`! So if everything I've said so far is true, *`file_out` must be the corrupted inode*.
Wait, if `file_out` *is* corrupted, `ocfs2_reflink_inodes_lock` fails when locking it. So, we've got the following: `ocfs2_reflink_inodes_lock` fails. `do_splice_direct` reads from `file_in` (succeeds) and it tries to write to `file_out`. Now, the crucial part: in writing to `file_out`, `ocfs2_write_begin` calls `ocfs2_inode_lock(inode, &di_bh, 1)`. Ah, but in this case, `&di_bh` is NOT NULL, so it *will* call `ocfs2_assign_bh`.
Which means `ocfs2_assign_bh` *will* call `ocfs2_read_inode_block`. And `ocfs2_read_inode_block` will call `ocfs2_validate_inode_block`. And *that* should fail with `-30`. Which means `ocfs2_write_begin` should fail with `-30`. So `generic_perform_write` should break and NOT call `ocfs2_write_end`. But the call trace *shows* `ocfs2_write_end` being called! How is *that* possible? Something is amiss in my reasoning.
Let's re-examine `ocfs2_assign_bh`. If `passed_bh` isn't NULL, it returns 0. Where does `passed_bh` come from? It's `local_bh` in `ocfs2_inode_lock_full_nested`. And `local_bh` is populated by `ocfs2_inode_lock_update`. Now in `ocfs2_inode_lock_update`, there's the `ocfs2_meta_lvb_is_trustable` check.
If that check is TRUE, it trusts the LVB, and it *doesn't* go to disk. It calls `ocfs2_refresh_inode_from_lvb` instead. So `local_bh` remains NULL! Then `ocfs2_assign_bh` is called with `passed_bh = NULL`. And if `passed_bh` is NULL, `ocfs2_read_inode_block` gets called! And if the inode is corrupted, that will FAIL.
But what if `ocfs2_meta_lvb_is_trustable` is FALSE? Then `ocfs2_inode_lock_update` *does* call `ocfs2_read_inode_block(inode, &local_bh)
|
| 95/2 |
2026/05/22 01:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_read_folio]
Results: map[SourceCode: 264: static int ocfs2_read_folio(struct file *file, struct folio *folio)
265: {
266: struct inode *inode = folio->mapping->host;
267: struct ocfs2_inode_info *oi = OCFS2_I(inode);
268: loff_t start = folio_pos(folio);
269: int ret, unlock = 1;
270:
271: trace_ocfs2_readpage((unsigned long long)oi->ip_blkno, folio->index);
272:
273: ret = ocfs2_inode_lock_with_folio(inode, NULL, 0, folio);
274: if (ret != 0) {
275: if (ret == AOP_TRUNCATED_PAGE)
276: unlock = 0;
277: mlog_errno(ret);
278: goto out;
279: }
280:
281: if (down_read_trylock(&oi->ip_alloc_sem) == 0) {
282: /*
283: * Unlock the folio and cycle ip_alloc_sem so that we don't
284: * busyloop waiting for ip_alloc_sem to unlock
285: */
286: ret = AOP_TRUNCATED_PAGE;
287: folio_unlock(folio);
288: unlock = 0;
289: down_read(&oi->ip_alloc_sem);
290: up_read(&oi->ip_alloc_sem);
291: goto out_inode_unlock;
292: }
293:
294: /*
295: * i_size might have just been updated as we grabbed the meta lock. We
296: * might now be discovering a truncate that hit on another node.
297: * block_read_full_folio->get_block freaks out if it is asked to read
298: * beyond the end of a file, so we check here. Callers
299: * (generic_file_read, vm_ops->fault) are clever enough to check i_size
300: * and notice that the folio they just read isn't needed.
301: *
302: * XXX sys_readahead() seems to get that wrong?
303: */
304: if (start >= i_size_read(inode)) {
305: folio_zero_segment(folio, 0, folio_size(folio));
306: folio_mark_uptodate(folio);
307: ret = 0;
308: goto out_alloc;
309: }
310:
311: if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL)
312: ret = ocfs2_readpage_inline(inode, folio);
313: else
314: ret = block_read_full_folio(folio, ocfs2_get_block);
315: unlock = 0;
316:
317: out_alloc:
318: up_read(&oi->ip_alloc_sem);
319: out_inode_unlock:
320: ocfs2_inode_unlock(inode, 0);
321: out:
322: if (unlock)
323: folio_unlock(folio);
324: return ret;
325: }
SourceFile:fs/ocfs2/aops.c]
|
| 96/2 |
2026/05/22 01:50 |
llm |
expert |
0m
Model:
Tokens:
input: 112084
output: 47
thoughts: 100
Thoughts: **Analyzing the OCFS2 Read Path**
Okay, so `ocfs2_read_folio` is the entry point, and it's calling `ocfs2_inode_lock_with_folio`. Interesting β it passes `ret_bh = NULL`. That's important. It means this path *isn't* using `ocfs2_assign_bh`. Consequently, that means this path can't fail based on `ocfs2_assign_bh` issues. Good, that simplifies things a bit. So, next step in `ocfs2_read_folio` is `ocfs2_readpage_inline`. Now, I need to understand what's happening *inside* that function. Let's dig into `ocfs2_readpage_inline` and see what it does.
|
| 97/2 |
2026/05/22 01:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/aops.c IncludeLines:true Name:ocfs2_readpage_inline]
Results: map[SourceCode: 242: static int ocfs2_readpage_inline(struct inode *inode, struct folio *folio)
243: {
244: int ret;
245: struct buffer_head *di_bh = NULL;
246:
247: BUG_ON(!folio_test_locked(folio));
248: BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL));
249:
250: ret = ocfs2_read_inode_block(inode, &di_bh);
251: if (ret) {
252: mlog_errno(ret);
253: goto out;
254: }
255:
256: ret = ocfs2_read_inline_data(inode, folio, di_bh);
257: out:
258: folio_unlock(folio);
259:
260: brelse(di_bh);
261: return ret;
262: }
SourceFile:fs/ocfs2/aops.c]
|
| 98/2 |
2026/05/22 01:50 |
llm |
expert |
1m
Model:
Tokens:
input: 112739
output: 17
thoughts: 9055
Thoughts: **Analysis of OCFS2 File Corruption Vulnerability**
Okay, so I've been tracing through this code, and it all boils down to a nasty heap out-of-bounds write due to a corrupted inline data count within the OCFS2 file system. Here's what I've figured out:
1. **The Core Problem:** The `VM_BUG_ON(offset + len > folio_size(folio))` in `ocfs2_write_end_inline` is being triggered, leading to an out-of-bounds write. This happens when we try to write inline data to a file. The key lies in the `memcpy_from_folio` function.
2. **Inline Data and `id_count`:** OCFS2 uses inline data for small files to store content directly in the inode block. The `id_count` field within the inode's inline data structure dictates how much data is *valid* in that inline section. The vulnerability exists when this `id_count` field is corrupted.
3. **The Validation Bypass:** Normally, when OCFS2 reads an inode block, it validates it using `ocfs2_validate_inode_block`. This function is supposed to check the `id_count` against a calculated maximum based on the block size and other metadata. **However, and this is crucial, `ocfs2_validate_inode_block` is bypassed if the inode block is already present and marked as "uptodate" in the block device's page cache!** This is where the vulnerability becomes exploitable.
4. **Buffer Cache Poisoning:** This is where syzkaller is making us suffer. A malicious actor with root privileges (or the ability to create the block device) can write a corrupted inode block with an invalid `id_count` (e.g., 65535 when it should be less than 4096) directly to the block device. This poisoned block is then loaded into the block device's page cache and marked as "uptodate".
5. **The Trigger:** When OCFS2 then *later* needs to write to this file, it grabs the already cached poisoned block. Because it is "uptodate", it *skips* validation, trusts the corrupted `id_count` and believes it's free to write a huge block of data. Then `ocfs2_write_begin_inline` is called which, if `id_count` is corrupted to a value larger than 4096, will write the corrupted `id_count` to the file. Then, in the function `ocfs2_write_end_inline`, `memcpy_from_folio` is called, copying more data than there's space for in the inline data section and writes past the end of the buffer and causing the out of bounds write and subsequent crash.
6. **The Security Implications:** This is a privilege escalation vulnerability. It requires someone with root or `CAP_SYS_ADMIN` access to write to the block device. It is not possible for an unprivileged user to exploit this. A malicious filesystem image is not sufficient to exploit this, as the validation would fail upon the first read.
7. **Key Steps Summarized:**
* A malicious user writes an inode with a corrupted `id_count` to `/dev/loop0`.
* The poisoned inode block is read into the block device's cache and marked "uptodate".
* OCFS2 *later* attempts to write to this file.
* The "uptodate" block is retrieved from the cache and validation is skipped.
* The `id_count` is read as a high number.
* `ocfs2_size_fits_inline_data` is bypassed.
* A subsequent write to the inline data area leads to a heap out-of-bounds write.
In short, it's a classic buffer cache poisoning scenario that leverages OCFS2's trust in cached, "uptodate" blocks to inject corrupted metadata.
|
| 99/2 |
2026/05/22 01:52 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 3286 lines.
Use more precise expression if possible.
[Documentation/bpf/bpf_iterators.rst=230=Here is a snippet from the ``bpf_iter_task_file.c`` file:
--
Documentation/bpf/bpf_iterators.rst-254- last_tgid = task->tgid;
Documentation/bpf/bpf_iterators.rst:255: unique_tgid_count++;
Documentation/bpf/bpf_iterators.rst-256- }
--
arch/loongarch/kernel/perf_event.c=130=struct loongarch_pmu {
arch/loongarch/kernel/perf_event.c-131- u64 max_period;
arch/loongarch/kernel/perf_event.c:132: u64 valid_count;
arch/loongarch/kernel/perf_event.c-133- u64 overflow;
--
arch/loongarch/kernel/perf_event.c=861=static int __init init_hw_perf_events(void)
--
arch/loongarch/kernel/perf_event.c-873- loongarch_pmu.max_period = (1ULL << 63) - 1;
arch/loongarch/kernel/perf_event.c:874: loongarch_pmu.valid_count = (1ULL << 63) - 1;
arch/loongarch/kernel/perf_event.c-875- loongarch_pmu.overflow = 1ULL << 63;
--
arch/mips/kernel/perf_event_mipsxx.c=76=struct mips_pmu {
arch/mips/kernel/perf_event_mipsxx.c-77- u64 max_period;
arch/mips/kernel/perf_event_mipsxx.c:78: u64 valid_count;
arch/mips/kernel/perf_event_mipsxx.c-79- u64 overflow;
--
arch/mips/kernel/perf_event_mipsxx.c=1890=init_hw_perf_events(void)
--
arch/mips/kernel/perf_event_mipsxx.c-2025- mipspmu.max_period = (1ULL << 47) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2026: mipspmu.valid_count = (1ULL << 47) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2027- mipspmu.overflow = 1ULL << 47;
--
arch/mips/kernel/perf_event_mipsxx.c-2030- mipspmu.max_period = (1ULL << 63) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2031: mipspmu.valid_count = (1ULL << 63) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2032- mipspmu.overflow = 1ULL << 63;
--
arch/mips/kernel/perf_event_mipsxx.c-2038- mipspmu.max_period = (1ULL << 31) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2039: mipspmu.valid_count = (1ULL << 31) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2040- mipspmu.overflow = 1ULL << 31;
--
arch/powerpc/include/asm/pnv-ocxl.h=59=int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, u16 *supported);
arch/powerpc/include/asm/pnv-ocxl.h:60:int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count);
arch/powerpc/include/asm/pnv-ocxl.h-61-
--
arch/powerpc/platforms/powernv/ocxl.c=276=EXPORT_SYMBOL_GPL(pnv_ocxl_get_actag);
arch/powerpc/platforms/powernv/ocxl.c-277-
arch/powerpc/platforms/powernv/ocxl.c:278:int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count)
arch/powerpc/platforms/powernv/ocxl.c-279-{
--
arch/powerpc/platforms/powernv/ocxl.c-310-}
arch/powerpc/platforms/powernv/ocxl.c:311:EXPORT_SYMBOL_GPL(pnv_ocxl_get_pasid_count);
arch/powerpc/platforms/powernv/ocxl.c-312-
--
arch/x86/boot/compressed/mem.c=15=static bool early_is_tdx_guest(void)
--
arch/x86/boot/compressed/mem.c-25-
arch/x86/boot/compressed/mem.c:26: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax,
arch/x86/boot/compressed/mem.c-27- &sig[0], &sig[2], &sig[1]);
--
arch/x86/boot/compressed/tdx.c=64=void early_tdx_detect(void)
--
arch/x86/boot/compressed/tdx.c-67-
arch/x86/boot/compressed/tdx.c:68: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2], &sig[1]);
arch/x86/boot/compressed/tdx.c-69-
--
arch/x86/boot/cpuflags.c=37=bool has_eflag(unsigned long mask)
--
arch/x86/boot/cpuflags.c-57-
arch/x86/boot/cpuflags.c:58:void cpuid_count(u32 id, u32 count, u32 *a, u32 *b, u32 *c, u32 *d)
arch/x86/boot/cpuflags.c-59-{
--
arch/x86/boot/cpuflags.c-65-
arch/x86/boot/cpuflags.c:66:#define cpuid(id, a, b, c, d) cpuid_count(id, 0, a, b, c, d)
arch/x86/boot/cpuflags.c-67-
arch/x86/boot/cpuflags.c=68=void get_cpuflags(void)
--
arch/x86/boot/cpuflags.c-96- if (max_intel_level >= 0x00000007) {
arch/x86/boot/cpuflags.c:97: cpuid_count(0x00000007, 0, &ignored, &ignored,
arch/x86/boot/cpuflags.c-98- &cpu.flags[16], &ignored);
--
arch/x86/boot/cpuflags.h=23=void get_cpuflags(void);
arch/x86/boot/cpuflags.h:24:void cpuid_count(u32 id, u32 count, u32 *a, u32 *b, u32 *c, u32 *d);
arch/x86/boot/cpuflags.h-25-bool has_cpuflag(int flag);
--
arch/x86/coco/tdx/tdx.c=1113=void __init tdx_early_init(void)
--
arch/x86/coco/tdx/tdx.c-1117-
arch/x86/coco/tdx/tdx.c:1118: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2], &sig[1]);
arch/x86/coco/tdx/tdx.c-1119-
--
arch/x86/events/intel/core.c=5928=static void update_pmu_cap(struct pmu *pmu)
--
arch/x86/events/intel/core.c-5946- if (eax_0.split.cntr_subleaf) {
arch/x86/events/intel/core.c:5947: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF,
arch/x86/events/intel/core.c-5948- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5954- if (eax_0.split.acr_subleaf) {
arch/x86/events/intel/core.c:5955: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_ACR_LEAF,
arch/x86/events/intel/core.c-5956- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5964- if (eax_0.split.pebs_caps_subleaf && eax_0.split.pebs_cnts_subleaf) {
arch/x86/events/intel/core.c:5965: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_CAP_LEAF,
arch/x86/events/intel/core.c-5966- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5968-
arch/x86/events/intel/core.c:5969: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_COUNTER_LEAF,
arch/x86/events/intel/core.c-5970- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/pt.c=190=static int __init pt_pmu_hw_init(void)
--
arch/x86/events/intel/pt.c-240- for (i = 0; i < PT_CPUID_LEAVES; i++) {
arch/x86/events/intel/pt.c:241: cpuid_count(20, i,
arch/x86/events/intel/pt.c-242- &pt_pmu.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM],
--
arch/x86/include/asm/cpuid/api.h=68=static inline void cpuid(u32 op,
--
arch/x86/include/asm/cpuid/api.h-77-/* Some CPUID calls want 'count' to be placed in ECX */
arch/x86/include/asm/cpuid/api.h:78:static inline void cpuid_count(u32 op, int count,
arch/x86/include/asm/cpuid/api.h-79- u32 *eax, u32 *ebx,
--
arch/x86/include/asm/user.h=18=struct user_xstate_header {
--
arch/x86/include/asm/user.h-31- * obtained by doing:
arch/x86/include/asm/user.h:32: * cpuid_count(0xd, 0, &eax, &ptrace_xstateregs_struct_size, &ecx, &edx);
arch/x86/include/asm/user.h-33- * i.e., cpuid.(eax=0xd,ecx=0).ebx will be the size that user (debuggers, etc.)
--
arch/x86/kernel/cpu/cacheinfo.c=240=static int amd_fill_cpuid4_info(int index, struct _cpuid4_info *id4)
--
arch/x86/kernel/cpu/cacheinfo.c-247- if (boot_cpu_has(X86_FEATURE_TOPOEXT) || boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
arch/x86/kernel/cpu/cacheinfo.c:248: cpuid_count(0x8000001d, index, &eax.full, &ebx.full, &ecx.full, &ignored);
arch/x86/kernel/cpu/cacheinfo.c-249- else
--
arch/x86/kernel/cpu/cacheinfo.c=255=static int intel_fill_cpuid4_info(int index, struct _cpuid4_info *id4)
--
arch/x86/kernel/cpu/cacheinfo.c-261-
arch/x86/kernel/cpu/cacheinfo.c:262: cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &ignored);
arch/x86/kernel/cpu/cacheinfo.c-263-
--
arch/x86/kernel/cpu/cacheinfo.c=276=static int find_num_cache_leaves(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/cacheinfo.c-285- ++i;
arch/x86/kernel/cpu/cacheinfo.c:286: cpuid_count(op, i, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/cacheinfo.c-287- cache_eax.full = eax;
--
arch/x86/kernel/cpu/common.c=1019=void get_cpu_cap(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/common.c-1036- if (c->cpuid_level >= 0x00000007) {
arch/x86/kernel/cpu/common.c:1037: cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1038- c->x86_capability[CPUID_7_0_EBX] = ebx;
--
arch/x86/kernel/cpu/common.c-1043- if (eax >= 1) {
arch/x86/kernel/cpu/common.c:1044: cpuid_count(0x00000007, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1045- c->x86_capability[CPUID_7_1_EAX] = eax;
--
arch/x86/kernel/cpu/common.c-1050- if (c->cpuid_level >= 0x0000000d) {
arch/x86/kernel/cpu/common.c:1051: cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1052-
--
arch/x86/kernel/cpu/resctrl/core.c=204=static __init bool __get_mem_config_intel(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-210-
arch/x86/kernel/cpu/resctrl/core.c:211: cpuid_count(0x00000010, 3, &eax.full, &ebx, &ecx, &edx.full);
arch/x86/kernel/cpu/resctrl/core.c-212- hw_res->num_closid = edx.split.cos_max + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=236=static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-246-
arch/x86/kernel/cpu/resctrl/core.c:247: cpuid_count(0x80000020, subleaf, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/core.c-248- hw_res->num_closid = edx + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=268=static void rdt_get_cache_alloc_cfg(int idx, struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-275-
arch/x86/kernel/cpu/resctrl/core.c:276: cpuid_count(0x00000010, idx, &eax.full, &ebx, &ecx.full, &edx.full);
arch/x86/kernel/cpu/resctrl/core.c-277- hw_res->num_closid = edx.split.cos_max + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=1075=void resctrl_cpu_detect(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/resctrl/core.c-1093- /* QoS sub-leaf, EAX=0Fh, ECX=1 */
arch/x86/kernel/cpu/resctrl/core.c:1094: cpuid_count(0xf, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/core.c-1095-
--
arch/x86/kernel/cpu/resctrl/monitor.c=405=int __init rdt_get_l3_mon_config(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/monitor.c-441- /* Detect list of bandwidth sources that can be tracked */
arch/x86/kernel/cpu/resctrl/monitor.c:442: cpuid_count(0x80000020, 3, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/monitor.c-443- r->mon.mbm_cfg_mask = ecx & MAX_EVT_CONFIG_BITS;
--
arch/x86/kernel/cpu/resctrl/monitor.c-456- r->mon.mbm_cntr_assignable = true;
arch/x86/kernel/cpu/resctrl/monitor.c:457: cpuid_count(0x80000020, 5, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/monitor.c-458- r->mon.num_mbm_cntrs = (ebx & GENMASK(15, 0)) + 1;
--
arch/x86/kernel/cpu/scattered.c=73=void init_scattered_cpuid_features(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/scattered.c-86-
arch/x86/kernel/cpu/scattered.c:87: cpuid_count(cb->level, cb->sub_leaf, ®s[CPUID_EAX],
arch/x86/kernel/cpu/scattered.c-88- ®s[CPUID_EBX], ®s[CPUID_ECX],
--
arch/x86/kernel/cpu/sgx/driver.c=163=int __init sgx_drv_init(void)
--
arch/x86/kernel/cpu/sgx/driver.c-174-
arch/x86/kernel/cpu/sgx/driver.c:175: cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/driver.c-176-
--
arch/x86/kernel/cpu/sgx/driver.c-183-
arch/x86/kernel/cpu/sgx/driver.c:184: cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/driver.c-185-
--
arch/x86/kernel/cpu/sgx/main.c=794=static bool __init sgx_page_cache_init(void)
--
arch/x86/kernel/cpu/sgx/main.c-805- for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) {
arch/x86/kernel/cpu/sgx/main.c:806: cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/main.c-807-
--
arch/x86/kernel/cpuid.c=50=static void cpuid_smp_cpuid(void *cmd_block)
--
arch/x86/kernel/cpuid.c-53-
arch/x86/kernel/cpuid.c:54: cpuid_count(cmd->regs.eax, cmd->regs.ecx,
arch/x86/kernel/cpuid.c-55- &cmd->regs.eax, &cmd->regs.ebx,
--
arch/x86/kernel/fpu/xstate.c=254=static void __init setup_xstate_cache(void)
--
arch/x86/kernel/fpu/xstate.c-270- for_each_extended_xfeature(xfeature, fpu_kernel_cfg.max_features) {
arch/x86/kernel/fpu/xstate.c:271: cpuid_count(CPUID_LEAF_XSTATE, xfeature, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-272-
--
arch/x86/kernel/fpu/xstate.c=421=int xfeature_size(int xfeature_nr)
--
arch/x86/kernel/fpu/xstate.c-425- CHECK_XFEATURE(xfeature_nr);
arch/x86/kernel/fpu/xstate.c:426: cpuid_count(CPUID_LEAF_XSTATE, xfeature_nr, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-427- return eax;
--
arch/x86/kernel/fpu/xstate.c=455=static void __init __xstate_dump_leaves(void)
--
arch/x86/kernel/fpu/xstate.c-468- for (i = 0; i < XFEATURE_MAX + 10; i++) {
arch/x86/kernel/fpu/xstate.c:469: cpuid_count(CPUID_LEAF_XSTATE, i, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-470- pr_warn("CPUID[%02x, %02x]: eax=%08x ebx=%08x ecx=%08x edx=%08x\n",
--
arch/x86/kernel/fpu/xstate.c=502=static int __init check_xtile_data_against_struct(int size)
--
arch/x86/kernel/fpu/xstate.c-511- */
arch/x86/kernel/fpu/xstate.c:512: cpuid_count(CPUID_LEAF_TILE, 0, &max_palid, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-513-
--
arch/x86/kernel/fpu/xstate.c-525- */
arch/x86/kernel/fpu/xstate.c:526: cpuid_count(CPUID_LEAF_TILE, palid, &eax, &ebx, &edx, &edx);
arch/x86/kernel/fpu/xstate.c-527- tile_size = eax >> 16;
--
arch/x86/kernel/fpu/xstate.c=655=static unsigned int __init get_compacted_size(void)
--
arch/x86/kernel/fpu/xstate.c-669- */
arch/x86/kernel/fpu/xstate.c:670: cpuid_count(CPUID_LEAF_XSTATE, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-671- return ebx;
--
arch/x86/kernel/fpu/xstate.c=701=static unsigned int __init get_xsave_size_user(void)
--
arch/x86/kernel/fpu/xstate.c-710- */
arch/x86/kernel/fpu/xstate.c:711: cpuid_count(CPUID_LEAF_XSTATE, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-712- return ebx;
--
arch/x86/kernel/fpu/xstate.c=806=void __init fpu__init_system_xstate(unsigned int legacy_size)
--
arch/x86/kernel/fpu/xstate.c-826- */
arch/x86/kernel/fpu/xstate.c:827: cpuid_count(CPUID_LEAF_XSTATE, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-828- fpu_kernel_cfg.max_features = eax + ((u64)edx << 32);
--
arch/x86/kernel/fpu/xstate.c-832- */
arch/x86/kernel/fpu/xstate.c:833: cpuid_count(CPUID_LEAF_XSTATE, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-834- fpu_kernel_cfg.max_features |= ecx + ((u64)edx << 32);
--
arch/x86/kvm/cpuid.c=50=void __init kvm_init_xstate_sizes(void)
--
arch/x86/kvm/cpuid.c-57-
arch/x86/kvm/cpuid.c:58: cpuid_count(0xD, i, &xs->eax, &xs->ebx, &xs->ecx, &ign);
arch/x86/kvm/cpuid.c-59- }
--
arch/x86/kvm/cpuid.c=675=static __always_inline u32 raw_cpuid_get(struct cpuid_reg cpuid)
--
arch/x86/kvm/cpuid.c-691-
arch/x86/kvm/cpuid.c:692: cpuid_count(cpuid.function, cpuid.index,
arch/x86/kvm/cpuid.c-693- &entry.eax, &entry.ebx, &entry.ecx, &entry.edx);
--
arch/x86/kvm/cpuid.c=1328=static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,
--
arch/x86/kvm/cpuid.c-1361-
arch/x86/kvm/cpuid.c:1362: cpuid_count(entry->function, entry->index,
arch/x86/kvm/cpuid.c-1363- &entry->eax, &entry->ebx, &entry->ecx, &entry->edx);
--
arch/x86/kvm/cpuid.c=1417=static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
--
arch/x86/kvm/cpuid.c-1421-
arch/x86/kvm/cpuid.c:1422: /* all calls to cpuid_count() should be made on the same cpu */
arch/x86/kvm/cpuid.c-1423- get_cpu();
--
arch/x86/kvm/svm/sev.c=3057=void __init sev_hardware_setup(void)
arch/x86/kvm/svm/sev.c-3058-{
arch/x86/kvm/svm/sev.c:3059: unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
arch/x86/kvm/svm/sev.c-3060- struct sev_platform_init_args init_args = {0};
--
arch/x86/kvm/svm/sev.c-3122- if (min_sev_asid <= max_sev_asid) {
arch/x86/kvm/svm/sev.c:3123: sev_asid_count = max_sev_asid - min_sev_asid + 1;
arch/x86/kvm/svm/sev.c:3124: WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV, sev_asid_count));
arch/x86/kvm/svm/sev.c-3125- }
--
arch/x86/kvm/svm/sev.c-3157-
arch/x86/kvm/svm/sev.c:3158: sev_es_asid_count = min_sev_asid - 1;
arch/x86/kvm/svm/sev.c:3159: WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV_ES, sev_es_asid_count));
arch/x86/kvm/svm/sev.c-3160- sev_es_supported = true;
--
arch/x86/kvm/vmx/sgx.c=440=static bool sgx_intercept_encls_ecreate(struct kvm_vcpu *vcpu)
--
arch/x86/kvm/vmx/sgx.c-451-
arch/x86/kvm/vmx/sgx.c:452: cpuid_count(0x12, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kvm/vmx/sgx.c-453- if (guest_cpuid->ebx != ebx || guest_cpuid->edx != edx)
--
arch/x86/kvm/vmx/sgx.c-459-
arch/x86/kvm/vmx/sgx.c:460: cpuid_count(0x12, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kvm/vmx/sgx.c-461- if (guest_cpuid->eax != eax || guest_cpuid->ebx != ebx ||
--
arch/x86/um/os-Linux/registers.c=48=int arch_init_registers(int pid)
--
arch/x86/um/os-Linux/registers.c-60-
arch/x86/um/os-Linux/registers.c:61: /* GDB has x86_xsave_length, which uses x86_cpuid_count */
arch/x86/um/os-Linux/registers.c-62- ptrace_regset = NT_X86_XSTATE;
--
arch/x86/xen/time.c=492=static int __init xen_tsc_safe_clocksource(void)
--
arch/x86/xen/time.c-505- /* Leaf 4, sub-leaf 0 (0x40000x03) */
arch/x86/xen/time.c:506: cpuid_count(xen_cpuid_base() + 3, 0, &eax, &ebx, &ecx, &edx);
arch/x86/xen/time.c-507-
--
drivers/accel/ivpu/ivpu_drv.c=214=static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
--
drivers/accel/ivpu/ivpu_drv.c-261- case DRM_IVPU_PARAM_UNIQUE_INFERENCE_ID:
drivers/accel/ivpu/ivpu_drv.c:262: args->value = (u64)atomic64_inc_return(&vdev->unique_id_counter);
drivers/accel/ivpu/ivpu_drv.c-263- break;
--
drivers/accel/ivpu/ivpu_drv.c=655=static int ivpu_dev_init(struct ivpu_device *vdev)
--
drivers/accel/ivpu/ivpu_drv.c-686- vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
drivers/accel/ivpu/ivpu_drv.c:687: atomic64_set(&vdev->unique_id_counter, 0);
drivers/accel/ivpu/ivpu_drv.c-688- atomic_set(&vdev->job_timeout_counter, 0);
--
drivers/accel/ivpu/ivpu_drv.h=135=struct ivpu_device {
--
drivers/accel/ivpu/ivpu_drv.h-173-
drivers/accel/ivpu/ivpu_drv.h:174: atomic64_t unique_id_counter;
drivers/accel/ivpu/ivpu_drv.h-175-
--
drivers/acpi/arm64/iort.c=343=static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
--
drivers/acpi/arm64/iort.c-360- if (rid_in < map->input_base ||
drivers/acpi/arm64/iort.c:361: (rid_in > map->input_base + map->id_count))
drivers/acpi/arm64/iort.c-362- return -ENXIO;
--
drivers/acpi/arm64/iort.c-383- /*
drivers/acpi/arm64/iort.c:384: * Due to confusion regarding the meaning of the id_count field (which
drivers/acpi/arm64/iort.c-385- * carries the number of IDs *minus 1*), we may have to disregard this
--
drivers/acpi/arm64/iort.c-388- */
drivers/acpi/arm64/iort.c:389: if (map->id_count > 0 && rid_in == map->input_base + map->id_count)
drivers/acpi/arm64/iort.c-390- return -EAGAIN;
--
drivers/acpi/arm64/iort.c=1072=static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
drivers/acpi/arm64/iort.c:1073: u32 id_count)
drivers/acpi/arm64/iort.c-1074-{
--
drivers/acpi/arm64/iort.c-1092- if (fwspec->ids[i] >= id_start &&
drivers/acpi/arm64/iort.c:1093: fwspec->ids[i] <= id_start + id_count)
drivers/acpi/arm64/iort.c-1094- return true;
--
drivers/acpi/arm64/iort.c=1100=static void iort_node_get_rmr_info(struct acpi_iort_node *node,
--
drivers/acpi/arm64/iort.c-1126- * and dev(if !NULL). If found, get the sids for the Node.
drivers/acpi/arm64/iort.c:1127: * Please note, id_count is equal to the number of IDs in the
drivers/acpi/arm64/iort.c-1128- * range minus one.
--
drivers/acpi/arm64/iort.c-1139- if (dev && !iort_rmr_has_dev(dev, map->output_base,
drivers/acpi/arm64/iort.c:1140: map->id_count))
drivers/acpi/arm64/iort.c-1141- continue;
--
drivers/acpi/arm64/iort.c-1144- sids = iort_rmr_alloc_sids(sids, num_sids, map->output_base,
drivers/acpi/arm64/iort.c:1145: map->id_count + 1);
drivers/acpi/arm64/iort.c-1146- if (!sids)
--
drivers/acpi/arm64/iort.c-1148-
drivers/acpi/arm64/iort.c:1149: num_sids += map->id_count + 1;
drivers/acpi/arm64/iort.c-1150- }
--
drivers/cpufreq/cppc_cpufreq.c=789=static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
--
drivers/cpufreq/cppc_cpufreq.c-805- /* Any of the associated CPPC regs is 0. */
drivers/cpufreq/cppc_cpufreq.c:806: goto out_invalid_counters;
drivers/cpufreq/cppc_cpufreq.c-807- else
--
drivers/cpufreq/cppc_cpufreq.c-814- if (!delivered_perf)
drivers/cpufreq/cppc_cpufreq.c:815: goto out_invalid_counters;
drivers/cpufreq/cppc_cpufreq.c-816-
--
drivers/cpufreq/cppc_cpufreq.c-818-
drivers/cpufreq/cppc_cpufreq.c:819:out_invalid_counters:
drivers/cpufreq/cppc_cpufreq.c-820- /*
--
drivers/firewire/core-topology.c=86=static inline struct fw_node *fw_node(struct list_head *l)
--
drivers/firewire/core-topology.c-97- */
drivers/firewire/core-topology.c:98:static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self_id_count,
drivers/firewire/core-topology.c-99- unsigned int generation)
--
drivers/firewire/core-topology.c-102- .cursor = sid,
drivers/firewire/core-topology.c:103: .quadlet_count = self_id_count,
drivers/firewire/core-topology.c-104- };
--
drivers/firewire/core-topology.c=440=static void update_topology_map(__be32 *buffer, size_t buffer_size, int root_node_id,
drivers/firewire/core-topology.c:441: const u32 *self_ids, int self_id_count)
drivers/firewire/core-topology.c-442-{
--
drivers/firewire/core-topology.c-448-
drivers/firewire/core-topology.c:449: *map++ = cpu_to_be32((self_id_count + 2) << 16);
drivers/firewire/core-topology.c-450- *map++ = cpu_to_be32(next_generation);
drivers/firewire/core-topology.c:451: *map++ = cpu_to_be32((node_count << 16) | self_id_count);
drivers/firewire/core-topology.c-452-
drivers/firewire/core-topology.c:453: while (self_id_count--)
drivers/firewire/core-topology.c-454- *map++ = cpu_to_be32p(self_ids++);
--
drivers/firewire/core-topology.c=459=void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
drivers/firewire/core-topology.c:460: int self_id_count, u32 *self_ids, bool bm_abdicate)
drivers/firewire/core-topology.c-461-{
--
drivers/firewire/core-topology.c-463-
drivers/firewire/core-topology.c:464: trace_bus_reset_handle(card->index, generation, node_id, bm_abdicate, self_ids, self_id_count);
drivers/firewire/core-topology.c-465-
--
drivers/firewire/core-topology.c-483-
drivers/firewire/core-topology.c:484: local_node = build_tree(card, self_ids, self_id_count, generation);
drivers/firewire/core-topology.c-485-
--
drivers/firewire/core-topology.c-503- update_topology_map(card->topology_map.buffer, sizeof(card->topology_map.buffer),
drivers/firewire/core-topology.c:504: card->root_node->node_id, self_ids, self_id_count);
drivers/firewire/core-topology.c-505- }
--
drivers/firewire/core.h=253=void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
drivers/firewire/core.h:254: int generation, int self_id_count, u32 *self_ids, bool bm_abdicate);
drivers/firewire/core.h-255-void fw_destroy_nodes(struct fw_card *card);
--
drivers/firewire/ohci-serdes-test.c-12-
drivers/firewire/ohci-serdes-test.c:13:static void test_self_id_count_register_deserialization(struct kunit *test)
drivers/firewire/ohci-serdes-test.c-14-{
--
drivers/firewire/ohci-serdes-test.c-16-
drivers/firewire/ohci-serdes-test.c:17: bool is_error = ohci1394_self_id_count_is_error(expected);
drivers/firewire/ohci-serdes-test.c:18: u8 generation = ohci1394_self_id_count_get_generation(expected);
drivers/firewire/ohci-serdes-test.c:19: u32 size = ohci1394_self_id_count_get_size(expected);
drivers/firewire/ohci-serdes-test.c-20-
--
drivers/firewire/ohci-serdes-test.c=107=static struct kunit_case ohci_serdes_test_cases[] = {
drivers/firewire/ohci-serdes-test.c:108: KUNIT_CASE(test_self_id_count_register_deserialization),
drivers/firewire/ohci-serdes-test.c-109- KUNIT_CASE(test_self_id_receive_buffer_deserialization),
--
drivers/firewire/ohci.c=1772=static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id,
drivers/firewire/ohci.c:1773: int self_id_count)
drivers/firewire/ohci.c-1774-{
--
drivers/firewire/ohci.c-1777-
drivers/firewire/ohci.c:1778: for (i = 0; i < self_id_count; i++) {
drivers/firewire/ohci.c-1779- u32 entry = ohci->self_id_buffer[i];
--
drivers/firewire/ohci.c=1790=static int detect_initiated_reset(struct fw_ohci *ohci, bool *is_initiated_reset)
--
drivers/firewire/ohci.c-1826- */
drivers/firewire/ohci.c:1827:static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
drivers/firewire/ohci.c-1828-{
--
drivers/firewire/ohci.c-1871-
drivers/firewire/ohci.c:1872: pos = get_self_id_pos(ohci, self_id, self_id_count);
drivers/firewire/ohci.c-1873- if (pos >= 0) {
--
drivers/firewire/ohci.c-1875- &(ohci->self_id_buffer[pos]),
drivers/firewire/ohci.c:1876: (self_id_count - pos) * sizeof(*ohci->self_id_buffer));
drivers/firewire/ohci.c-1877- ohci->self_id_buffer[pos] = self_id;
drivers/firewire/ohci.c:1878: self_id_count++;
drivers/firewire/ohci.c-1879- }
drivers/firewire/ohci.c:1880: return self_id_count;
drivers/firewire/ohci.c-1881-}
--
drivers/firewire/ohci.c=1883=static irqreturn_t handle_selfid_complete_event(int irq, void *data)
--
drivers/firewire/ohci.c-1885- struct fw_ohci *ohci = data;
drivers/firewire/ohci.c:1886: int self_id_count, generation, new_generation, i, j;
drivers/firewire/ohci.c-1887- u32 reg, quadlet;
--
drivers/firewire/ohci.c-1911- reg = reg_read(ohci, OHCI1394_SelfIDCount);
drivers/firewire/ohci.c:1912: if (ohci1394_self_id_count_is_error(reg)) {
drivers/firewire/ohci.c-1913- ohci_notice(ohci, "self ID receive error\n");
--
drivers/firewire/ohci.c-1924- */
drivers/firewire/ohci.c:1925: self_id_count = ohci1394_self_id_count_get_size(reg) >> 1;
drivers/firewire/ohci.c-1926-
drivers/firewire/ohci.c:1927: if (self_id_count > 252) {
drivers/firewire/ohci.c-1928- ohci_notice(ohci, "bad selfIDSize (%08x)\n", reg);
--
drivers/firewire/ohci.c-1935-
drivers/firewire/ohci.c:1936: for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
drivers/firewire/ohci.c-1937- u32 id = cond_le32_to_cpu(ohci->self_id[i], has_be_header_quirk(ohci));
]
]
|
| 100/2 |
2026/05/22 01:52 |
llm |
expert |
0m
Model:
Tokens:
input: 135485
output: 17
thoughts: 41
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into the `ocfs2_inline_data` Structure**
Hold on a second. I'm trying to figure out where this `id_count` variable resides, and it just clicked: it's part of the `struct ocfs2_inline_data` definition. Right, that makes sense given the context of what I'm looking at. Now that I have a specific field and a specific structure, the best way forward is to track how it's used. Time to get a quick overview of how this data member is used. Let me fire up a `grep` to quickly search the `fs/ocfs2/` directory for all instances of `id_count`. I want to see this member in action. That should give me the usage context I need to better understand the code around it.
|
| 101/2 |
2026/05/22 01:52 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 3286 lines.
Use more precise expression if possible.
[Documentation/bpf/bpf_iterators.rst=230=Here is a snippet from the ``bpf_iter_task_file.c`` file:
--
Documentation/bpf/bpf_iterators.rst-254- last_tgid = task->tgid;
Documentation/bpf/bpf_iterators.rst:255: unique_tgid_count++;
Documentation/bpf/bpf_iterators.rst-256- }
--
arch/loongarch/kernel/perf_event.c=130=struct loongarch_pmu {
arch/loongarch/kernel/perf_event.c-131- u64 max_period;
arch/loongarch/kernel/perf_event.c:132: u64 valid_count;
arch/loongarch/kernel/perf_event.c-133- u64 overflow;
--
arch/loongarch/kernel/perf_event.c=861=static int __init init_hw_perf_events(void)
--
arch/loongarch/kernel/perf_event.c-873- loongarch_pmu.max_period = (1ULL << 63) - 1;
arch/loongarch/kernel/perf_event.c:874: loongarch_pmu.valid_count = (1ULL << 63) - 1;
arch/loongarch/kernel/perf_event.c-875- loongarch_pmu.overflow = 1ULL << 63;
--
arch/mips/kernel/perf_event_mipsxx.c=76=struct mips_pmu {
arch/mips/kernel/perf_event_mipsxx.c-77- u64 max_period;
arch/mips/kernel/perf_event_mipsxx.c:78: u64 valid_count;
arch/mips/kernel/perf_event_mipsxx.c-79- u64 overflow;
--
arch/mips/kernel/perf_event_mipsxx.c=1890=init_hw_perf_events(void)
--
arch/mips/kernel/perf_event_mipsxx.c-2025- mipspmu.max_period = (1ULL << 47) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2026: mipspmu.valid_count = (1ULL << 47) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2027- mipspmu.overflow = 1ULL << 47;
--
arch/mips/kernel/perf_event_mipsxx.c-2030- mipspmu.max_period = (1ULL << 63) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2031: mipspmu.valid_count = (1ULL << 63) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2032- mipspmu.overflow = 1ULL << 63;
--
arch/mips/kernel/perf_event_mipsxx.c-2038- mipspmu.max_period = (1ULL << 31) - 1;
arch/mips/kernel/perf_event_mipsxx.c:2039: mipspmu.valid_count = (1ULL << 31) - 1;
arch/mips/kernel/perf_event_mipsxx.c-2040- mipspmu.overflow = 1ULL << 31;
--
arch/powerpc/include/asm/pnv-ocxl.h=59=int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, u16 *supported);
arch/powerpc/include/asm/pnv-ocxl.h:60:int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count);
arch/powerpc/include/asm/pnv-ocxl.h-61-
--
arch/powerpc/platforms/powernv/ocxl.c=276=EXPORT_SYMBOL_GPL(pnv_ocxl_get_actag);
arch/powerpc/platforms/powernv/ocxl.c-277-
arch/powerpc/platforms/powernv/ocxl.c:278:int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count)
arch/powerpc/platforms/powernv/ocxl.c-279-{
--
arch/powerpc/platforms/powernv/ocxl.c-310-}
arch/powerpc/platforms/powernv/ocxl.c:311:EXPORT_SYMBOL_GPL(pnv_ocxl_get_pasid_count);
arch/powerpc/platforms/powernv/ocxl.c-312-
--
arch/x86/boot/compressed/mem.c=15=static bool early_is_tdx_guest(void)
--
arch/x86/boot/compressed/mem.c-25-
arch/x86/boot/compressed/mem.c:26: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax,
arch/x86/boot/compressed/mem.c-27- &sig[0], &sig[2], &sig[1]);
--
arch/x86/boot/compressed/tdx.c=64=void early_tdx_detect(void)
--
arch/x86/boot/compressed/tdx.c-67-
arch/x86/boot/compressed/tdx.c:68: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2], &sig[1]);
arch/x86/boot/compressed/tdx.c-69-
--
arch/x86/boot/cpuflags.c=37=bool has_eflag(unsigned long mask)
--
arch/x86/boot/cpuflags.c-57-
arch/x86/boot/cpuflags.c:58:void cpuid_count(u32 id, u32 count, u32 *a, u32 *b, u32 *c, u32 *d)
arch/x86/boot/cpuflags.c-59-{
--
arch/x86/boot/cpuflags.c-65-
arch/x86/boot/cpuflags.c:66:#define cpuid(id, a, b, c, d) cpuid_count(id, 0, a, b, c, d)
arch/x86/boot/cpuflags.c-67-
arch/x86/boot/cpuflags.c=68=void get_cpuflags(void)
--
arch/x86/boot/cpuflags.c-96- if (max_intel_level >= 0x00000007) {
arch/x86/boot/cpuflags.c:97: cpuid_count(0x00000007, 0, &ignored, &ignored,
arch/x86/boot/cpuflags.c-98- &cpu.flags[16], &ignored);
--
arch/x86/boot/cpuflags.h=23=void get_cpuflags(void);
arch/x86/boot/cpuflags.h:24:void cpuid_count(u32 id, u32 count, u32 *a, u32 *b, u32 *c, u32 *d);
arch/x86/boot/cpuflags.h-25-bool has_cpuflag(int flag);
--
arch/x86/coco/tdx/tdx.c=1113=void __init tdx_early_init(void)
--
arch/x86/coco/tdx/tdx.c-1117-
arch/x86/coco/tdx/tdx.c:1118: cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2], &sig[1]);
arch/x86/coco/tdx/tdx.c-1119-
--
arch/x86/events/intel/core.c=5928=static void update_pmu_cap(struct pmu *pmu)
--
arch/x86/events/intel/core.c-5946- if (eax_0.split.cntr_subleaf) {
arch/x86/events/intel/core.c:5947: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF,
arch/x86/events/intel/core.c-5948- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5954- if (eax_0.split.acr_subleaf) {
arch/x86/events/intel/core.c:5955: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_ACR_LEAF,
arch/x86/events/intel/core.c-5956- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5964- if (eax_0.split.pebs_caps_subleaf && eax_0.split.pebs_cnts_subleaf) {
arch/x86/events/intel/core.c:5965: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_CAP_LEAF,
arch/x86/events/intel/core.c-5966- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/core.c-5968-
arch/x86/events/intel/core.c:5969: cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_COUNTER_LEAF,
arch/x86/events/intel/core.c-5970- &eax, &ebx, &ecx, &edx);
--
arch/x86/events/intel/pt.c=190=static int __init pt_pmu_hw_init(void)
--
arch/x86/events/intel/pt.c-240- for (i = 0; i < PT_CPUID_LEAVES; i++) {
arch/x86/events/intel/pt.c:241: cpuid_count(20, i,
arch/x86/events/intel/pt.c-242- &pt_pmu.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM],
--
arch/x86/include/asm/cpuid/api.h=68=static inline void cpuid(u32 op,
--
arch/x86/include/asm/cpuid/api.h-77-/* Some CPUID calls want 'count' to be placed in ECX */
arch/x86/include/asm/cpuid/api.h:78:static inline void cpuid_count(u32 op, int count,
arch/x86/include/asm/cpuid/api.h-79- u32 *eax, u32 *ebx,
--
arch/x86/include/asm/user.h=18=struct user_xstate_header {
--
arch/x86/include/asm/user.h-31- * obtained by doing:
arch/x86/include/asm/user.h:32: * cpuid_count(0xd, 0, &eax, &ptrace_xstateregs_struct_size, &ecx, &edx);
arch/x86/include/asm/user.h-33- * i.e., cpuid.(eax=0xd,ecx=0).ebx will be the size that user (debuggers, etc.)
--
arch/x86/kernel/cpu/cacheinfo.c=240=static int amd_fill_cpuid4_info(int index, struct _cpuid4_info *id4)
--
arch/x86/kernel/cpu/cacheinfo.c-247- if (boot_cpu_has(X86_FEATURE_TOPOEXT) || boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
arch/x86/kernel/cpu/cacheinfo.c:248: cpuid_count(0x8000001d, index, &eax.full, &ebx.full, &ecx.full, &ignored);
arch/x86/kernel/cpu/cacheinfo.c-249- else
--
arch/x86/kernel/cpu/cacheinfo.c=255=static int intel_fill_cpuid4_info(int index, struct _cpuid4_info *id4)
--
arch/x86/kernel/cpu/cacheinfo.c-261-
arch/x86/kernel/cpu/cacheinfo.c:262: cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &ignored);
arch/x86/kernel/cpu/cacheinfo.c-263-
--
arch/x86/kernel/cpu/cacheinfo.c=276=static int find_num_cache_leaves(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/cacheinfo.c-285- ++i;
arch/x86/kernel/cpu/cacheinfo.c:286: cpuid_count(op, i, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/cacheinfo.c-287- cache_eax.full = eax;
--
arch/x86/kernel/cpu/common.c=1019=void get_cpu_cap(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/common.c-1036- if (c->cpuid_level >= 0x00000007) {
arch/x86/kernel/cpu/common.c:1037: cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1038- c->x86_capability[CPUID_7_0_EBX] = ebx;
--
arch/x86/kernel/cpu/common.c-1043- if (eax >= 1) {
arch/x86/kernel/cpu/common.c:1044: cpuid_count(0x00000007, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1045- c->x86_capability[CPUID_7_1_EAX] = eax;
--
arch/x86/kernel/cpu/common.c-1050- if (c->cpuid_level >= 0x0000000d) {
arch/x86/kernel/cpu/common.c:1051: cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/common.c-1052-
--
arch/x86/kernel/cpu/resctrl/core.c=204=static __init bool __get_mem_config_intel(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-210-
arch/x86/kernel/cpu/resctrl/core.c:211: cpuid_count(0x00000010, 3, &eax.full, &ebx, &ecx, &edx.full);
arch/x86/kernel/cpu/resctrl/core.c-212- hw_res->num_closid = edx.split.cos_max + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=236=static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-246-
arch/x86/kernel/cpu/resctrl/core.c:247: cpuid_count(0x80000020, subleaf, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/core.c-248- hw_res->num_closid = edx + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=268=static void rdt_get_cache_alloc_cfg(int idx, struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/core.c-275-
arch/x86/kernel/cpu/resctrl/core.c:276: cpuid_count(0x00000010, idx, &eax.full, &ebx, &ecx.full, &edx.full);
arch/x86/kernel/cpu/resctrl/core.c-277- hw_res->num_closid = edx.split.cos_max + 1;
--
arch/x86/kernel/cpu/resctrl/core.c=1075=void resctrl_cpu_detect(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/resctrl/core.c-1093- /* QoS sub-leaf, EAX=0Fh, ECX=1 */
arch/x86/kernel/cpu/resctrl/core.c:1094: cpuid_count(0xf, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/core.c-1095-
--
arch/x86/kernel/cpu/resctrl/monitor.c=405=int __init rdt_get_l3_mon_config(struct rdt_resource *r)
--
arch/x86/kernel/cpu/resctrl/monitor.c-441- /* Detect list of bandwidth sources that can be tracked */
arch/x86/kernel/cpu/resctrl/monitor.c:442: cpuid_count(0x80000020, 3, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/monitor.c-443- r->mon.mbm_cfg_mask = ecx & MAX_EVT_CONFIG_BITS;
--
arch/x86/kernel/cpu/resctrl/monitor.c-456- r->mon.mbm_cntr_assignable = true;
arch/x86/kernel/cpu/resctrl/monitor.c:457: cpuid_count(0x80000020, 5, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/resctrl/monitor.c-458- r->mon.num_mbm_cntrs = (ebx & GENMASK(15, 0)) + 1;
--
arch/x86/kernel/cpu/scattered.c=73=void init_scattered_cpuid_features(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/scattered.c-86-
arch/x86/kernel/cpu/scattered.c:87: cpuid_count(cb->level, cb->sub_leaf, ®s[CPUID_EAX],
arch/x86/kernel/cpu/scattered.c-88- ®s[CPUID_EBX], ®s[CPUID_ECX],
--
arch/x86/kernel/cpu/sgx/driver.c=163=int __init sgx_drv_init(void)
--
arch/x86/kernel/cpu/sgx/driver.c-174-
arch/x86/kernel/cpu/sgx/driver.c:175: cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/driver.c-176-
--
arch/x86/kernel/cpu/sgx/driver.c-183-
arch/x86/kernel/cpu/sgx/driver.c:184: cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/driver.c-185-
--
arch/x86/kernel/cpu/sgx/main.c=794=static bool __init sgx_page_cache_init(void)
--
arch/x86/kernel/cpu/sgx/main.c-805- for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) {
arch/x86/kernel/cpu/sgx/main.c:806: cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/cpu/sgx/main.c-807-
--
arch/x86/kernel/cpuid.c=50=static void cpuid_smp_cpuid(void *cmd_block)
--
arch/x86/kernel/cpuid.c-53-
arch/x86/kernel/cpuid.c:54: cpuid_count(cmd->regs.eax, cmd->regs.ecx,
arch/x86/kernel/cpuid.c-55- &cmd->regs.eax, &cmd->regs.ebx,
--
arch/x86/kernel/fpu/xstate.c=254=static void __init setup_xstate_cache(void)
--
arch/x86/kernel/fpu/xstate.c-270- for_each_extended_xfeature(xfeature, fpu_kernel_cfg.max_features) {
arch/x86/kernel/fpu/xstate.c:271: cpuid_count(CPUID_LEAF_XSTATE, xfeature, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-272-
--
arch/x86/kernel/fpu/xstate.c=421=int xfeature_size(int xfeature_nr)
--
arch/x86/kernel/fpu/xstate.c-425- CHECK_XFEATURE(xfeature_nr);
arch/x86/kernel/fpu/xstate.c:426: cpuid_count(CPUID_LEAF_XSTATE, xfeature_nr, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-427- return eax;
--
arch/x86/kernel/fpu/xstate.c=455=static void __init __xstate_dump_leaves(void)
--
arch/x86/kernel/fpu/xstate.c-468- for (i = 0; i < XFEATURE_MAX + 10; i++) {
arch/x86/kernel/fpu/xstate.c:469: cpuid_count(CPUID_LEAF_XSTATE, i, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-470- pr_warn("CPUID[%02x, %02x]: eax=%08x ebx=%08x ecx=%08x edx=%08x\n",
--
arch/x86/kernel/fpu/xstate.c=502=static int __init check_xtile_data_against_struct(int size)
--
arch/x86/kernel/fpu/xstate.c-511- */
arch/x86/kernel/fpu/xstate.c:512: cpuid_count(CPUID_LEAF_TILE, 0, &max_palid, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-513-
--
arch/x86/kernel/fpu/xstate.c-525- */
arch/x86/kernel/fpu/xstate.c:526: cpuid_count(CPUID_LEAF_TILE, palid, &eax, &ebx, &edx, &edx);
arch/x86/kernel/fpu/xstate.c-527- tile_size = eax >> 16;
--
arch/x86/kernel/fpu/xstate.c=655=static unsigned int __init get_compacted_size(void)
--
arch/x86/kernel/fpu/xstate.c-669- */
arch/x86/kernel/fpu/xstate.c:670: cpuid_count(CPUID_LEAF_XSTATE, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-671- return ebx;
--
arch/x86/kernel/fpu/xstate.c=701=static unsigned int __init get_xsave_size_user(void)
--
arch/x86/kernel/fpu/xstate.c-710- */
arch/x86/kernel/fpu/xstate.c:711: cpuid_count(CPUID_LEAF_XSTATE, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-712- return ebx;
--
arch/x86/kernel/fpu/xstate.c=806=void __init fpu__init_system_xstate(unsigned int legacy_size)
--
arch/x86/kernel/fpu/xstate.c-826- */
arch/x86/kernel/fpu/xstate.c:827: cpuid_count(CPUID_LEAF_XSTATE, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-828- fpu_kernel_cfg.max_features = eax + ((u64)edx << 32);
--
arch/x86/kernel/fpu/xstate.c-832- */
arch/x86/kernel/fpu/xstate.c:833: cpuid_count(CPUID_LEAF_XSTATE, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kernel/fpu/xstate.c-834- fpu_kernel_cfg.max_features |= ecx + ((u64)edx << 32);
--
arch/x86/kvm/cpuid.c=50=void __init kvm_init_xstate_sizes(void)
--
arch/x86/kvm/cpuid.c-57-
arch/x86/kvm/cpuid.c:58: cpuid_count(0xD, i, &xs->eax, &xs->ebx, &xs->ecx, &ign);
arch/x86/kvm/cpuid.c-59- }
--
arch/x86/kvm/cpuid.c=675=static __always_inline u32 raw_cpuid_get(struct cpuid_reg cpuid)
--
arch/x86/kvm/cpuid.c-691-
arch/x86/kvm/cpuid.c:692: cpuid_count(cpuid.function, cpuid.index,
arch/x86/kvm/cpuid.c-693- &entry.eax, &entry.ebx, &entry.ecx, &entry.edx);
--
arch/x86/kvm/cpuid.c=1328=static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,
--
arch/x86/kvm/cpuid.c-1361-
arch/x86/kvm/cpuid.c:1362: cpuid_count(entry->function, entry->index,
arch/x86/kvm/cpuid.c-1363- &entry->eax, &entry->ebx, &entry->ecx, &entry->edx);
--
arch/x86/kvm/cpuid.c=1417=static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
--
arch/x86/kvm/cpuid.c-1421-
arch/x86/kvm/cpuid.c:1422: /* all calls to cpuid_count() should be made on the same cpu */
arch/x86/kvm/cpuid.c-1423- get_cpu();
--
arch/x86/kvm/svm/sev.c=3057=void __init sev_hardware_setup(void)
arch/x86/kvm/svm/sev.c-3058-{
arch/x86/kvm/svm/sev.c:3059: unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
arch/x86/kvm/svm/sev.c-3060- struct sev_platform_init_args init_args = {0};
--
arch/x86/kvm/svm/sev.c-3122- if (min_sev_asid <= max_sev_asid) {
arch/x86/kvm/svm/sev.c:3123: sev_asid_count = max_sev_asid - min_sev_asid + 1;
arch/x86/kvm/svm/sev.c:3124: WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV, sev_asid_count));
arch/x86/kvm/svm/sev.c-3125- }
--
arch/x86/kvm/svm/sev.c-3157-
arch/x86/kvm/svm/sev.c:3158: sev_es_asid_count = min_sev_asid - 1;
arch/x86/kvm/svm/sev.c:3159: WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV_ES, sev_es_asid_count));
arch/x86/kvm/svm/sev.c-3160- sev_es_supported = true;
--
arch/x86/kvm/vmx/sgx.c=440=static bool sgx_intercept_encls_ecreate(struct kvm_vcpu *vcpu)
--
arch/x86/kvm/vmx/sgx.c-451-
arch/x86/kvm/vmx/sgx.c:452: cpuid_count(0x12, 0, &eax, &ebx, &ecx, &edx);
arch/x86/kvm/vmx/sgx.c-453- if (guest_cpuid->ebx != ebx || guest_cpuid->edx != edx)
--
arch/x86/kvm/vmx/sgx.c-459-
arch/x86/kvm/vmx/sgx.c:460: cpuid_count(0x12, 1, &eax, &ebx, &ecx, &edx);
arch/x86/kvm/vmx/sgx.c-461- if (guest_cpuid->eax != eax || guest_cpuid->ebx != ebx ||
--
arch/x86/um/os-Linux/registers.c=48=int arch_init_registers(int pid)
--
arch/x86/um/os-Linux/registers.c-60-
arch/x86/um/os-Linux/registers.c:61: /* GDB has x86_xsave_length, which uses x86_cpuid_count */
arch/x86/um/os-Linux/registers.c-62- ptrace_regset = NT_X86_XSTATE;
--
arch/x86/xen/time.c=492=static int __init xen_tsc_safe_clocksource(void)
--
arch/x86/xen/time.c-505- /* Leaf 4, sub-leaf 0 (0x40000x03) */
arch/x86/xen/time.c:506: cpuid_count(xen_cpuid_base() + 3, 0, &eax, &ebx, &ecx, &edx);
arch/x86/xen/time.c-507-
--
drivers/accel/ivpu/ivpu_drv.c=214=static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
--
drivers/accel/ivpu/ivpu_drv.c-261- case DRM_IVPU_PARAM_UNIQUE_INFERENCE_ID:
drivers/accel/ivpu/ivpu_drv.c:262: args->value = (u64)atomic64_inc_return(&vdev->unique_id_counter);
drivers/accel/ivpu/ivpu_drv.c-263- break;
--
drivers/accel/ivpu/ivpu_drv.c=655=static int ivpu_dev_init(struct ivpu_device *vdev)
--
drivers/accel/ivpu/ivpu_drv.c-686- vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
drivers/accel/ivpu/ivpu_drv.c:687: atomic64_set(&vdev->unique_id_counter, 0);
drivers/accel/ivpu/ivpu_drv.c-688- atomic_set(&vdev->job_timeout_counter, 0);
--
drivers/accel/ivpu/ivpu_drv.h=135=struct ivpu_device {
--
drivers/accel/ivpu/ivpu_drv.h-173-
drivers/accel/ivpu/ivpu_drv.h:174: atomic64_t unique_id_counter;
drivers/accel/ivpu/ivpu_drv.h-175-
--
drivers/acpi/arm64/iort.c=343=static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
--
drivers/acpi/arm64/iort.c-360- if (rid_in < map->input_base ||
drivers/acpi/arm64/iort.c:361: (rid_in > map->input_base + map->id_count))
drivers/acpi/arm64/iort.c-362- return -ENXIO;
--
drivers/acpi/arm64/iort.c-383- /*
drivers/acpi/arm64/iort.c:384: * Due to confusion regarding the meaning of the id_count field (which
drivers/acpi/arm64/iort.c-385- * carries the number of IDs *minus 1*), we may have to disregard this
--
drivers/acpi/arm64/iort.c-388- */
drivers/acpi/arm64/iort.c:389: if (map->id_count > 0 && rid_in == map->input_base + map->id_count)
drivers/acpi/arm64/iort.c-390- return -EAGAIN;
--
drivers/acpi/arm64/iort.c=1072=static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
drivers/acpi/arm64/iort.c:1073: u32 id_count)
drivers/acpi/arm64/iort.c-1074-{
--
drivers/acpi/arm64/iort.c-1092- if (fwspec->ids[i] >= id_start &&
drivers/acpi/arm64/iort.c:1093: fwspec->ids[i] <= id_start + id_count)
drivers/acpi/arm64/iort.c-1094- return true;
--
drivers/acpi/arm64/iort.c=1100=static void iort_node_get_rmr_info(struct acpi_iort_node *node,
--
drivers/acpi/arm64/iort.c-1126- * and dev(if !NULL). If found, get the sids for the Node.
drivers/acpi/arm64/iort.c:1127: * Please note, id_count is equal to the number of IDs in the
drivers/acpi/arm64/iort.c-1128- * range minus one.
--
drivers/acpi/arm64/iort.c-1139- if (dev && !iort_rmr_has_dev(dev, map->output_base,
drivers/acpi/arm64/iort.c:1140: map->id_count))
drivers/acpi/arm64/iort.c-1141- continue;
--
drivers/acpi/arm64/iort.c-1144- sids = iort_rmr_alloc_sids(sids, num_sids, map->output_base,
drivers/acpi/arm64/iort.c:1145: map->id_count + 1);
drivers/acpi/arm64/iort.c-1146- if (!sids)
--
drivers/acpi/arm64/iort.c-1148-
drivers/acpi/arm64/iort.c:1149: num_sids += map->id_count + 1;
drivers/acpi/arm64/iort.c-1150- }
--
drivers/cpufreq/cppc_cpufreq.c=789=static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
--
drivers/cpufreq/cppc_cpufreq.c-805- /* Any of the associated CPPC regs is 0. */
drivers/cpufreq/cppc_cpufreq.c:806: goto out_invalid_counters;
drivers/cpufreq/cppc_cpufreq.c-807- else
--
drivers/cpufreq/cppc_cpufreq.c-814- if (!delivered_perf)
drivers/cpufreq/cppc_cpufreq.c:815: goto out_invalid_counters;
drivers/cpufreq/cppc_cpufreq.c-816-
--
drivers/cpufreq/cppc_cpufreq.c-818-
drivers/cpufreq/cppc_cpufreq.c:819:out_invalid_counters:
drivers/cpufreq/cppc_cpufreq.c-820- /*
--
drivers/firewire/core-topology.c=86=static inline struct fw_node *fw_node(struct list_head *l)
--
drivers/firewire/core-topology.c-97- */
drivers/firewire/core-topology.c:98:static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self_id_count,
drivers/firewire/core-topology.c-99- unsigned int generation)
--
drivers/firewire/core-topology.c-102- .cursor = sid,
drivers/firewire/core-topology.c:103: .quadlet_count = self_id_count,
drivers/firewire/core-topology.c-104- };
--
drivers/firewire/core-topology.c=440=static void update_topology_map(__be32 *buffer, size_t buffer_size, int root_node_id,
drivers/firewire/core-topology.c:441: const u32 *self_ids, int self_id_count)
drivers/firewire/core-topology.c-442-{
--
drivers/firewire/core-topology.c-448-
drivers/firewire/core-topology.c:449: *map++ = cpu_to_be32((self_id_count + 2) << 16);
drivers/firewire/core-topology.c-450- *map++ = cpu_to_be32(next_generation);
drivers/firewire/core-topology.c:451: *map++ = cpu_to_be32((node_count << 16) | self_id_count);
drivers/firewire/core-topology.c-452-
drivers/firewire/core-topology.c:453: while (self_id_count--)
drivers/firewire/core-topology.c-454- *map++ = cpu_to_be32p(self_ids++);
--
drivers/firewire/core-topology.c=459=void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
drivers/firewire/core-topology.c:460: int self_id_count, u32 *self_ids, bool bm_abdicate)
drivers/firewire/core-topology.c-461-{
--
drivers/firewire/core-topology.c-463-
drivers/firewire/core-topology.c:464: trace_bus_reset_handle(card->index, generation, node_id, bm_abdicate, self_ids, self_id_count);
drivers/firewire/core-topology.c-465-
--
drivers/firewire/core-topology.c-483-
drivers/firewire/core-topology.c:484: local_node = build_tree(card, self_ids, self_id_count, generation);
drivers/firewire/core-topology.c-485-
--
drivers/firewire/core-topology.c-503- update_topology_map(card->topology_map.buffer, sizeof(card->topology_map.buffer),
drivers/firewire/core-topology.c:504: card->root_node->node_id, self_ids, self_id_count);
drivers/firewire/core-topology.c-505- }
--
drivers/firewire/core.h=253=void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
drivers/firewire/core.h:254: int generation, int self_id_count, u32 *self_ids, bool bm_abdicate);
drivers/firewire/core.h-255-void fw_destroy_nodes(struct fw_card *card);
--
drivers/firewire/ohci-serdes-test.c-12-
drivers/firewire/ohci-serdes-test.c:13:static void test_self_id_count_register_deserialization(struct kunit *test)
drivers/firewire/ohci-serdes-test.c-14-{
--
drivers/firewire/ohci-serdes-test.c-16-
drivers/firewire/ohci-serdes-test.c:17: bool is_error = ohci1394_self_id_count_is_error(expected);
drivers/firewire/ohci-serdes-test.c:18: u8 generation = ohci1394_self_id_count_get_generation(expected);
drivers/firewire/ohci-serdes-test.c:19: u32 size = ohci1394_self_id_count_get_size(expected);
drivers/firewire/ohci-serdes-test.c-20-
--
drivers/firewire/ohci-serdes-test.c=107=static struct kunit_case ohci_serdes_test_cases[] = {
drivers/firewire/ohci-serdes-test.c:108: KUNIT_CASE(test_self_id_count_register_deserialization),
drivers/firewire/ohci-serdes-test.c-109- KUNIT_CASE(test_self_id_receive_buffer_deserialization),
--
drivers/firewire/ohci.c=1772=static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id,
drivers/firewire/ohci.c:1773: int self_id_count)
drivers/firewire/ohci.c-1774-{
--
drivers/firewire/ohci.c-1777-
drivers/firewire/ohci.c:1778: for (i = 0; i < self_id_count; i++) {
drivers/firewire/ohci.c-1779- u32 entry = ohci->self_id_buffer[i];
--
drivers/firewire/ohci.c=1790=static int detect_initiated_reset(struct fw_ohci *ohci, bool *is_initiated_reset)
--
drivers/firewire/ohci.c-1826- */
drivers/firewire/ohci.c:1827:static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
drivers/firewire/ohci.c-1828-{
--
drivers/firewire/ohci.c-1871-
drivers/firewire/ohci.c:1872: pos = get_self_id_pos(ohci, self_id, self_id_count);
drivers/firewire/ohci.c-1873- if (pos >= 0) {
--
drivers/firewire/ohci.c-1875- &(ohci->self_id_buffer[pos]),
drivers/firewire/ohci.c:1876: (self_id_count - pos) * sizeof(*ohci->self_id_buffer));
drivers/firewire/ohci.c-1877- ohci->self_id_buffer[pos] = self_id;
drivers/firewire/ohci.c:1878: self_id_count++;
drivers/firewire/ohci.c-1879- }
drivers/firewire/ohci.c:1880: return self_id_count;
drivers/firewire/ohci.c-1881-}
--
drivers/firewire/ohci.c=1883=static irqreturn_t handle_selfid_complete_event(int irq, void *data)
--
drivers/firewire/ohci.c-1885- struct fw_ohci *ohci = data;
drivers/firewire/ohci.c:1886: int self_id_count, generation, new_generation, i, j;
drivers/firewire/ohci.c-1887- u32 reg, quadlet;
--
drivers/firewire/ohci.c-1911- reg = reg_read(ohci, OHCI1394_SelfIDCount);
drivers/firewire/ohci.c:1912: if (ohci1394_self_id_count_is_error(reg)) {
drivers/firewire/ohci.c-1913- ohci_notice(ohci, "self ID receive error\n");
--
drivers/firewire/ohci.c-1924- */
drivers/firewire/ohci.c:1925: self_id_count = ohci1394_self_id_count_get_size(reg) >> 1;
drivers/firewire/ohci.c-1926-
drivers/firewire/ohci.c:1927: if (self_id_count > 252) {
drivers/firewire/ohci.c-1928- ohci_notice(ohci, "bad selfIDSize (%08x)\n", reg);
--
drivers/firewire/ohci.c-1935-
drivers/firewire/ohci.c:1936: for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
drivers/firewire/ohci.c-1937- u32 id = cond_le32_to_cpu(ohci->self_id[i], has_be_header_quirk(ohci));
]
]
|
| 102/2 |
2026/05/22 01:52 |
llm |
expert |
0m
Model:
Tokens:
input: 148531
output: 23
thoughts: 19
Thoughts: Okay, here's my summarized thought process, presented as if I were thinking it:
**Thinking Through OCFS2 Identification**
Alright, so I need to find where `id_count` is being used within the OCFS2 filesystem. My first instinct, given my experience, is to leverage a quick and dirty approach. Let's see... Rather than wading through potentially complicated cross-referencing or relying on IDE functionality, I think I'll just use `grep`. Specifically, I'll direct it at the likely source directory. I'll just `grep` for the exact string, `id_count`, within the `fs/ocfs2/` directory. That seems like the most efficient initial approach to get a handle on the direct usages. This should give me a very focused list of files and lines, which will be the perfect starting point for more detailed analysis. It gets to the heart of the matter quickly.
|
| 103/2 |
2026/05/22 01:52 |
tool |
grepper |
0m
Error:
Args: map[Expression:id_count.*fs/ocfs2]
Results:
|
| 104/2 |
2026/05/22 01:52 |
llm |
expert |
0m
Model:
Tokens:
input: 148783
output: 49
thoughts: 74
Thoughts: **My Git Grep Strategy**
Okay, let's see, the syntax for `git grep` again⦠right, `git grep "id_count" fs/ocfs2/`. I remember now that I can specify an `Expression: "id_count"` and that will perform a tree-wide search. Good, good. I think I actually *did* that already, but let's just make sure. My brain is getting a little fuzzy today. What was I looking for? Oh yeah, `id_count`. Okay, let's specifically look at `fs/ocfs2/ocfs2_fs.h` and see if the definition of `id_count` is in there. That's the most likely place.
|
| 105/2 |
2026/05/22 01:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/ocfs2/ocfs2_fs.h IncludeLines:true Name:ocfs2_inline_data]
Results: map[SourceCode: 647: /*
648: * Data-in-inode header. This is only used if i_dyn_features has
649: * OCFS2_INLINE_DATA_FL set.
650: */
651: struct ocfs2_inline_data
652: {
653: /*00*/ __le16 id_count; /* Number of bytes that can be used
654: * for data, starting at id_data */
655: __le16 id_reserved0;
656: __le32 id_reserved1;
657: __u8 id_data[] __counted_by_le(id_count); /* Start of user data */
658: };
SourceFile:fs/ocfs2/ocfs2_fs.h]
|
| 106/2 |
2026/05/22 01:52 |
llm |
expert |
0m
Model:
Tokens:
input: 149309
output: 48
thoughts: 1851
Thoughts: **Bug Analysis: Corrupted Inline Data in OCFS2**
Okay, let's break this down. My initial thought was: `id_count` being 65535, potentially leading to an out-of-bounds write via `memcpy_from_folio`. But, the validation in `ocfs2_validate_inode_block` seemed like a safeguard. The filesystem being mounted from a loop device opened the possibility for syzkaller to directly corrupt the backing file. But then I realized that if the validation failed, the inode wouldn't be used, and the bug wouldn't trigger. So, the key is understanding *why* validation might not fail.
I went back to the code, specifically `ocfs2_validate_inode_block`. If `OCFS2_INLINE_DATA_FL` isn't set, the `id_count` isn't checked! This is the critical observation! If the flag isn't set, a corrupted `id_count` goes undetected and the inode is marked uptodate.
Next, I looked at how the corrupted `id_count` would be used. The `copy_file_range` call leads to `ocfs2_write_begin_nolock`, and the `ocfs2_try_to_write_inline_data` function. If the inline data flag isn't set, a size check is done: `end > ocfs2_max_inline_data_with_xattr`. `ocfs2_max_inline_data_with_xattr` has to be a positive value greater than the block size to trigger the bug. If it's not inline data, the function should return a smaller size limit, which prevents `memcpy_from_folio` from writing past the limits of the buffer.
I then revisited the implementation of `ocfs2_max_inline_data_with_xattr`. The interesting part is the `OCFS2_INLINE_XATTR_FL` flag and how `xattrsize` is used. If `xattrsize` is too large, the function can return a negative integer. This means the comparison (`end > negative_number`) would *always* be true, preventing inline data write, therefore negating any possibility of memory corruption.
It seems impossible to craft an xattr size and inline size such that `ocfs2_max_inline_data_with_xattr` returns a value larger than the blocksize and smaller than 65535, so this cannot trigger the bug. I then realized that the block size calculation may be a key here.
Finally, I checked the block size logic in `ocfs2_sb_probe`. Maybe `OCFS2_MAX_BLOCKSIZE` can be larger than 4096.
|
| 107/2 |
2026/05/22 01:53 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:fs/ocfs2/ocfs2.h IncludeLines:true Name:OCFS2_MAX_BLOCKSIZE]
Results: map[SourceCode: SourceFile:]
|
| 108/2 |
2026/05/22 01:53 |
llm |
expert |
0m
Model:
Tokens:
input: 151827
output: 25
thoughts: 15
Thoughts: Here's my thought process, summarized as you requested:
**Investigating Block Size Limits in OCFS2**
Okay, so I need to understand the maximum block size supported by the Oracle Cluster File System 2 (OCFS2). My first instinct is to dive into the source code, so I'm going to start with a `grep` search. The thing I'm looking for is a constant or macro that defines the block size limit, and based on experience with these kinds of systems, I'm betting it's named something like `OCFS2_MAX_BLOCKSIZE`. I'll run the `grep` command and see what I can turn up. This should point me directly to the relevant definition, and likely related code that uses it. Hopefully, it's not buried too deep! Let's see what the results tell us.
|
| 109/2 |
2026/05/22 01:53 |
tool |
grepper |
0m
Args: map[Expression:#define OCFS2_MAX_BLOCKSIZE]
Results: map[Output:fs/ocfs2/ocfs2_fs.h-43-#define OCFS2_MIN_BLOCKSIZE 512
fs/ocfs2/ocfs2_fs.h:44:#define OCFS2_MAX_BLOCKSIZE OCFS2_MIN_CLUSTERSIZE
fs/ocfs2/ocfs2_fs.h-45-
]
|
| 110/2 |
2026/05/22 01:53 |
llm |
expert |
0m
Model:
Tokens:
input: 152147
output: 25
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Delving into OCFS2 Cluster Sizes**
Alright, so the phrase `OCFS2_MIN_CLUSTERSIZE` just popped into my head, and my mind immediately starts dissecting it. It's a fundamental parameter, obviously, but I'm curious about the specific context. I know OCFS2, but the precise minimum cluster size⦠that's the niggling question. Is there a scenario I'm currently working on where knowing the exact minimum matters? Or is this just a general refresher to solidify my foundational knowledge? I'm probably thinking about performance implications, maybe the metadata layout or block management within the file system itself. It's that subtle intersection of theoretical understanding and practical application that I'm keen to revisit. The minimum sets the floor, influencing how efficiently the file system handles smaller files and overall space utilization. Let's see if this thought triggers any related architectural considerations.
|
| 111/2 |
2026/05/22 01:53 |
tool |
grepper |
0m
Args: map[Expression:#define OCFS2_MIN_CLUSTERSIZE]
Results: map[Output:fs/ocfs2/ocfs2_fs.h-35- */
fs/ocfs2/ocfs2_fs.h:36:#define OCFS2_MIN_CLUSTERSIZE 4096
fs/ocfs2/ocfs2_fs.h-37-#define OCFS2_MAX_CLUSTERSIZE 1048576
]
|
| 112/2 |
2026/05/22 01:53 |
llm |
expert |
0m
Model:
Tokens:
input: 152500
output: 66
thoughts: 7173
Thoughts: **Analysis of the OCFS2 VM_BUG_ON Trigger**
Okay, so I've dug into this `VM_BUG_ON` in `memcpy_from_folio`, and it looks like the crux of the problem lies in how OCFS2 handles inline data and inode block validation. Specifically, it involves the combination of a large `pos` combined with a large `*copied` within `memcpy_from_folio`. The maximum block size, `OCFS2_MAX_BLOCKSIZE`, is 4096, and therefore `sb->s_blocksize` is also at most 4096. This limits `ocfs2_max_inline_data_with_xattr` to a maximum of 3896 bytes (accounting for the inode header). Logically, `pos + *copied` should never exceed 3896, and therefore be less than or equal to `folio_size(folio)` (4096). That's the expected behavior.
The `VM_BUG_ON(offset + len > folio_size(folio))` triggers when `offset + len` (in our case, `pos + *copied`) goes beyond the folio size. I've tracked that `memcpy_from_folio` is called in `ocfs2_write_end_inline`, where `offset` is essentially `pos`, and `len` is `*copied`. `*copied` is limited by `bytes`, which in turn, is limited by `chunk - offset`, where `chunk` is essentially the folio size. But if `pos` is very large, then `pos + *copied` goes over the folio size (4096). The key is this: How can `pos` ever be greater than 4096, given the limits of inline data and OCFS2 block sizes?
The vulnerability arises when we can *bypass* the `ocfs2_validate_inode_block` check. If `id_count` *somehow* gets corrupted to a large value (say, 65535) and `OCFS2_INLINE_DATA_FL` is set, `ocfs2_size_fits_inline_data` is used. This function will now pass a `new_size` of up to 65535, because the corrupted `id_count` makes it return 1 for a new size up to 65535, even though, validly, the inline data area is much smaller. The code that calls `ocfs2_size_fits_inline_data` in this case does *not* validate the `id_count`, so it returns 1. If it returns 1, then the code will set `end = pos + len`. If `pos + len` becomes large, `pos` can be up to 65535, and trigger the `VM_BUG_ON`.
The real question is *how* can that inode block get into memory with a corrupted `id_count`? Let's check the validation again. If we can't find a way to make it fail, then the only way that `id_count` is corrupted is via buffer cache poisoning. If the inode is read from disk, `ocfs2_validate_inode_block` checks `id_count`. It compares `id_count` with `ocfs2_max_inline_data_with_xattr`. When `id_count` is 65535, this validation should *always* fail. If `ocfs2_max_inline_data_with_xattr` returns a negative value, the validation passes because `(int)id_count > (int)negative` returns true. Otherwise, if `ocfs2_max_inline_data_with_xattr` returns a value less than 65535, but greater than or equal to 3896, `id_count` will still be larger than `ocfs2_max_inline_data_with_xattr`. So it always fails. *Therefore*, if the filesystem reads an invalid inode with a corrupted `id_count` from disk, `ocfs2_validate_inode_block` ALWAYS detects the corruption and returns an error! There's no way around it.
What if we create the inode in-memory? If it is created with `ocfs2_mknod`, then it gets set to a valid value using `ocfs2_populate_inode`, so this case would not occur. What about if the validation fails, but the error is ignored? Let's check `ocfs2_read_inode_block_full`: if it fails, the error is returned. So that won't happen.
However
|
| 113/2 |
2026/05/22 01:54 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 114/2 |
2026/05/22 01:54 |
llm |
expert |
0m
Model:
Tokens:
input: 160828
output: 764
thoughts: 432
Thoughts: **Finalizing the Response: The Heap Out-of-Bounds Issue**
Okay, the analysis is complete, and it's time to craft the final response. My findings point to a heap out-of-bounds read/write within the `ocfs2_write_end_inline` function. The root cause? A corrupted `id_count` field within the inode's inline data.
Specifically, it boils down to the interaction between `memcpy_from_folio` and the `id_count` value. `memcpy_from_folio` uses a `pos` variable as an offset into the folio (a page, usually 4096 bytes). The problem arises when `pos + *copied` surpasses the folio's size, leading to an out-of-bounds memory access or a `VM_BUG_ON` triggering.
The vulnerability is exploited when the `ocfs2_size_fits_inline_data` function incorrectly returns `true` for a `new_size` larger than the folio size. This function checks if `new_size <= le16_to_cpu(di->id2.i_data.id_count)`. If `id_count` is corrupted and is set to a large value, say, 65535, the check passes, and `pos` can become dangerously large.
However, a critical point here is that `id_count` is usually validated by `ocfs2_validate_inode_block` during inode reads from disk. This validation ensures that `id_count` doesn't exceed the block size (minus an offset). This safeguard is supposed to prevent this very scenario, setting the threshold to something less than 3896 bytes (given a standard block size).
The trick to bypass this validation is ingenious. The attacker, in this case, Syzkaller, leverages the block device's page cache. By writing the corrupted inode block directly to the underlying block device (e.g., `/dev/loop0`), they bypass the initial read from disk. When OCFS2 subsequently reads the inode, it finds the "uptodate" buffer in the cache and skips the validation process.
Importantly, though, this bug isn't a walk in the park for attackers. Writing directly to the block device requires root privileges or `CAP_SYS_ADMIN` capabilities, limiting its exploitability. Further, the bug isn't exploitable by mounting a malicious filesystem image, because the initial read would trigger validation and reject the corrupted inode. So, while serious, this isn't a readily available or easily triggered exploit.
|
| 115/1 |
2026/05/22 01:54 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug**
The bug is a heap out-of-bounds read and write (and a `VM_BUG_ON` crash if
`CONFIG_DEBUG_VM` is enabled) in `ocfs2_write_end_inline`.
**Root Cause:**
When writing to an ocfs2 file with inline data, `ocfs2_write_begin_nolock`
checks if the write fits within the inline data limits by calling
`ocfs2_size_fits_inline_data`. This function verifies that the new file size
does not exceed `di->id2.i_data.id_count` (the maximum inline data size recorded
in the on-disk inode).
If `id_count` is corrupted to a large value (e.g., `65535`), the filesystem
allows the inline write to proceed for large offsets. Subsequently,
`ocfs2_write_end_inline` calls `memcpy_from_folio` to copy data from the page
cache folio to the inode's buffer head:
```c
memcpy_from_folio(di->id2.i_data.id_data + pos, wc->w_target_folio, pos,
*copied);
```
Here, `pos` is used as the offset within the folio. If `pos` is larger than the
folio size (typically 4096 bytes), `memcpy_from_folio` reads out-of-bounds from
the folio and writes out-of-bounds into the buffer head data, corrupting
adjacent heap memory.
**Bypassing Validation:**
Under normal circumstances, a corrupted `id_count` is caught by
`ocfs2_validate_inode_block` when the inode is read from disk. The validation
strictly ensures that `id_count` is less than or equal to the block size minus
headers (at most ~3896 bytes). If validation fails, the inode is rejected, and
the filesystem is marked read-only.
However, the validation is completely bypassed if the inode block is *already*
present in the block device's page cache and marked as uptodate. The syzkaller
fuzzer triggers this by opening the underlying block device (e.g., `/dev/loop0`)
and directly writing a crafted inode block with `id_count = 65535` into it. When
ocfs2 later accesses this inode, `ocfs2_read_blocks` finds the buffer already
uptodate in the cache and skips the disk read and the associated validation
(`ocfs2_validate_inode_block`). This is a classic "buffer cache poisoning"
technique.
**Security Impact:**
- **Exploitable / Denial of Service:** Yes. The bug leads to a massive heap
out-of-bounds write (up to 64KB) which can corrupt critical kernel structures,
leading to a system crash or potentially arbitrary code execution.
- **Unprivileged / User Namespace:** False. Poisoning the buffer cache requires
direct write access to the underlying block device (e.g., `/dev/loop0`), which
strictly requires `root` privileges or `CAP_SYS_ADMIN`. The `ocfs2` filesystem
also cannot be mounted within an unprivileged user namespace.
- **Malicious Filesystem / Peripheral Trigger:** False. If a user plugs in a USB
drive containing a malicious ocfs2 image with a corrupted `id_count`, the
initial read from the disk will correctly trigger `ocfs2_validate_inode_block`.
The validation will fail, the inode will be rejected, and the bug will not be
triggered.]
|