302 lines
8.7 KiB
C
302 lines
8.7 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();
|
||
|
||
/*
|
||
* O_APPEND fd 的 lseek:POSIX 允许 lseek,但下次 write 时
|
||
* 仍会从文件末尾写。lseek 只影响 read 的位置。
|
||
* 我们照常更新 of->offset。
|
||
*/
|
||
pthread_mutex_lock(&of->inode->mu); /* SEEK_END 需读 logical_size */
|
||
uint64_t new_off = openfile_seek(of, (int64_t)offset, whence);
|
||
pthread_mutex_unlock(&of->inode->mu);
|
||
|
||
if (new_off == (uint64_t)-1) {
|
||
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 的 openfile 做 truncate。
|
||
* 找到任意一个打开该 inode 的 openfile 取其 handle。
|
||
*/
|
||
static int
|
||
zvfs_truncate_inode_with_handle(struct zvfs_inode *inode,
|
||
int real_fd, uint64_t new_size)
|
||
{
|
||
/* 在 fd_table 里找一个指向该 inode 的 openfile 取 handle */
|
||
struct zvfs_blob_handle *handle = NULL;
|
||
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 = of->handle;
|
||
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) {
|
||
if (blob_resize(handle, new_size) < 0) {
|
||
errno = EIO;
|
||
return -1;
|
||
}
|
||
} else if (new_size != old_size && !handle) {
|
||
/*
|
||
* 文件未被打开:需要临时 blob_open。
|
||
* 这种情况下 truncate(path, ...) 被调用但文件没有 fd。
|
||
*/
|
||
handle = blob_open(inode->blob_id);
|
||
if (!handle) { errno = EIO; return -1; }
|
||
int rc = blob_resize(handle, new_size);
|
||
blob_close(handle);
|
||
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;
|
||
}
|
||
|
||
/* FALLOC_FL_KEEP_SIZE:预分配但不改变文件逻辑大小,直接返回 0 */
|
||
if (mode & FALLOC_FL_KEEP_SIZE) {
|
||
ZVFS_HOOK_LEAVE();
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 普通 fallocate(mode == 0):
|
||
* 确保 [offset, offset+len) 范围内的空间被"分配"。
|
||
* zvfs 的语义:把 logical_size 扩展到 max(logical_size, offset+len)。
|
||
* 不提前 blob_resize,因为 SPDK cluster 按写入时分配更高效。
|
||
*/
|
||
uint64_t new_end = (uint64_t)offset + (uint64_t)len;
|
||
|
||
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;
|
||
|
||
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;
|
||
}
|