mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
183e21e23b
Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
186 lines
5.8 KiB
Plaintext
186 lines
5.8 KiB
Plaintext
FUSE Extensions
|
|
================
|
|
|
|
Overview
|
|
--------
|
|
|
|
FUSE extensions are a mechanism for passing additional metadata alongside regular
|
|
FUSE requests. Extensions are placed at the end of the request payload and can
|
|
contain various types of supplementary information such as security contexts or
|
|
supplementary group IDs.
|
|
|
|
Wire Protocol
|
|
-------------
|
|
|
|
Extensions are appended to the end of FUSE request messages. Their presence and
|
|
total size is indicated by the `total_extlen` field in the fuse_in_header.
|
|
|
|
struct fuse_in_header {
|
|
uint32_t len;
|
|
uint32_t opcode;
|
|
uint64_t unique;
|
|
uint64_t nodeid;
|
|
uint32_t uid;
|
|
uint32_t gid;
|
|
uint32_t pid;
|
|
uint16_t total_extlen; /* Total size of all extensions in 8-byte units */
|
|
uint16_t padding;
|
|
};
|
|
|
|
The total_extlen field:
|
|
- Measured in 8-byte units (not bytes)
|
|
- Allows encoding up to 512 KB of extensions in a 16-bit field
|
|
- Convert to bytes: total_bytes = total_extlen * 8
|
|
- All extensions combined are always 8-byte aligned (FUSE_REC_ALIGN)
|
|
|
|
Extension Location
|
|
------------------
|
|
|
|
Extensions are located at the END of the request buffer:
|
|
|
|
Non-io_uring:
|
|
[fuse_in_header][operation header][operation payload][extensions]
|
|
^
|
|
ext_start = buffer_end - (total_extlen * 8)
|
|
|
|
io_uring:
|
|
Headers: [fuse_in_header][operation header]
|
|
Payload: [operation payload][extensions]
|
|
^
|
|
ext_start = payload_end - (total_extlen * 8)
|
|
|
|
Extension Header Format
|
|
-----------------------
|
|
|
|
Extensions use a polymorphic header design where the same 8-byte structure
|
|
is interpreted differently based on the second field's value:
|
|
|
|
For security contexts (nr_secctx = 1..31):
|
|
struct fuse_secctx_header {
|
|
uint32_t size; /* Total size of this extension in bytes */
|
|
uint32_t nr_secctx; /* Number of security contexts (1-31) */
|
|
};
|
|
|
|
For other extensions (type >= 32):
|
|
struct fuse_ext_header {
|
|
uint32_t size; /* Total size of this extension in bytes */
|
|
uint32_t type; /* Extension type ID (32+) */
|
|
};
|
|
|
|
The second field determines interpretation:
|
|
- Value 0: Invalid / reserved
|
|
- Value 1-31: Security context header (nr_secctx)
|
|
- Value 32+: Other extension type (type ID)
|
|
|
|
Extension Types
|
|
---------------
|
|
|
|
Defined in enum fuse_ext_type:
|
|
|
|
FUSE_MAX_NR_SECCTX = 31
|
|
Maximum number of security contexts in a single extension.
|
|
Values 0-31 are reserved for fuse_secctx_header.
|
|
|
|
FUSE_EXT_GROUPS = 32
|
|
Supplementary groups extension (fuse_supp_groups).
|
|
|
|
Multiple Extensions
|
|
-------------------
|
|
|
|
A single request can contain multiple extensions. They are concatenated
|
|
sequentially, each 8-byte aligned:
|
|
|
|
[fuse_secctx_header + secctx data][padding][fuse_ext_header + groups data][padding]
|
|
|
|
To iterate:
|
|
1. Start at ext_start = buffer_end - (total_extlen * 8)
|
|
2. Read header (8 bytes)
|
|
3. Process extension based on second field value
|
|
4. Advance: next_ext = current_ext + FUSE_REC_ALIGN(header.size)
|
|
5. Repeat until reaching ext_end
|
|
|
|
Security Context Extension
|
|
--------------------------
|
|
|
|
Wire format for security context (type 0-31):
|
|
|
|
Offset 0-7: fuse_secctx_header { size, nr_secctx }
|
|
Offset 8: First fuse_secctx structure (8 bytes)
|
|
{
|
|
uint32_t size; /* Size of context VALUE only */
|
|
uint32_t padding; /* Always 0 */
|
|
}
|
|
Offset 16: Context name (null-terminated string)
|
|
Example: "security.selinux\0"
|
|
Offset N: Context value (size bytes)
|
|
Example: "unconfined_u:object_r:user_home_t:s0"
|
|
[Padding to 8-byte boundary]
|
|
[Additional contexts if nr_secctx > 1]
|
|
|
|
Example (72 bytes total):
|
|
Offset 0-7: { size=72, nr_secctx=1 }
|
|
Offset 8-15: { size=38, padding=0 }
|
|
Offset 16-32: "security.selinux\0" (17 bytes)
|
|
Offset 33-70: "unconfined_u:object_r:user_home_t:s0" (38 bytes)
|
|
Offset 71: 0x00 (padding)
|
|
|
|
Kernel Guarantees
|
|
-----------------
|
|
|
|
The kernel guarantees:
|
|
1. Extension data is always at the end of the buffer
|
|
2. total_extlen accurately represents the extension size
|
|
3. All extensions are 8-byte aligned (FUSE_REC_ALIGN)
|
|
4. Security context name strings are null-terminated (via strscpy)
|
|
5. Extension headers are well-formed
|
|
|
|
Userspace Requirements
|
|
----------------------
|
|
|
|
Userspace filesystem daemons must:
|
|
1. Check total_extlen in fuse_in_header
|
|
2. Calculate extension location: buffer_end - (total_extlen * 8)
|
|
3. Iterate through extensions checking the second header field
|
|
4. For security contexts (1-31): interpret as fuse_secctx_header
|
|
5. For other types (32+): interpret as fuse_ext_header
|
|
6. Validate all size fields to prevent buffer overruns
|
|
7. Use FUSE_REC_ALIGN when advancing to next extension
|
|
|
|
Capability Negotiation
|
|
----------------------
|
|
|
|
Extensions are only sent when negotiated during FUSE_INIT:
|
|
|
|
For security contexts:
|
|
Kernel flag: FUSE_SECURITY_CTX (1ULL << 32)
|
|
Libfuse flag: FUSE_CAP_SECURITY_CTX
|
|
|
|
The kernel only sends security context extensions for operations where
|
|
security labels can be atomically set during creation:
|
|
- FUSE_CREATE
|
|
- FUSE_MKNOD
|
|
- FUSE_MKDIR
|
|
- FUSE_SYMLINK
|
|
|
|
Alignment Considerations
|
|
------------------------
|
|
|
|
While extensions are 8-byte aligned relative to each other, their absolute
|
|
position in the buffer may not be 8-byte aligned because they follow
|
|
variable-length operation data (e.g., filenames).
|
|
|
|
Example:
|
|
[header:40][create_in:16][filename:9][extension:72]
|
|
^
|
|
Offset 65 - NOT 8-byte aligned!
|
|
|
|
Therefore, userspace must use memcpy() when reading extension headers to
|
|
avoid unaligned access issues on architectures that don't support it.
|
|
|
|
References
|
|
----------
|
|
|
|
- include/fuse_kernel.h: Extension structure definitions
|
|
- Linux kernel: fs/fuse/dir.c: get_security_context()
|
|
- Linux kernel: fs/fuse/dev.c: fuse_args_to_req()
|