323 lines
9.2 KiB
C
323 lines
9.2 KiB
C
#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 指针做 truncate(path / 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_size(real_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;
|
||
}
|