mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:56:38 +00:00
Previous patch introduced a limit of max. 1024 simultaneous xattr FIDs. This patch introduces an option "max_attr" that allows to override this limit, just for the case that some user might run into this limit for some reason, even if unlikely; or for reducing the limit further down (e.g. that default limit of 1024 would cap at max. 64 MiB host memory, at least on Linux hosts where the limit per xattr is 64k). This new "max_xattr" option can be specified with both -fsdev and -virtfs command line options, with the "local" and the "synth" fs drivers. The previous limit of 1024 is preserved as the default value. Link: https://lore.kernel.org/qemu-devel/b7631ac0d8dde0629bc7c4f2c4185d9f57b962b4.1781361555.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
693 lines
18 KiB
C
693 lines
18 KiB
C
/*
|
|
* 9p synthetic file system support
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Malahal Naineni <malahal@us.ibm.com>
|
|
* Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
* the COPYING file in the top-level directory.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Not so fast! You might want to read the 9p developer docs first:
|
|
* https://wiki.qemu.org/Documentation/9p
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "9p.h"
|
|
#include "fsdev/qemu-fsdev.h"
|
|
#include "9p-synth.h"
|
|
#include "qemu/rcu.h"
|
|
#include "qemu/rcu_queue.h"
|
|
#include "qemu/cutils.h"
|
|
#include "system/qtest.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/option.h"
|
|
|
|
/* Root node for synth file system */
|
|
static V9fsSynthNode synth_root = {
|
|
.name = "/",
|
|
.actual_attr = {
|
|
.mode = 0555 | S_IFDIR,
|
|
.nlink = 1,
|
|
},
|
|
.attr = &synth_root.actual_attr,
|
|
};
|
|
|
|
static QemuMutex synth_mutex;
|
|
static int synth_node_count;
|
|
/* set to 1 when the synth fs is ready */
|
|
static int synth_fs;
|
|
|
|
static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
|
|
const char *name,
|
|
V9fsSynthNodeAttr *attr, int inode)
|
|
{
|
|
V9fsSynthNode *node;
|
|
|
|
/* Add directory type and remove write bits */
|
|
mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
|
|
node = g_new0(V9fsSynthNode, 1);
|
|
if (attr) {
|
|
/* We are adding .. or . entries */
|
|
node->attr = attr;
|
|
node->attr->nlink++;
|
|
} else {
|
|
node->attr = &node->actual_attr;
|
|
node->attr->inode = inode;
|
|
node->attr->nlink = 1;
|
|
/* We don't allow write to directories */
|
|
node->attr->mode = mode;
|
|
node->attr->write = NULL;
|
|
node->attr->read = NULL;
|
|
}
|
|
node->private = node;
|
|
pstrcpy(node->name, sizeof(node->name), name);
|
|
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
|
|
return node;
|
|
}
|
|
|
|
int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
|
|
const char *name, V9fsSynthNode **result)
|
|
{
|
|
V9fsSynthNode *node, *tmp;
|
|
|
|
if (!synth_fs) {
|
|
return -EAGAIN;
|
|
}
|
|
if (!name || (strlen(name) >= NAME_MAX)) {
|
|
return -EINVAL;
|
|
}
|
|
if (!parent) {
|
|
parent = &synth_root;
|
|
}
|
|
QEMU_LOCK_GUARD(&synth_mutex);
|
|
QLIST_FOREACH(tmp, &parent->child, sibling) {
|
|
if (!strcmp(tmp->name, name)) {
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
/* Add the name */
|
|
node = v9fs_add_dir_node(parent, mode, name, NULL, ++synth_node_count);
|
|
v9fs_add_dir_node(node, parent->attr->mode, "..",
|
|
parent->attr, parent->attr->inode);
|
|
v9fs_add_dir_node(node, node->attr->mode, ".",
|
|
node->attr, node->attr->inode);
|
|
*result = node;
|
|
return 0;
|
|
}
|
|
|
|
int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
|
|
const char *name, v9fs_synth_read read,
|
|
v9fs_synth_write write, void *arg)
|
|
{
|
|
V9fsSynthNode *node, *tmp;
|
|
|
|
if (!synth_fs) {
|
|
return -EAGAIN;
|
|
}
|
|
if (!name || (strlen(name) >= NAME_MAX)) {
|
|
return -EINVAL;
|
|
}
|
|
if (!parent) {
|
|
parent = &synth_root;
|
|
}
|
|
|
|
QEMU_LOCK_GUARD(&synth_mutex);
|
|
QLIST_FOREACH(tmp, &parent->child, sibling) {
|
|
if (!strcmp(tmp->name, name)) {
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
/* Add file type and remove write bits */
|
|
mode = ((mode & 0777) | S_IFREG);
|
|
node = g_new0(V9fsSynthNode, 1);
|
|
node->attr = &node->actual_attr;
|
|
node->attr->inode = ++synth_node_count;
|
|
node->attr->nlink = 1;
|
|
node->attr->read = read;
|
|
node->attr->write = write;
|
|
node->attr->mode = mode;
|
|
node->private = arg;
|
|
pstrcpy(node->name, sizeof(node->name), name);
|
|
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
|
|
return 0;
|
|
}
|
|
|
|
static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
|
|
{
|
|
stbuf->st_dev = 0;
|
|
stbuf->st_ino = node->attr->inode;
|
|
stbuf->st_mode = node->attr->mode;
|
|
stbuf->st_nlink = node->attr->nlink;
|
|
stbuf->st_uid = 0;
|
|
stbuf->st_gid = 0;
|
|
stbuf->st_rdev = 0;
|
|
stbuf->st_size = 0;
|
|
stbuf->st_blksize = 0;
|
|
stbuf->st_blocks = 0;
|
|
stbuf->st_atime = 0;
|
|
stbuf->st_mtime = 0;
|
|
stbuf->st_ctime = 0;
|
|
}
|
|
|
|
static int synth_lstat(FsContext *fs_ctx,
|
|
V9fsPath *fs_path, struct stat *stbuf)
|
|
{
|
|
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
|
|
|
|
synth_fill_statbuf(node, stbuf);
|
|
return 0;
|
|
}
|
|
|
|
static int synth_fstat(FsContext *fs_ctx, int fid_type,
|
|
V9fsFidOpenState *fs, struct stat *stbuf)
|
|
{
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
synth_fill_statbuf(synth_open->node, stbuf);
|
|
return 0;
|
|
}
|
|
|
|
static int synth_opendir(FsContext *ctx,
|
|
V9fsPath *fs_path, V9fsFidOpenState *fs)
|
|
{
|
|
V9fsSynthOpenState *synth_open;
|
|
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
|
|
|
|
/*
|
|
* V9fsSynthOpenState contains 'struct dirent' which have OS-specific
|
|
* properties, thus it's zero cleared on allocation here and below
|
|
* in synth_open.
|
|
*/
|
|
synth_open = g_new0(V9fsSynthOpenState, 1);
|
|
synth_open->node = node;
|
|
node->open_count++;
|
|
fs->private = synth_open;
|
|
return 0;
|
|
}
|
|
|
|
static int synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
|
|
{
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
V9fsSynthNode *node = synth_open->node;
|
|
|
|
node->open_count--;
|
|
g_free(synth_open);
|
|
fs->private = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static off_t synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
|
|
{
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
return synth_open->offset;
|
|
}
|
|
|
|
static void synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
|
|
{
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
synth_open->offset = off;
|
|
}
|
|
|
|
static void synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
|
|
{
|
|
synth_seekdir(ctx, fs, 0);
|
|
}
|
|
|
|
static void synth_direntry(V9fsSynthNode *node,
|
|
struct dirent *entry, off_t off)
|
|
{
|
|
size_t sz = strlen(node->name) + 1;
|
|
/*
|
|
* 'entry' is always inside of V9fsSynthOpenState which have NAME_MAX
|
|
* back padding. Ensure we do not overflow it.
|
|
*/
|
|
g_assert(sizeof(struct dirent) + NAME_MAX >=
|
|
offsetof(struct dirent, d_name) + sz);
|
|
memcpy(entry->d_name, node->name, sz);
|
|
entry->d_ino = node->attr->inode;
|
|
#ifdef CONFIG_DARWIN
|
|
entry->d_seekoff = off + 1;
|
|
#else
|
|
entry->d_off = off + 1;
|
|
#endif
|
|
}
|
|
|
|
static struct dirent *synth_get_dentry(V9fsSynthNode *dir,
|
|
struct dirent *entry, off_t off)
|
|
{
|
|
int i = 0;
|
|
V9fsSynthNode *node;
|
|
|
|
rcu_read_lock();
|
|
QLIST_FOREACH(node, &dir->child, sibling) {
|
|
/* This is the off child of the directory */
|
|
if (i == off) {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
rcu_read_unlock();
|
|
if (!node) {
|
|
/* end of directory */
|
|
return NULL;
|
|
}
|
|
synth_direntry(node, entry, off);
|
|
return entry;
|
|
}
|
|
|
|
static struct dirent *synth_readdir(FsContext *ctx, V9fsFidOpenState *fs)
|
|
{
|
|
struct dirent *entry;
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
V9fsSynthNode *node = synth_open->node;
|
|
entry = synth_get_dentry(node, &synth_open->dent, synth_open->offset);
|
|
if (entry) {
|
|
synth_open->offset++;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
static int synth_open(FsContext *ctx, V9fsPath *fs_path,
|
|
int flags, V9fsFidOpenState *fs)
|
|
{
|
|
V9fsSynthOpenState *synth_open;
|
|
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
|
|
|
|
synth_open = g_new0(V9fsSynthOpenState, 1);
|
|
synth_open->node = node;
|
|
node->open_count++;
|
|
fs->private = synth_open;
|
|
return 0;
|
|
}
|
|
|
|
static int synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
|
|
const char *name, int flags,
|
|
FsCred *credp, V9fsFidOpenState *fs)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_close(FsContext *ctx, V9fsFidOpenState *fs)
|
|
{
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
V9fsSynthNode *node = synth_open->node;
|
|
|
|
node->open_count--;
|
|
g_free(synth_open);
|
|
fs->private = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
|
|
const struct iovec *iov,
|
|
int iovcnt, off_t offset)
|
|
{
|
|
int i, count = 0, wcount;
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
V9fsSynthNode *node = synth_open->node;
|
|
if (!node->attr->write) {
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
for (i = 0; i < iovcnt; i++) {
|
|
wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
|
|
offset, node->private);
|
|
offset += wcount;
|
|
count += wcount;
|
|
/* If we wrote less than requested. we are done */
|
|
if (wcount < iov[i].iov_len) {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
|
|
const struct iovec *iov,
|
|
int iovcnt, off_t offset)
|
|
{
|
|
int i, count = 0, rcount;
|
|
V9fsSynthOpenState *synth_open = fs->private;
|
|
V9fsSynthNode *node = synth_open->node;
|
|
if (!node->attr->read) {
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
for (i = 0; i < iovcnt; i++) {
|
|
rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
|
|
offset, node->private);
|
|
offset += rcount;
|
|
count += rcount;
|
|
/* If we read less than requested. we are done */
|
|
if (rcount < iov[i].iov_len) {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
|
|
off_t size)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_mknod(FsContext *fs_ctx, V9fsPath *path,
|
|
const char *buf, FsCred *credp)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
|
|
const char *buf, FsCred *credp)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t synth_readlink(FsContext *fs_ctx, V9fsPath *path,
|
|
char *buf, size_t bufsz)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_symlink(FsContext *fs_ctx, const char *oldpath,
|
|
V9fsPath *newpath, const char *buf, FsCred *credp)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
|
|
V9fsPath *newpath, const char *buf)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_rename(FsContext *ctx, const char *oldpath,
|
|
const char *newpath)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
|
|
const struct timespec *buf)
|
|
{
|
|
errno = EPERM;
|
|
return 0;
|
|
}
|
|
|
|
static int synth_futimens(FsContext *fs_ctx, int fid_type, V9fsFidOpenState *fs,
|
|
const struct timespec *buf)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_remove(FsContext *ctx, const char *path)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_fsync(FsContext *ctx, int fid_type,
|
|
V9fsFidOpenState *fs, int datasync)
|
|
{
|
|
errno = ENOSYS;
|
|
return 0;
|
|
}
|
|
|
|
static int synth_statfs(FsContext *s, V9fsPath *fs_path,
|
|
struct statfs *stbuf)
|
|
{
|
|
stbuf->f_type = 0xABCD;
|
|
stbuf->f_bsize = 512;
|
|
stbuf->f_blocks = 0;
|
|
stbuf->f_files = synth_node_count;
|
|
#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD)
|
|
stbuf->f_namelen = NAME_MAX;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t synth_lgetxattr(FsContext *ctx, V9fsPath *path,
|
|
const char *name, void *value, size_t size)
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t synth_llistxattr(FsContext *ctx, V9fsPath *path,
|
|
void *value, size_t size)
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_lsetxattr(FsContext *ctx, V9fsPath *path,
|
|
const char *name, void *value,
|
|
size_t size, int flags)
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_lremovexattr(FsContext *ctx,
|
|
V9fsPath *path, const char *name)
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
|
|
const char *name, V9fsPath *target)
|
|
{
|
|
V9fsSynthNode *node;
|
|
V9fsSynthNode *dir_node;
|
|
|
|
/* "." and ".." are not allowed */
|
|
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
|
|
}
|
|
if (!dir_path) {
|
|
dir_node = &synth_root;
|
|
} else {
|
|
dir_node = *(V9fsSynthNode **)dir_path->data;
|
|
}
|
|
if (!strcmp(name, "/")) {
|
|
node = dir_node;
|
|
goto out;
|
|
}
|
|
/* search for the name in the children */
|
|
rcu_read_lock();
|
|
QLIST_FOREACH(node, &dir_node->child, sibling) {
|
|
if (!strcmp(node->name, name)) {
|
|
break;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
if (!node) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
out:
|
|
/* Copy the node pointer to fid */
|
|
g_free(target->data);
|
|
target->data = g_memdup(&node, sizeof(void *));
|
|
target->size = sizeof(void *);
|
|
return 0;
|
|
}
|
|
|
|
static int synth_renameat(FsContext *ctx, V9fsPath *olddir,
|
|
const char *old_name, V9fsPath *newdir,
|
|
const char *new_name)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static int synth_unlinkat(FsContext *ctx, V9fsPath *dir,
|
|
const char *name, int flags)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t v9fs_synth_qtest_write(void *buf, int len, off_t offset,
|
|
void *arg)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static ssize_t v9fs_synth_qtest_flush_write(void *buf, int len, off_t offset,
|
|
void *arg)
|
|
{
|
|
bool should_block = !!*(uint8_t *)buf;
|
|
|
|
if (should_block) {
|
|
/* This will cause the server to call us again until we're cancelled */
|
|
errno = EINTR;
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int synth_init(FsContext *ctx, Error **errp)
|
|
{
|
|
QLIST_INIT(&synth_root.child);
|
|
qemu_mutex_init(&synth_mutex);
|
|
|
|
/* Add "." and ".." entries for root */
|
|
v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
|
|
"..", synth_root.attr, synth_root.attr->inode);
|
|
v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
|
|
".", synth_root.attr, synth_root.attr->inode);
|
|
|
|
/* Mark the subsystem is ready for use */
|
|
synth_fs = 1;
|
|
|
|
if (qtest_enabled()) {
|
|
V9fsSynthNode *node = NULL;
|
|
int i, ret;
|
|
|
|
/* Directory hierarchy for WALK test */
|
|
for (i = 0; i < P9_MAXWELEM; i++) {
|
|
char *name = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
|
|
|
|
ret = qemu_v9fs_synth_mkdir(node, 0700, name, &node);
|
|
assert(!ret);
|
|
g_free(name);
|
|
}
|
|
|
|
/* File for LOPEN test */
|
|
ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_LOPEN_FILE,
|
|
NULL, NULL, ctx);
|
|
assert(!ret);
|
|
|
|
/* File for WRITE test */
|
|
ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_WRITE_FILE,
|
|
NULL, v9fs_synth_qtest_write, ctx);
|
|
assert(!ret);
|
|
|
|
/* File for FLUSH test */
|
|
ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_FLUSH_FILE,
|
|
NULL, v9fs_synth_qtest_flush_write,
|
|
ctx);
|
|
assert(!ret);
|
|
|
|
/* Directory for READDIR test */
|
|
{
|
|
V9fsSynthNode *dir = NULL;
|
|
ret = qemu_v9fs_synth_mkdir(
|
|
NULL, 0700, QTEST_V9FS_SYNTH_READDIR_DIR, &dir
|
|
);
|
|
assert(!ret);
|
|
for (i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
|
|
char *name = g_strdup_printf(
|
|
QTEST_V9FS_SYNTH_READDIR_FILE, i
|
|
);
|
|
ret = qemu_v9fs_synth_add_file(
|
|
dir, 0, name, NULL, NULL, ctx
|
|
);
|
|
assert(!ret);
|
|
g_free(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int synth_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
|
|
{
|
|
uint64_t val = qemu_opt_get_number(opts, "max_xattr",
|
|
V9FS_MAX_XATTR_DEFAULT);
|
|
if (val > UINT32_MAX) {
|
|
error_setg(errp, "max_xattr value '%s' too large",
|
|
qemu_opt_get(opts, "max_xattr"));
|
|
return -1;
|
|
}
|
|
fse->max_xattr = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FileOperations synth_ops = {
|
|
.parse_opts = synth_parse_opts,
|
|
.init = synth_init,
|
|
.lstat = synth_lstat,
|
|
.readlink = synth_readlink,
|
|
.close = synth_close,
|
|
.closedir = synth_closedir,
|
|
.open = synth_open,
|
|
.opendir = synth_opendir,
|
|
.rewinddir = synth_rewinddir,
|
|
.telldir = synth_telldir,
|
|
.readdir = synth_readdir,
|
|
.seekdir = synth_seekdir,
|
|
.preadv = synth_preadv,
|
|
.pwritev = synth_pwritev,
|
|
.chmod = synth_chmod,
|
|
.mknod = synth_mknod,
|
|
.mkdir = synth_mkdir,
|
|
.fstat = synth_fstat,
|
|
.open2 = synth_open2,
|
|
.symlink = synth_symlink,
|
|
.link = synth_link,
|
|
.truncate = synth_truncate,
|
|
.rename = synth_rename,
|
|
.chown = synth_chown,
|
|
.utimensat = synth_utimensat,
|
|
.remove = synth_remove,
|
|
.fsync = synth_fsync,
|
|
.statfs = synth_statfs,
|
|
.lgetxattr = synth_lgetxattr,
|
|
.llistxattr = synth_llistxattr,
|
|
.lsetxattr = synth_lsetxattr,
|
|
.lremovexattr = synth_lremovexattr,
|
|
.name_to_path = synth_name_to_path,
|
|
.renameat = synth_renameat,
|
|
.unlinkat = synth_unlinkat,
|
|
.has_valid_file_handle = synth_has_valid_file_handle,
|
|
.ftruncate = synth_ftruncate,
|
|
.futimens = synth_futimens,
|
|
};
|