Files
zvfs/src/hook/zvfs_hook_seek.c
2026-04-14 07:40:56 +00:00

323 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "zvfs_hook_seek.h"
#include "zvfs_hook_init.h"
#include "zvfs_hook_reentrant.h"
#include "fs/zvfs.h"
#include "fs/zvfs_open_file.h"
#include "fs/zvfs_inode.h"
#include "fs/zvfs_path_entry.h"
#include "spdk_engine/io_engine.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/falloc.h> /* FALLOC_FL_* */
#include <pthread.h>
#include <stdint.h>
/* ------------------------------------------------------------------ */
/* lseek / lseek64 */
/* ------------------------------------------------------------------ */
off_t
lseek(int fd, off_t offset, int whence)
{
ZVFS_HOOK_ENTER();
struct zvfs_open_file *of = NULL;
if (!ZVFS_IN_HOOK()) {
pthread_mutex_lock(&g_fs.fd_mu);
of = openfile_lookup(fd);
pthread_mutex_unlock(&g_fs.fd_mu);
}
if (!of) {
off_t r = real_lseek(fd, offset, whence);
ZVFS_HOOK_LEAVE();
return r;
}
zvfs_ensure_init();
pthread_mutex_lock(&of->inode->mu);
uint64_t logical_size = of->inode->logical_size;
pthread_mutex_unlock(&of->inode->mu);
uint64_t new_off = 0;
if (blob_seek(of->handle_id, (int64_t)offset, whence, logical_size, &new_off) != 0) {
ZVFS_HOOK_LEAVE();
return (off_t)-1;
}
ZVFS_HOOK_LEAVE();
return (off_t)new_off;
}
off_t lseek64(int fd, off_t offset, int whence)
{
return lseek(fd, offset, whence);
}
/* ------------------------------------------------------------------ */
/* 内部:按 inode 指针做 truncatepath / fd 路径共用) */
/* ------------------------------------------------------------------ */
/*
* zvfs_truncate_by_inode - 对有 handle_id 的 openfile 做 truncate。
* 找到任意一个打开该 inode 的 openfile 取其 handle_id。
*/
static int
zvfs_truncate_inode_with_handle(struct zvfs_inode *inode,
int real_fd, uint64_t new_size)
{
/* 在 fd_table 里找一个指向该 inode 的 openfile 取 handle_id */
uint64_t handle_id = 0;
pthread_mutex_lock(&g_fs.fd_mu);
struct zvfs_open_file *of, *tmp;
HASH_ITER(hh, g_fs.fd_table, of, tmp) {
(void)tmp;
if (of->inode == inode) {
handle_id = of->handle_id;
break;
}
}
pthread_mutex_unlock(&g_fs.fd_mu);
pthread_mutex_lock(&inode->mu);
uint64_t old_size = inode->logical_size;
pthread_mutex_unlock(&inode->mu);
if (new_size != old_size && handle_id != 0) {
if (blob_resize(handle_id, new_size) < 0) {
errno = EIO;
return -1;
}
} else if (new_size != old_size && handle_id == 0) {
/*
* 文件未被打开:需要临时 blob_open。
* 这种情况下 truncate(path, ...) 被调用但文件没有 fd。
*/
uint64_t temp_handle_id = 0;
if (blob_open(inode->blob_id, O_RDWR, &temp_handle_id) < 0) {
errno = EIO;
return -1;
}
int rc = blob_resize(temp_handle_id, new_size);
blob_close(temp_handle_id);
if (rc < 0) { errno = EIO; return -1; }
}
pthread_mutex_lock(&inode->mu);
inode_update_size(inode, real_fd, new_size);
pthread_mutex_unlock(&inode->mu);
return 0;
}
/* ------------------------------------------------------------------ */
/* ftruncate / ftruncate64 */
/* ------------------------------------------------------------------ */
int
ftruncate(int fd, off_t length)
{
ZVFS_HOOK_ENTER();
struct zvfs_open_file *of = NULL;
if (!ZVFS_IN_HOOK()) {
pthread_mutex_lock(&g_fs.fd_mu);
of = openfile_lookup(fd);
pthread_mutex_unlock(&g_fs.fd_mu);
}
if (!of) {
int r = real_ftruncate(fd, length);
ZVFS_HOOK_LEAVE();
return r;
}
zvfs_ensure_init();
if (length < 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }
int r = zvfs_truncate_inode_with_handle(of->inode, fd, (uint64_t)length);
ZVFS_HOOK_LEAVE();
return r;
}
int ftruncate64(int fd, off_t length) { return ftruncate(fd, length); }
/* ------------------------------------------------------------------ */
/* truncate / truncate64按路径 */
/* ------------------------------------------------------------------ */
int
truncate(const char *path, off_t length)
{
ZVFS_HOOK_ENTER();
if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_path(path)) {
int r = real_truncate(path, length);
ZVFS_HOOK_LEAVE();
return r;
}
zvfs_ensure_init();
if (length < 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }
/* 查 path_cache 拿 inode */
pthread_mutex_lock(&g_fs.path_mu);
struct zvfs_path_entry *pe = path_cache_lookup(path);
struct zvfs_inode *inode = pe ? pe->inode : NULL;
pthread_mutex_unlock(&g_fs.path_mu);
if (!inode) {
/*
* inode 不在缓存:文件存在于 FS 但从未被 open。
* 需要读 xattr 拿 blob_id临时构建 inode。
* 最简单的做法:先 real_open再走 zvfs 路径,再 real_close。
* 这里直接调 real_truncate 改 st_size但 blob 不会被截断。
*
* 更正确的做法open + ftruncate + close。
* 调用方通常不会在 file 未被打开的情况下做 truncate
* 所以这里先报 ENOENT找不到 zvfs inode作为安全兜底。
*/
errno = ENOENT;
ZVFS_HOOK_LEAVE();
return -1;
}
int r = zvfs_truncate_inode_with_handle(inode, -1, (uint64_t)length);
/* 同步真实文件 st_sizereal_truncate 更新磁盘元数据) */
if (r == 0)
real_truncate(path, length);
ZVFS_HOOK_LEAVE();
return r;
}
int truncate64(const char *path, off_t length) { return truncate(path, length); }
/* ------------------------------------------------------------------ */
/* fallocate */
/* ------------------------------------------------------------------ */
int
fallocate(int fd, int mode, off_t offset, off_t len)
{
ZVFS_HOOK_ENTER();
struct zvfs_open_file *of = NULL;
if (!ZVFS_IN_HOOK()) {
pthread_mutex_lock(&g_fs.fd_mu);
of = openfile_lookup(fd);
pthread_mutex_unlock(&g_fs.fd_mu);
}
if (!of) {
int r = real_fallocate(fd, mode, offset, len);
ZVFS_HOOK_LEAVE();
return r;
}
zvfs_ensure_init();
if (offset < 0 || len <= 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }
/* FALLOC_FL_PUNCH_HOLE打孔暂不支持 */
if (mode & FALLOC_FL_PUNCH_HOLE) {
errno = ENOTSUP;
ZVFS_HOOK_LEAVE();
return -1;
}
uint64_t new_end = (uint64_t)offset + (uint64_t)len;
uint64_t alloc_end = new_end;
/*
* ZVFS 读路径会按 blob 当前 cluster 大小做越界检查,因此这里不能只
* 更新 logical_size/st_size必须同步把 blob capacity 扩到目标范围。
*
* 对 KEEP_SIZE
* 预分配空间,但不改变文件逻辑大小。
* 对普通 fallocate
* 预分配空间,并把逻辑大小扩展到 offset + len。
*/
pthread_mutex_lock(&of->inode->mu);
if (alloc_end < of->inode->logical_size)
alloc_end = of->inode->logical_size;
pthread_mutex_unlock(&of->inode->mu);
if (blob_resize(of->handle_id, alloc_end) < 0) {
errno = EIO;
ZVFS_HOOK_LEAVE();
return -1;
}
if ((mode & FALLOC_FL_KEEP_SIZE) == 0) {
pthread_mutex_lock(&of->inode->mu);
if (new_end > of->inode->logical_size)
inode_update_size(of->inode, fd, new_end);
pthread_mutex_unlock(&of->inode->mu);
}
ZVFS_HOOK_LEAVE();
return 0;
}
/* ------------------------------------------------------------------ */
/* posix_fallocate */
/* ------------------------------------------------------------------ */
int
posix_fallocate(int fd, off_t offset, off_t len)
{
ZVFS_HOOK_ENTER();
struct zvfs_open_file *of = NULL;
if (!ZVFS_IN_HOOK()) {
pthread_mutex_lock(&g_fs.fd_mu);
of = openfile_lookup(fd);
pthread_mutex_unlock(&g_fs.fd_mu);
}
if (!of) {
int r = real_posix_fallocate(fd, offset, len);
ZVFS_HOOK_LEAVE();
return r;
}
zvfs_ensure_init();
/*
* posix_fallocate 不接受 mode 参数,语义等价于 fallocate(fd, 0, ...)。
* 注意posix_fallocate 出错时返回错误码(正值),不设置 errno。
*/
if (offset < 0 || len <= 0) { ZVFS_HOOK_LEAVE(); return EINVAL; }
uint64_t new_end = (uint64_t)offset + (uint64_t)len;
uint64_t alloc_end = new_end;
pthread_mutex_lock(&of->inode->mu);
if (alloc_end < of->inode->logical_size)
alloc_end = of->inode->logical_size;
pthread_mutex_unlock(&of->inode->mu);
if (blob_resize(of->handle_id, alloc_end) < 0) {
ZVFS_HOOK_LEAVE();
return errno ? errno : EIO;
}
pthread_mutex_lock(&of->inode->mu);
if (new_end > of->inode->logical_size)
inode_update_size(of->inode, fd, new_end);
pthread_mutex_unlock(&of->inode->mu);
ZVFS_HOOK_LEAVE();
return 0;
}