14728 lines
441 KiB
Diff
14728 lines
441 KiB
Diff
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
|
|
Date: Mon, 27 Jan 2020 19:00:45 +0000
|
|
Subject: [PATCH] virtiofsd: Format imported files to qemu style
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Mostly using a set like:
|
|
|
|
indent -nut -i 4 -nlp -br -cs -ce --no-space-after-function-call-names file
|
|
clang-format -style=file -i -- file
|
|
clang-tidy -fix-errors -checks=readability-braces-around-statements file
|
|
clang-format -style=file -i -- file
|
|
|
|
With manual cleanups.
|
|
|
|
The .clang-format used is below.
|
|
|
|
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
Reviewed by: Aleksandar Markovic <amarkovic@wavecomp.com>
|
|
|
|
Language: Cpp
|
|
AlignAfterOpenBracket: Align
|
|
AlignConsecutiveAssignments: false # although we like it, it creates churn
|
|
AlignConsecutiveDeclarations: false
|
|
AlignEscapedNewlinesLeft: true
|
|
AlignOperands: true
|
|
AlignTrailingComments: false # churn
|
|
AllowAllParametersOfDeclarationOnNextLine: true
|
|
AllowShortBlocksOnASingleLine: false
|
|
AllowShortCaseLabelsOnASingleLine: false
|
|
AllowShortFunctionsOnASingleLine: None
|
|
AllowShortIfStatementsOnASingleLine: false
|
|
AllowShortLoopsOnASingleLine: false
|
|
AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account
|
|
AlwaysBreakBeforeMultilineStrings: false
|
|
BinPackArguments: true
|
|
BinPackParameters: true
|
|
BraceWrapping:
|
|
AfterControlStatement: false
|
|
AfterEnum: false
|
|
AfterFunction: true
|
|
AfterStruct: false
|
|
AfterUnion: false
|
|
BeforeElse: false
|
|
IndentBraces: false
|
|
BreakBeforeBinaryOperators: None
|
|
BreakBeforeBraces: Custom
|
|
BreakBeforeTernaryOperators: false
|
|
BreakStringLiterals: true
|
|
ColumnLimit: 80
|
|
ContinuationIndentWidth: 4
|
|
Cpp11BracedListStyle: false
|
|
DerivePointerAlignment: false
|
|
DisableFormat: false
|
|
ForEachMacros: [
|
|
'CPU_FOREACH',
|
|
'CPU_FOREACH_REVERSE',
|
|
'CPU_FOREACH_SAFE',
|
|
'IOMMU_NOTIFIER_FOREACH',
|
|
'QLIST_FOREACH',
|
|
'QLIST_FOREACH_ENTRY',
|
|
'QLIST_FOREACH_RCU',
|
|
'QLIST_FOREACH_SAFE',
|
|
'QLIST_FOREACH_SAFE_RCU',
|
|
'QSIMPLEQ_FOREACH',
|
|
'QSIMPLEQ_FOREACH_SAFE',
|
|
'QSLIST_FOREACH',
|
|
'QSLIST_FOREACH_SAFE',
|
|
'QTAILQ_FOREACH',
|
|
'QTAILQ_FOREACH_REVERSE',
|
|
'QTAILQ_FOREACH_SAFE',
|
|
'QTAILQ_RAW_FOREACH',
|
|
'RAMBLOCK_FOREACH'
|
|
]
|
|
IncludeCategories:
|
|
- Regex: '^"qemu/osdep.h'
|
|
Priority: -3
|
|
- Regex: '^"(block|chardev|crypto|disas|exec|fpu|hw|io|libdecnumber|migration|monitor|net|qapi|qemu|qom|standard-headers|sysemu|ui)/'
|
|
Priority: -2
|
|
- Regex: '^"(elf.h|qemu-common.h|glib-compat.h|qemu-io.h|trace-tcg.h)'
|
|
Priority: -1
|
|
- Regex: '.*'
|
|
Priority: 1
|
|
IncludeIsMainRegex: '$'
|
|
IndentCaseLabels: false
|
|
IndentWidth: 4
|
|
IndentWrappedFunctionNames: false
|
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ?
|
|
MacroBlockEnd: '.*_END$'
|
|
MaxEmptyLinesToKeep: 2
|
|
PointerAlignment: Right
|
|
ReflowComments: true
|
|
SortIncludes: true
|
|
SpaceAfterCStyleCast: false
|
|
SpaceBeforeAssignmentOperators: true
|
|
SpaceBeforeParens: ControlStatements
|
|
SpaceInEmptyParentheses: false
|
|
SpacesBeforeTrailingComments: 1
|
|
SpacesInContainerLiterals: true
|
|
SpacesInParentheses: false
|
|
SpacesInSquareBrackets: false
|
|
Standard: Auto
|
|
UseTab: Never
|
|
...
|
|
|
|
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
(cherry picked from commit 7387863d033e8028aa09a815736617a7c4490827)
|
|
---
|
|
tools/virtiofsd/buffer.c | 434 +--
|
|
tools/virtiofsd/fuse.h | 1572 +++++------
|
|
tools/virtiofsd/fuse_common.h | 730 ++---
|
|
tools/virtiofsd/fuse_i.h | 121 +-
|
|
tools/virtiofsd/fuse_log.c | 38 +-
|
|
tools/virtiofsd/fuse_log.h | 32 +-
|
|
tools/virtiofsd/fuse_lowlevel.c | 3638 +++++++++++++------------
|
|
tools/virtiofsd/fuse_lowlevel.h | 2392 ++++++++--------
|
|
tools/virtiofsd/fuse_misc.h | 30 +-
|
|
tools/virtiofsd/fuse_opt.c | 659 ++---
|
|
tools/virtiofsd/fuse_opt.h | 79 +-
|
|
tools/virtiofsd/fuse_signals.c | 118 +-
|
|
tools/virtiofsd/helper.c | 506 ++--
|
|
tools/virtiofsd/passthrough_helpers.h | 33 +-
|
|
tools/virtiofsd/passthrough_ll.c | 2061 +++++++-------
|
|
15 files changed, 6382 insertions(+), 6061 deletions(-)
|
|
|
|
diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c
|
|
index aefb7dbf15..5df946c82c 100644
|
|
--- a/tools/virtiofsd/buffer.c
|
|
+++ b/tools/virtiofsd/buffer.c
|
|
@@ -1,252 +1,272 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- Functions for dealing with `struct fuse_buf` and `struct
|
|
- fuse_bufvec`.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * Functions for dealing with `struct fuse_buf` and `struct
|
|
+ * fuse_bufvec`.
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "config.h"
|
|
#include "fuse_i.h"
|
|
#include "fuse_lowlevel.h"
|
|
+#include <assert.h>
|
|
+#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
-#include <errno.h>
|
|
-#include <assert.h>
|
|
|
|
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
|
|
{
|
|
- size_t i;
|
|
- size_t size = 0;
|
|
-
|
|
- for (i = 0; i < bufv->count; i++) {
|
|
- if (bufv->buf[i].size == SIZE_MAX)
|
|
- size = SIZE_MAX;
|
|
- else
|
|
- size += bufv->buf[i].size;
|
|
- }
|
|
-
|
|
- return size;
|
|
+ size_t i;
|
|
+ size_t size = 0;
|
|
+
|
|
+ for (i = 0; i < bufv->count; i++) {
|
|
+ if (bufv->buf[i].size == SIZE_MAX) {
|
|
+ size = SIZE_MAX;
|
|
+ } else {
|
|
+ size += bufv->buf[i].size;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return size;
|
|
}
|
|
|
|
static size_t min_size(size_t s1, size_t s2)
|
|
{
|
|
- return s1 < s2 ? s1 : s2;
|
|
+ return s1 < s2 ? s1 : s2;
|
|
}
|
|
|
|
static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
|
|
- const struct fuse_buf *src, size_t src_off,
|
|
- size_t len)
|
|
+ const struct fuse_buf *src, size_t src_off,
|
|
+ size_t len)
|
|
{
|
|
- ssize_t res = 0;
|
|
- size_t copied = 0;
|
|
-
|
|
- while (len) {
|
|
- if (dst->flags & FUSE_BUF_FD_SEEK) {
|
|
- res = pwrite(dst->fd, (char *)src->mem + src_off, len,
|
|
- dst->pos + dst_off);
|
|
- } else {
|
|
- res = write(dst->fd, (char *)src->mem + src_off, len);
|
|
- }
|
|
- if (res == -1) {
|
|
- if (!copied)
|
|
- return -errno;
|
|
- break;
|
|
- }
|
|
- if (res == 0)
|
|
- break;
|
|
-
|
|
- copied += res;
|
|
- if (!(dst->flags & FUSE_BUF_FD_RETRY))
|
|
- break;
|
|
-
|
|
- src_off += res;
|
|
- dst_off += res;
|
|
- len -= res;
|
|
- }
|
|
-
|
|
- return copied;
|
|
+ ssize_t res = 0;
|
|
+ size_t copied = 0;
|
|
+
|
|
+ while (len) {
|
|
+ if (dst->flags & FUSE_BUF_FD_SEEK) {
|
|
+ res = pwrite(dst->fd, (char *)src->mem + src_off, len,
|
|
+ dst->pos + dst_off);
|
|
+ } else {
|
|
+ res = write(dst->fd, (char *)src->mem + src_off, len);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ if (!copied) {
|
|
+ return -errno;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if (res == 0) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ copied += res;
|
|
+ if (!(dst->flags & FUSE_BUF_FD_RETRY)) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ src_off += res;
|
|
+ dst_off += res;
|
|
+ len -= res;
|
|
+ }
|
|
+
|
|
+ return copied;
|
|
}
|
|
|
|
static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
|
|
- const struct fuse_buf *src, size_t src_off,
|
|
- size_t len)
|
|
+ const struct fuse_buf *src, size_t src_off,
|
|
+ size_t len)
|
|
{
|
|
- ssize_t res = 0;
|
|
- size_t copied = 0;
|
|
-
|
|
- while (len) {
|
|
- if (src->flags & FUSE_BUF_FD_SEEK) {
|
|
- res = pread(src->fd, (char *)dst->mem + dst_off, len,
|
|
- src->pos + src_off);
|
|
- } else {
|
|
- res = read(src->fd, (char *)dst->mem + dst_off, len);
|
|
- }
|
|
- if (res == -1) {
|
|
- if (!copied)
|
|
- return -errno;
|
|
- break;
|
|
- }
|
|
- if (res == 0)
|
|
- break;
|
|
-
|
|
- copied += res;
|
|
- if (!(src->flags & FUSE_BUF_FD_RETRY))
|
|
- break;
|
|
-
|
|
- dst_off += res;
|
|
- src_off += res;
|
|
- len -= res;
|
|
- }
|
|
-
|
|
- return copied;
|
|
+ ssize_t res = 0;
|
|
+ size_t copied = 0;
|
|
+
|
|
+ while (len) {
|
|
+ if (src->flags & FUSE_BUF_FD_SEEK) {
|
|
+ res = pread(src->fd, (char *)dst->mem + dst_off, len,
|
|
+ src->pos + src_off);
|
|
+ } else {
|
|
+ res = read(src->fd, (char *)dst->mem + dst_off, len);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ if (!copied) {
|
|
+ return -errno;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if (res == 0) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ copied += res;
|
|
+ if (!(src->flags & FUSE_BUF_FD_RETRY)) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dst_off += res;
|
|
+ src_off += res;
|
|
+ len -= res;
|
|
+ }
|
|
+
|
|
+ return copied;
|
|
}
|
|
|
|
static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
|
|
- const struct fuse_buf *src, size_t src_off,
|
|
- size_t len)
|
|
+ const struct fuse_buf *src, size_t src_off,
|
|
+ size_t len)
|
|
{
|
|
- char buf[4096];
|
|
- struct fuse_buf tmp = {
|
|
- .size = sizeof(buf),
|
|
- .flags = 0,
|
|
- };
|
|
- ssize_t res;
|
|
- size_t copied = 0;
|
|
-
|
|
- tmp.mem = buf;
|
|
-
|
|
- while (len) {
|
|
- size_t this_len = min_size(tmp.size, len);
|
|
- size_t read_len;
|
|
-
|
|
- res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
|
|
- if (res < 0) {
|
|
- if (!copied)
|
|
- return res;
|
|
- break;
|
|
- }
|
|
- if (res == 0)
|
|
- break;
|
|
-
|
|
- read_len = res;
|
|
- res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
|
|
- if (res < 0) {
|
|
- if (!copied)
|
|
- return res;
|
|
- break;
|
|
- }
|
|
- if (res == 0)
|
|
- break;
|
|
-
|
|
- copied += res;
|
|
-
|
|
- if (res < this_len)
|
|
- break;
|
|
-
|
|
- dst_off += res;
|
|
- src_off += res;
|
|
- len -= res;
|
|
- }
|
|
-
|
|
- return copied;
|
|
+ char buf[4096];
|
|
+ struct fuse_buf tmp = {
|
|
+ .size = sizeof(buf),
|
|
+ .flags = 0,
|
|
+ };
|
|
+ ssize_t res;
|
|
+ size_t copied = 0;
|
|
+
|
|
+ tmp.mem = buf;
|
|
+
|
|
+ while (len) {
|
|
+ size_t this_len = min_size(tmp.size, len);
|
|
+ size_t read_len;
|
|
+
|
|
+ res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
|
|
+ if (res < 0) {
|
|
+ if (!copied) {
|
|
+ return res;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if (res == 0) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ read_len = res;
|
|
+ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
|
|
+ if (res < 0) {
|
|
+ if (!copied) {
|
|
+ return res;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if (res == 0) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ copied += res;
|
|
+
|
|
+ if (res < this_len) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dst_off += res;
|
|
+ src_off += res;
|
|
+ len -= res;
|
|
+ }
|
|
+
|
|
+ return copied;
|
|
}
|
|
|
|
static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
|
|
- const struct fuse_buf *src, size_t src_off,
|
|
- size_t len, enum fuse_buf_copy_flags flags)
|
|
+ const struct fuse_buf *src, size_t src_off,
|
|
+ size_t len, enum fuse_buf_copy_flags flags)
|
|
{
|
|
- int src_is_fd = src->flags & FUSE_BUF_IS_FD;
|
|
- int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
|
|
-
|
|
- if (!src_is_fd && !dst_is_fd) {
|
|
- char *dstmem = (char *)dst->mem + dst_off;
|
|
- char *srcmem = (char *)src->mem + src_off;
|
|
-
|
|
- if (dstmem != srcmem) {
|
|
- if (dstmem + len <= srcmem || srcmem + len <= dstmem)
|
|
- memcpy(dstmem, srcmem, len);
|
|
- else
|
|
- memmove(dstmem, srcmem, len);
|
|
- }
|
|
-
|
|
- return len;
|
|
- } else if (!src_is_fd) {
|
|
- return fuse_buf_write(dst, dst_off, src, src_off, len);
|
|
- } else if (!dst_is_fd) {
|
|
- return fuse_buf_read(dst, dst_off, src, src_off, len);
|
|
- } else {
|
|
- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
|
- }
|
|
+ int src_is_fd = src->flags & FUSE_BUF_IS_FD;
|
|
+ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
|
|
+
|
|
+ if (!src_is_fd && !dst_is_fd) {
|
|
+ char *dstmem = (char *)dst->mem + dst_off;
|
|
+ char *srcmem = (char *)src->mem + src_off;
|
|
+
|
|
+ if (dstmem != srcmem) {
|
|
+ if (dstmem + len <= srcmem || srcmem + len <= dstmem) {
|
|
+ memcpy(dstmem, srcmem, len);
|
|
+ } else {
|
|
+ memmove(dstmem, srcmem, len);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+ } else if (!src_is_fd) {
|
|
+ return fuse_buf_write(dst, dst_off, src, src_off, len);
|
|
+ } else if (!dst_is_fd) {
|
|
+ return fuse_buf_read(dst, dst_off, src, src_off, len);
|
|
+ } else {
|
|
+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
|
+ }
|
|
}
|
|
|
|
static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
|
|
{
|
|
- if (bufv->idx < bufv->count)
|
|
- return &bufv->buf[bufv->idx];
|
|
- else
|
|
- return NULL;
|
|
+ if (bufv->idx < bufv->count) {
|
|
+ return &bufv->buf[bufv->idx];
|
|
+ } else {
|
|
+ return NULL;
|
|
+ }
|
|
}
|
|
|
|
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
|
|
{
|
|
- const struct fuse_buf *buf = fuse_bufvec_current(bufv);
|
|
-
|
|
- bufv->off += len;
|
|
- assert(bufv->off <= buf->size);
|
|
- if (bufv->off == buf->size) {
|
|
- assert(bufv->idx < bufv->count);
|
|
- bufv->idx++;
|
|
- if (bufv->idx == bufv->count)
|
|
- return 0;
|
|
- bufv->off = 0;
|
|
- }
|
|
- return 1;
|
|
+ const struct fuse_buf *buf = fuse_bufvec_current(bufv);
|
|
+
|
|
+ bufv->off += len;
|
|
+ assert(bufv->off <= buf->size);
|
|
+ if (bufv->off == buf->size) {
|
|
+ assert(bufv->idx < bufv->count);
|
|
+ bufv->idx++;
|
|
+ if (bufv->idx == bufv->count) {
|
|
+ return 0;
|
|
+ }
|
|
+ bufv->off = 0;
|
|
+ }
|
|
+ return 1;
|
|
}
|
|
|
|
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
|
|
- enum fuse_buf_copy_flags flags)
|
|
+ enum fuse_buf_copy_flags flags)
|
|
{
|
|
- size_t copied = 0;
|
|
-
|
|
- if (dstv == srcv)
|
|
- return fuse_buf_size(dstv);
|
|
-
|
|
- for (;;) {
|
|
- const struct fuse_buf *src = fuse_bufvec_current(srcv);
|
|
- const struct fuse_buf *dst = fuse_bufvec_current(dstv);
|
|
- size_t src_len;
|
|
- size_t dst_len;
|
|
- size_t len;
|
|
- ssize_t res;
|
|
-
|
|
- if (src == NULL || dst == NULL)
|
|
- break;
|
|
-
|
|
- src_len = src->size - srcv->off;
|
|
- dst_len = dst->size - dstv->off;
|
|
- len = min_size(src_len, dst_len);
|
|
-
|
|
- res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
|
|
- if (res < 0) {
|
|
- if (!copied)
|
|
- return res;
|
|
- break;
|
|
- }
|
|
- copied += res;
|
|
-
|
|
- if (!fuse_bufvec_advance(srcv, res) ||
|
|
- !fuse_bufvec_advance(dstv, res))
|
|
- break;
|
|
-
|
|
- if (res < len)
|
|
- break;
|
|
- }
|
|
-
|
|
- return copied;
|
|
+ size_t copied = 0;
|
|
+
|
|
+ if (dstv == srcv) {
|
|
+ return fuse_buf_size(dstv);
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ const struct fuse_buf *src = fuse_bufvec_current(srcv);
|
|
+ const struct fuse_buf *dst = fuse_bufvec_current(dstv);
|
|
+ size_t src_len;
|
|
+ size_t dst_len;
|
|
+ size_t len;
|
|
+ ssize_t res;
|
|
+
|
|
+ if (src == NULL || dst == NULL) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ src_len = src->size - srcv->off;
|
|
+ dst_len = dst->size - dstv->off;
|
|
+ len = min_size(src_len, dst_len);
|
|
+
|
|
+ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
|
|
+ if (res < 0) {
|
|
+ if (!copied) {
|
|
+ return res;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ copied += res;
|
|
+
|
|
+ if (!fuse_bufvec_advance(srcv, res) ||
|
|
+ !fuse_bufvec_advance(dstv, res)) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (res < len) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return copied;
|
|
}
|
|
diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h
|
|
index 3202fba6bb..7a4c713559 100644
|
|
--- a/tools/virtiofsd/fuse.h
|
|
+++ b/tools/virtiofsd/fuse.h
|
|
@@ -1,15 +1,15 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
#ifndef FUSE_H_
|
|
#define FUSE_H_
|
|
|
|
-/** @file
|
|
+/*
|
|
*
|
|
* This file defines the library interface of FUSE
|
|
*
|
|
@@ -19,15 +19,15 @@
|
|
#include "fuse_common.h"
|
|
|
|
#include <fcntl.h>
|
|
-#include <time.h>
|
|
-#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.h>
|
|
+#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
+#include <time.h>
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Basic FUSE API *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Basic FUSE API
|
|
+ */
|
|
|
|
/** Handle for a FUSE filesystem */
|
|
struct fuse;
|
|
@@ -36,38 +36,39 @@ struct fuse;
|
|
* Readdir flags, passed to ->readdir()
|
|
*/
|
|
enum fuse_readdir_flags {
|
|
- /**
|
|
- * "Plus" mode.
|
|
- *
|
|
- * The kernel wants to prefill the inode cache during readdir. The
|
|
- * filesystem may honour this by filling in the attributes and setting
|
|
- * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also
|
|
- * just ignore this flag completely.
|
|
- */
|
|
- FUSE_READDIR_PLUS = (1 << 0),
|
|
+ /**
|
|
+ * "Plus" mode.
|
|
+ *
|
|
+ * The kernel wants to prefill the inode cache during readdir. The
|
|
+ * filesystem may honour this by filling in the attributes and setting
|
|
+ * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also
|
|
+ * just ignore this flag completely.
|
|
+ */
|
|
+ FUSE_READDIR_PLUS = (1 << 0),
|
|
};
|
|
|
|
enum fuse_fill_dir_flags {
|
|
- /**
|
|
- * "Plus" mode: all file attributes are valid
|
|
- *
|
|
- * The attributes are used by the kernel to prefill the inode cache
|
|
- * during a readdir.
|
|
- *
|
|
- * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set
|
|
- * and vice versa.
|
|
- */
|
|
- FUSE_FILL_DIR_PLUS = (1 << 1),
|
|
+ /**
|
|
+ * "Plus" mode: all file attributes are valid
|
|
+ *
|
|
+ * The attributes are used by the kernel to prefill the inode cache
|
|
+ * during a readdir.
|
|
+ *
|
|
+ * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set
|
|
+ * and vice versa.
|
|
+ */
|
|
+ FUSE_FILL_DIR_PLUS = (1 << 1),
|
|
};
|
|
|
|
-/** Function to add an entry in a readdir() operation
|
|
+/**
|
|
+ * Function to add an entry in a readdir() operation
|
|
*
|
|
* The *off* parameter can be any non-zero value that enables the
|
|
* filesystem to identify the current point in the directory
|
|
* stream. It does not need to be the actual physical position. A
|
|
* value of zero is reserved to indicate that seeking in directories
|
|
* is not supported.
|
|
- *
|
|
+ *
|
|
* @param buf the buffer passed to the readdir() operation
|
|
* @param name the file name of the directory entry
|
|
* @param stat file attributes, can be NULL
|
|
@@ -75,9 +76,9 @@ enum fuse_fill_dir_flags {
|
|
* @param flags fill flags
|
|
* @return 1 if buffer is full, zero otherwise
|
|
*/
|
|
-typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
|
|
- const struct stat *stbuf, off_t off,
|
|
- enum fuse_fill_dir_flags flags);
|
|
+typedef int (*fuse_fill_dir_t)(void *buf, const char *name,
|
|
+ const struct stat *stbuf, off_t off,
|
|
+ enum fuse_fill_dir_flags flags);
|
|
/**
|
|
* Configuration of the high-level API
|
|
*
|
|
@@ -87,186 +88,186 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
|
|
* file system implementation.
|
|
*/
|
|
struct fuse_config {
|
|
- /**
|
|
- * If `set_gid` is non-zero, the st_gid attribute of each file
|
|
- * is overwritten with the value of `gid`.
|
|
- */
|
|
- int set_gid;
|
|
- unsigned int gid;
|
|
-
|
|
- /**
|
|
- * If `set_uid` is non-zero, the st_uid attribute of each file
|
|
- * is overwritten with the value of `uid`.
|
|
- */
|
|
- int set_uid;
|
|
- unsigned int uid;
|
|
-
|
|
- /**
|
|
- * If `set_mode` is non-zero, the any permissions bits set in
|
|
- * `umask` are unset in the st_mode attribute of each file.
|
|
- */
|
|
- int set_mode;
|
|
- unsigned int umask;
|
|
-
|
|
- /**
|
|
- * The timeout in seconds for which name lookups will be
|
|
- * cached.
|
|
- */
|
|
- double entry_timeout;
|
|
-
|
|
- /**
|
|
- * The timeout in seconds for which a negative lookup will be
|
|
- * cached. This means, that if file did not exist (lookup
|
|
- * retuned ENOENT), the lookup will only be redone after the
|
|
- * timeout, and the file/directory will be assumed to not
|
|
- * exist until then. A value of zero means that negative
|
|
- * lookups are not cached.
|
|
- */
|
|
- double negative_timeout;
|
|
-
|
|
- /**
|
|
- * The timeout in seconds for which file/directory attributes
|
|
- * (as returned by e.g. the `getattr` handler) are cached.
|
|
- */
|
|
- double attr_timeout;
|
|
-
|
|
- /**
|
|
- * Allow requests to be interrupted
|
|
- */
|
|
- int intr;
|
|
-
|
|
- /**
|
|
- * Specify which signal number to send to the filesystem when
|
|
- * a request is interrupted. The default is hardcoded to
|
|
- * USR1.
|
|
- */
|
|
- int intr_signal;
|
|
-
|
|
- /**
|
|
- * Normally, FUSE assigns inodes to paths only for as long as
|
|
- * the kernel is aware of them. With this option inodes are
|
|
- * instead remembered for at least this many seconds. This
|
|
- * will require more memory, but may be necessary when using
|
|
- * applications that make use of inode numbers.
|
|
- *
|
|
- * A number of -1 means that inodes will be remembered for the
|
|
- * entire life-time of the file-system process.
|
|
- */
|
|
- int remember;
|
|
-
|
|
- /**
|
|
- * The default behavior is that if an open file is deleted,
|
|
- * the file is renamed to a hidden file (.fuse_hiddenXXX), and
|
|
- * only removed when the file is finally released. This
|
|
- * relieves the filesystem implementation of having to deal
|
|
- * with this problem. This option disables the hiding
|
|
- * behavior, and files are removed immediately in an unlink
|
|
- * operation (or in a rename operation which overwrites an
|
|
- * existing file).
|
|
- *
|
|
- * It is recommended that you not use the hard_remove
|
|
- * option. When hard_remove is set, the following libc
|
|
- * functions fail on unlinked files (returning errno of
|
|
- * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2),
|
|
- * ftruncate(2), fstat(2), fchmod(2), fchown(2)
|
|
- */
|
|
- int hard_remove;
|
|
-
|
|
- /**
|
|
- * Honor the st_ino field in the functions getattr() and
|
|
- * fill_dir(). This value is used to fill in the st_ino field
|
|
- * in the stat(2), lstat(2), fstat(2) functions and the d_ino
|
|
- * field in the readdir(2) function. The filesystem does not
|
|
- * have to guarantee uniqueness, however some applications
|
|
- * rely on this value being unique for the whole filesystem.
|
|
- *
|
|
- * Note that this does *not* affect the inode that libfuse
|
|
- * and the kernel use internally (also called the "nodeid").
|
|
- */
|
|
- int use_ino;
|
|
-
|
|
- /**
|
|
- * If use_ino option is not given, still try to fill in the
|
|
- * d_ino field in readdir(2). If the name was previously
|
|
- * looked up, and is still in the cache, the inode number
|
|
- * found there will be used. Otherwise it will be set to -1.
|
|
- * If use_ino option is given, this option is ignored.
|
|
- */
|
|
- int readdir_ino;
|
|
-
|
|
- /**
|
|
- * This option disables the use of page cache (file content cache)
|
|
- * in the kernel for this filesystem. This has several affects:
|
|
- *
|
|
- * 1. Each read(2) or write(2) system call will initiate one
|
|
- * or more read or write operations, data will not be
|
|
- * cached in the kernel.
|
|
- *
|
|
- * 2. The return value of the read() and write() system calls
|
|
- * will correspond to the return values of the read and
|
|
- * write operations. This is useful for example if the
|
|
- * file size is not known in advance (before reading it).
|
|
- *
|
|
- * Internally, enabling this option causes fuse to set the
|
|
- * `direct_io` field of `struct fuse_file_info` - overwriting
|
|
- * any value that was put there by the file system.
|
|
- */
|
|
- int direct_io;
|
|
-
|
|
- /**
|
|
- * This option disables flushing the cache of the file
|
|
- * contents on every open(2). This should only be enabled on
|
|
- * filesystems where the file data is never changed
|
|
- * externally (not through the mounted FUSE filesystem). Thus
|
|
- * it is not suitable for network filesystems and other
|
|
- * intermediate filesystems.
|
|
- *
|
|
- * NOTE: if this option is not specified (and neither
|
|
- * direct_io) data is still cached after the open(2), so a
|
|
- * read(2) system call will not always initiate a read
|
|
- * operation.
|
|
- *
|
|
- * Internally, enabling this option causes fuse to set the
|
|
- * `keep_cache` field of `struct fuse_file_info` - overwriting
|
|
- * any value that was put there by the file system.
|
|
- */
|
|
- int kernel_cache;
|
|
-
|
|
- /**
|
|
- * This option is an alternative to `kernel_cache`. Instead of
|
|
- * unconditionally keeping cached data, the cached data is
|
|
- * invalidated on open(2) if if the modification time or the
|
|
- * size of the file has changed since it was last opened.
|
|
- */
|
|
- int auto_cache;
|
|
-
|
|
- /**
|
|
- * The timeout in seconds for which file attributes are cached
|
|
- * for the purpose of checking if auto_cache should flush the
|
|
- * file data on open.
|
|
- */
|
|
- int ac_attr_timeout_set;
|
|
- double ac_attr_timeout;
|
|
-
|
|
- /**
|
|
- * If this option is given the file-system handlers for the
|
|
- * following operations will not receive path information:
|
|
- * read, write, flush, release, fsync, readdir, releasedir,
|
|
- * fsyncdir, lock, ioctl and poll.
|
|
- *
|
|
- * For the truncate, getattr, chmod, chown and utimens
|
|
- * operations the path will be provided only if the struct
|
|
- * fuse_file_info argument is NULL.
|
|
- */
|
|
- int nullpath_ok;
|
|
-
|
|
- /**
|
|
- * The remaining options are used by libfuse internally and
|
|
- * should not be touched.
|
|
- */
|
|
- int show_help;
|
|
- char *modules;
|
|
- int debug;
|
|
+ /**
|
|
+ * If `set_gid` is non-zero, the st_gid attribute of each file
|
|
+ * is overwritten with the value of `gid`.
|
|
+ */
|
|
+ int set_gid;
|
|
+ unsigned int gid;
|
|
+
|
|
+ /**
|
|
+ * If `set_uid` is non-zero, the st_uid attribute of each file
|
|
+ * is overwritten with the value of `uid`.
|
|
+ */
|
|
+ int set_uid;
|
|
+ unsigned int uid;
|
|
+
|
|
+ /**
|
|
+ * If `set_mode` is non-zero, the any permissions bits set in
|
|
+ * `umask` are unset in the st_mode attribute of each file.
|
|
+ */
|
|
+ int set_mode;
|
|
+ unsigned int umask;
|
|
+
|
|
+ /**
|
|
+ * The timeout in seconds for which name lookups will be
|
|
+ * cached.
|
|
+ */
|
|
+ double entry_timeout;
|
|
+
|
|
+ /**
|
|
+ * The timeout in seconds for which a negative lookup will be
|
|
+ * cached. This means, that if file did not exist (lookup
|
|
+ * retuned ENOENT), the lookup will only be redone after the
|
|
+ * timeout, and the file/directory will be assumed to not
|
|
+ * exist until then. A value of zero means that negative
|
|
+ * lookups are not cached.
|
|
+ */
|
|
+ double negative_timeout;
|
|
+
|
|
+ /**
|
|
+ * The timeout in seconds for which file/directory attributes
|
|
+ * (as returned by e.g. the `getattr` handler) are cached.
|
|
+ */
|
|
+ double attr_timeout;
|
|
+
|
|
+ /**
|
|
+ * Allow requests to be interrupted
|
|
+ */
|
|
+ int intr;
|
|
+
|
|
+ /**
|
|
+ * Specify which signal number to send to the filesystem when
|
|
+ * a request is interrupted. The default is hardcoded to
|
|
+ * USR1.
|
|
+ */
|
|
+ int intr_signal;
|
|
+
|
|
+ /**
|
|
+ * Normally, FUSE assigns inodes to paths only for as long as
|
|
+ * the kernel is aware of them. With this option inodes are
|
|
+ * instead remembered for at least this many seconds. This
|
|
+ * will require more memory, but may be necessary when using
|
|
+ * applications that make use of inode numbers.
|
|
+ *
|
|
+ * A number of -1 means that inodes will be remembered for the
|
|
+ * entire life-time of the file-system process.
|
|
+ */
|
|
+ int remember;
|
|
+
|
|
+ /**
|
|
+ * The default behavior is that if an open file is deleted,
|
|
+ * the file is renamed to a hidden file (.fuse_hiddenXXX), and
|
|
+ * only removed when the file is finally released. This
|
|
+ * relieves the filesystem implementation of having to deal
|
|
+ * with this problem. This option disables the hiding
|
|
+ * behavior, and files are removed immediately in an unlink
|
|
+ * operation (or in a rename operation which overwrites an
|
|
+ * existing file).
|
|
+ *
|
|
+ * It is recommended that you not use the hard_remove
|
|
+ * option. When hard_remove is set, the following libc
|
|
+ * functions fail on unlinked files (returning errno of
|
|
+ * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2),
|
|
+ * ftruncate(2), fstat(2), fchmod(2), fchown(2)
|
|
+ */
|
|
+ int hard_remove;
|
|
+
|
|
+ /**
|
|
+ * Honor the st_ino field in the functions getattr() and
|
|
+ * fill_dir(). This value is used to fill in the st_ino field
|
|
+ * in the stat(2), lstat(2), fstat(2) functions and the d_ino
|
|
+ * field in the readdir(2) function. The filesystem does not
|
|
+ * have to guarantee uniqueness, however some applications
|
|
+ * rely on this value being unique for the whole filesystem.
|
|
+ *
|
|
+ * Note that this does *not* affect the inode that libfuse
|
|
+ * and the kernel use internally (also called the "nodeid").
|
|
+ */
|
|
+ int use_ino;
|
|
+
|
|
+ /**
|
|
+ * If use_ino option is not given, still try to fill in the
|
|
+ * d_ino field in readdir(2). If the name was previously
|
|
+ * looked up, and is still in the cache, the inode number
|
|
+ * found there will be used. Otherwise it will be set to -1.
|
|
+ * If use_ino option is given, this option is ignored.
|
|
+ */
|
|
+ int readdir_ino;
|
|
+
|
|
+ /**
|
|
+ * This option disables the use of page cache (file content cache)
|
|
+ * in the kernel for this filesystem. This has several affects:
|
|
+ *
|
|
+ * 1. Each read(2) or write(2) system call will initiate one
|
|
+ * or more read or write operations, data will not be
|
|
+ * cached in the kernel.
|
|
+ *
|
|
+ * 2. The return value of the read() and write() system calls
|
|
+ * will correspond to the return values of the read and
|
|
+ * write operations. This is useful for example if the
|
|
+ * file size is not known in advance (before reading it).
|
|
+ *
|
|
+ * Internally, enabling this option causes fuse to set the
|
|
+ * `direct_io` field of `struct fuse_file_info` - overwriting
|
|
+ * any value that was put there by the file system.
|
|
+ */
|
|
+ int direct_io;
|
|
+
|
|
+ /**
|
|
+ * This option disables flushing the cache of the file
|
|
+ * contents on every open(2). This should only be enabled on
|
|
+ * filesystems where the file data is never changed
|
|
+ * externally (not through the mounted FUSE filesystem). Thus
|
|
+ * it is not suitable for network filesystems and other
|
|
+ * intermediate filesystems.
|
|
+ *
|
|
+ * NOTE: if this option is not specified (and neither
|
|
+ * direct_io) data is still cached after the open(2), so a
|
|
+ * read(2) system call will not always initiate a read
|
|
+ * operation.
|
|
+ *
|
|
+ * Internally, enabling this option causes fuse to set the
|
|
+ * `keep_cache` field of `struct fuse_file_info` - overwriting
|
|
+ * any value that was put there by the file system.
|
|
+ */
|
|
+ int kernel_cache;
|
|
+
|
|
+ /**
|
|
+ * This option is an alternative to `kernel_cache`. Instead of
|
|
+ * unconditionally keeping cached data, the cached data is
|
|
+ * invalidated on open(2) if if the modification time or the
|
|
+ * size of the file has changed since it was last opened.
|
|
+ */
|
|
+ int auto_cache;
|
|
+
|
|
+ /**
|
|
+ * The timeout in seconds for which file attributes are cached
|
|
+ * for the purpose of checking if auto_cache should flush the
|
|
+ * file data on open.
|
|
+ */
|
|
+ int ac_attr_timeout_set;
|
|
+ double ac_attr_timeout;
|
|
+
|
|
+ /**
|
|
+ * If this option is given the file-system handlers for the
|
|
+ * following operations will not receive path information:
|
|
+ * read, write, flush, release, fsync, readdir, releasedir,
|
|
+ * fsyncdir, lock, ioctl and poll.
|
|
+ *
|
|
+ * For the truncate, getattr, chmod, chown and utimens
|
|
+ * operations the path will be provided only if the struct
|
|
+ * fuse_file_info argument is NULL.
|
|
+ */
|
|
+ int nullpath_ok;
|
|
+
|
|
+ /**
|
|
+ * The remaining options are used by libfuse internally and
|
|
+ * should not be touched.
|
|
+ */
|
|
+ int show_help;
|
|
+ char *modules;
|
|
+ int debug;
|
|
};
|
|
|
|
|
|
@@ -293,515 +294,535 @@ struct fuse_config {
|
|
* Almost all operations take a path which can be of any length.
|
|
*/
|
|
struct fuse_operations {
|
|
- /** Get file attributes.
|
|
- *
|
|
- * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
|
|
- * ignored. The 'st_ino' field is ignored except if the 'use_ino'
|
|
- * mount option is given. In that case it is passed to userspace,
|
|
- * but libfuse and the kernel will still assign a different
|
|
- * inode for internal use (called the "nodeid").
|
|
- *
|
|
- * `fi` will always be NULL if the file is not currently open, but
|
|
- * may also be NULL if the file is open.
|
|
- */
|
|
- int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);
|
|
-
|
|
- /** Read the target of a symbolic link
|
|
- *
|
|
- * The buffer should be filled with a null terminated string. The
|
|
- * buffer size argument includes the space for the terminating
|
|
- * null character. If the linkname is too long to fit in the
|
|
- * buffer, it should be truncated. The return value should be 0
|
|
- * for success.
|
|
- */
|
|
- int (*readlink) (const char *, char *, size_t);
|
|
-
|
|
- /** Create a file node
|
|
- *
|
|
- * This is called for creation of all non-directory, non-symlink
|
|
- * nodes. If the filesystem defines a create() method, then for
|
|
- * regular files that will be called instead.
|
|
- */
|
|
- int (*mknod) (const char *, mode_t, dev_t);
|
|
-
|
|
- /** Create a directory
|
|
- *
|
|
- * Note that the mode argument may not have the type specification
|
|
- * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
|
|
- * correct directory type bits use mode|S_IFDIR
|
|
- * */
|
|
- int (*mkdir) (const char *, mode_t);
|
|
-
|
|
- /** Remove a file */
|
|
- int (*unlink) (const char *);
|
|
-
|
|
- /** Remove a directory */
|
|
- int (*rmdir) (const char *);
|
|
-
|
|
- /** Create a symbolic link */
|
|
- int (*symlink) (const char *, const char *);
|
|
-
|
|
- /** Rename a file
|
|
- *
|
|
- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
|
|
- * RENAME_NOREPLACE is specified, the filesystem must not
|
|
- * overwrite *newname* if it exists and return an error
|
|
- * instead. If `RENAME_EXCHANGE` is specified, the filesystem
|
|
- * must atomically exchange the two files, i.e. both must
|
|
- * exist and neither may be deleted.
|
|
- */
|
|
- int (*rename) (const char *, const char *, unsigned int flags);
|
|
-
|
|
- /** Create a hard link to a file */
|
|
- int (*link) (const char *, const char *);
|
|
-
|
|
- /** Change the permission bits of a file
|
|
- *
|
|
- * `fi` will always be NULL if the file is not currenlty open, but
|
|
- * may also be NULL if the file is open.
|
|
- */
|
|
- int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);
|
|
-
|
|
- /** Change the owner and group of a file
|
|
- *
|
|
- * `fi` will always be NULL if the file is not currenlty open, but
|
|
- * may also be NULL if the file is open.
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- */
|
|
- int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);
|
|
-
|
|
- /** Change the size of a file
|
|
- *
|
|
- * `fi` will always be NULL if the file is not currenlty open, but
|
|
- * may also be NULL if the file is open.
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- */
|
|
- int (*truncate) (const char *, off_t, struct fuse_file_info *fi);
|
|
-
|
|
- /** Open a file
|
|
- *
|
|
- * Open flags are available in fi->flags. The following rules
|
|
- * apply.
|
|
- *
|
|
- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be
|
|
- * filtered out / handled by the kernel.
|
|
- *
|
|
- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH)
|
|
- * should be used by the filesystem to check if the operation is
|
|
- * permitted. If the ``-o default_permissions`` mount option is
|
|
- * given, this check is already done by the kernel before calling
|
|
- * open() and may thus be omitted by the filesystem.
|
|
- *
|
|
- * - When writeback caching is enabled, the kernel may send
|
|
- * read requests even for files opened with O_WRONLY. The
|
|
- * filesystem should be prepared to handle this.
|
|
- *
|
|
- * - When writeback caching is disabled, the filesystem is
|
|
- * expected to properly handle the O_APPEND flag and ensure
|
|
- * that each write is appending to the end of the file.
|
|
- *
|
|
- * - When writeback caching is enabled, the kernel will
|
|
- * handle O_APPEND. However, unless all changes to the file
|
|
- * come through the kernel this will not work reliably. The
|
|
- * filesystem should thus either ignore the O_APPEND flag
|
|
- * (and let the kernel handle it), or return an error
|
|
- * (indicating that reliably O_APPEND is not available).
|
|
- *
|
|
- * Filesystem may store an arbitrary file handle (pointer,
|
|
- * index, etc) in fi->fh, and use this in other all other file
|
|
- * operations (read, write, flush, release, fsync).
|
|
- *
|
|
- * Filesystem may also implement stateless file I/O and not store
|
|
- * anything in fi->fh.
|
|
- *
|
|
- * There are also some flags (direct_io, keep_cache) which the
|
|
- * filesystem may set in fi, to change the way the file is opened.
|
|
- * See fuse_file_info structure in <fuse_common.h> for more details.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS
|
|
- * and FUSE_CAP_NO_OPEN_SUPPORT is set in
|
|
- * `fuse_conn_info.capable`, this is treated as success and
|
|
- * future calls to open will also succeed without being send
|
|
- * to the filesystem process.
|
|
- *
|
|
- */
|
|
- int (*open) (const char *, struct fuse_file_info *);
|
|
-
|
|
- /** Read data from an open file
|
|
- *
|
|
- * Read should return exactly the number of bytes requested except
|
|
- * on EOF or error, otherwise the rest of the data will be
|
|
- * substituted with zeroes. An exception to this is when the
|
|
- * 'direct_io' mount option is specified, in which case the return
|
|
- * value of the read system call will reflect the return value of
|
|
- * this operation.
|
|
- */
|
|
- int (*read) (const char *, char *, size_t, off_t,
|
|
- struct fuse_file_info *);
|
|
-
|
|
- /** Write data to an open file
|
|
- *
|
|
- * Write should return exactly the number of bytes requested
|
|
- * except on error. An exception to this is when the 'direct_io'
|
|
- * mount option is specified (see read operation).
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- */
|
|
- int (*write) (const char *, const char *, size_t, off_t,
|
|
- struct fuse_file_info *);
|
|
-
|
|
- /** Get file system statistics
|
|
- *
|
|
- * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
|
|
- */
|
|
- int (*statfs) (const char *, struct statvfs *);
|
|
-
|
|
- /** Possibly flush cached data
|
|
- *
|
|
- * BIG NOTE: This is not equivalent to fsync(). It's not a
|
|
- * request to sync dirty data.
|
|
- *
|
|
- * Flush is called on each close() of a file descriptor, as opposed to
|
|
- * release which is called on the close of the last file descriptor for
|
|
- * a file. Under Linux, errors returned by flush() will be passed to
|
|
- * userspace as errors from close(), so flush() is a good place to write
|
|
- * back any cached dirty data. However, many applications ignore errors
|
|
- * on close(), and on non-Linux systems, close() may succeed even if flush()
|
|
- * returns an error. For these reasons, filesystems should not assume
|
|
- * that errors returned by flush will ever be noticed or even
|
|
- * delivered.
|
|
- *
|
|
- * NOTE: The flush() method may be called more than once for each
|
|
- * open(). This happens if more than one file descriptor refers to an
|
|
- * open file handle, e.g. due to dup(), dup2() or fork() calls. It is
|
|
- * not possible to determine if a flush is final, so each flush should
|
|
- * be treated equally. Multiple write-flush sequences are relatively
|
|
- * rare, so this shouldn't be a problem.
|
|
- *
|
|
- * Filesystems shouldn't assume that flush will be called at any
|
|
- * particular point. It may be called more times than expected, or not
|
|
- * at all.
|
|
- *
|
|
- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
|
|
- */
|
|
- int (*flush) (const char *, struct fuse_file_info *);
|
|
-
|
|
- /** Release an open file
|
|
- *
|
|
- * Release is called when there are no more references to an open
|
|
- * file: all file descriptors are closed and all memory mappings
|
|
- * are unmapped.
|
|
- *
|
|
- * For every open() call there will be exactly one release() call
|
|
- * with the same flags and file handle. It is possible to
|
|
- * have a file opened more than once, in which case only the last
|
|
- * release will mean, that no more reads/writes will happen on the
|
|
- * file. The return value of release is ignored.
|
|
- */
|
|
- int (*release) (const char *, struct fuse_file_info *);
|
|
-
|
|
- /** Synchronize file contents
|
|
- *
|
|
- * If the datasync parameter is non-zero, then only the user data
|
|
- * should be flushed, not the meta data.
|
|
- */
|
|
- int (*fsync) (const char *, int, struct fuse_file_info *);
|
|
-
|
|
- /** Set extended attributes */
|
|
- int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
|
-
|
|
- /** Get extended attributes */
|
|
- int (*getxattr) (const char *, const char *, char *, size_t);
|
|
-
|
|
- /** List extended attributes */
|
|
- int (*listxattr) (const char *, char *, size_t);
|
|
-
|
|
- /** Remove extended attributes */
|
|
- int (*removexattr) (const char *, const char *);
|
|
-
|
|
- /** Open directory
|
|
- *
|
|
- * Unless the 'default_permissions' mount option is given,
|
|
- * this method should check if opendir is permitted for this
|
|
- * directory. Optionally opendir may also return an arbitrary
|
|
- * filehandle in the fuse_file_info structure, which will be
|
|
- * passed to readdir, releasedir and fsyncdir.
|
|
- */
|
|
- int (*opendir) (const char *, struct fuse_file_info *);
|
|
-
|
|
- /** Read directory
|
|
- *
|
|
- * The filesystem may choose between two modes of operation:
|
|
- *
|
|
- * 1) The readdir implementation ignores the offset parameter, and
|
|
- * passes zero to the filler function's offset. The filler
|
|
- * function will not return '1' (unless an error happens), so the
|
|
- * whole directory is read in a single readdir operation.
|
|
- *
|
|
- * 2) The readdir implementation keeps track of the offsets of the
|
|
- * directory entries. It uses the offset parameter and always
|
|
- * passes non-zero offset to the filler function. When the buffer
|
|
- * is full (or an error happens) the filler function will return
|
|
- * '1'.
|
|
- */
|
|
- int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
|
|
- struct fuse_file_info *, enum fuse_readdir_flags);
|
|
-
|
|
- /** Release directory
|
|
- */
|
|
- int (*releasedir) (const char *, struct fuse_file_info *);
|
|
-
|
|
- /** Synchronize directory contents
|
|
- *
|
|
- * If the datasync parameter is non-zero, then only the user data
|
|
- * should be flushed, not the meta data
|
|
- */
|
|
- int (*fsyncdir) (const char *, int, struct fuse_file_info *);
|
|
-
|
|
- /**
|
|
- * Initialize filesystem
|
|
- *
|
|
- * The return value will passed in the `private_data` field of
|
|
- * `struct fuse_context` to all file operations, and as a
|
|
- * parameter to the destroy() method. It overrides the initial
|
|
- * value provided to fuse_main() / fuse_new().
|
|
- */
|
|
- void *(*init) (struct fuse_conn_info *conn,
|
|
- struct fuse_config *cfg);
|
|
-
|
|
- /**
|
|
- * Clean up filesystem
|
|
- *
|
|
- * Called on filesystem exit.
|
|
- */
|
|
- void (*destroy) (void *private_data);
|
|
-
|
|
- /**
|
|
- * Check file access permissions
|
|
- *
|
|
- * This will be called for the access() system call. If the
|
|
- * 'default_permissions' mount option is given, this method is not
|
|
- * called.
|
|
- *
|
|
- * This method is not called under Linux kernel versions 2.4.x
|
|
- */
|
|
- int (*access) (const char *, int);
|
|
-
|
|
- /**
|
|
- * Create and open a file
|
|
- *
|
|
- * If the file does not exist, first create it with the specified
|
|
- * mode, and then open it.
|
|
- *
|
|
- * If this method is not implemented or under Linux kernel
|
|
- * versions earlier than 2.6.15, the mknod() and open() methods
|
|
- * will be called instead.
|
|
- */
|
|
- int (*create) (const char *, mode_t, struct fuse_file_info *);
|
|
-
|
|
- /**
|
|
- * Perform POSIX file locking operation
|
|
- *
|
|
- * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
|
|
- *
|
|
- * For the meaning of fields in 'struct flock' see the man page
|
|
- * for fcntl(2). The l_whence field will always be set to
|
|
- * SEEK_SET.
|
|
- *
|
|
- * For checking lock ownership, the 'fuse_file_info->owner'
|
|
- * argument must be used.
|
|
- *
|
|
- * For F_GETLK operation, the library will first check currently
|
|
- * held locks, and if a conflicting lock is found it will return
|
|
- * information without calling this method. This ensures, that
|
|
- * for local locks the l_pid field is correctly filled in. The
|
|
- * results may not be accurate in case of race conditions and in
|
|
- * the presence of hard links, but it's unlikely that an
|
|
- * application would rely on accurate GETLK results in these
|
|
- * cases. If a conflicting lock is not found, this method will be
|
|
- * called, and the filesystem may fill out l_pid by a meaningful
|
|
- * value, or it may leave this field zero.
|
|
- *
|
|
- * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
|
|
- * of the process performing the locking operation.
|
|
- *
|
|
- * Note: if this method is not implemented, the kernel will still
|
|
- * allow file locking to work locally. Hence it is only
|
|
- * interesting for network filesystems and similar.
|
|
- */
|
|
- int (*lock) (const char *, struct fuse_file_info *, int cmd,
|
|
- struct flock *);
|
|
-
|
|
- /**
|
|
- * Change the access and modification times of a file with
|
|
- * nanosecond resolution
|
|
- *
|
|
- * This supersedes the old utime() interface. New applications
|
|
- * should use this.
|
|
- *
|
|
- * `fi` will always be NULL if the file is not currenlty open, but
|
|
- * may also be NULL if the file is open.
|
|
- *
|
|
- * See the utimensat(2) man page for details.
|
|
- */
|
|
- int (*utimens) (const char *, const struct timespec tv[2],
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Map block index within file to block index within device
|
|
- *
|
|
- * Note: This makes sense only for block device backed filesystems
|
|
- * mounted with the 'blkdev' option
|
|
- */
|
|
- int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
|
|
-
|
|
- /**
|
|
- * Ioctl
|
|
- *
|
|
- * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
|
|
- * 64bit environment. The size and direction of data is
|
|
- * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
|
|
- * data will be NULL, for _IOC_WRITE data is out area, for
|
|
- * _IOC_READ in area and if both are set in/out area. In all
|
|
- * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
|
|
- *
|
|
- * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a
|
|
- * directory file handle.
|
|
- *
|
|
- * Note : the unsigned long request submitted by the application
|
|
- * is truncated to 32 bits.
|
|
- */
|
|
- int (*ioctl) (const char *, unsigned int cmd, void *arg,
|
|
- struct fuse_file_info *, unsigned int flags, void *data);
|
|
-
|
|
- /**
|
|
- * Poll for IO readiness events
|
|
- *
|
|
- * Note: If ph is non-NULL, the client should notify
|
|
- * when IO readiness events occur by calling
|
|
- * fuse_notify_poll() with the specified ph.
|
|
- *
|
|
- * Regardless of the number of times poll with a non-NULL ph
|
|
- * is received, single notification is enough to clear all.
|
|
- * Notifying more times incurs overhead but doesn't harm
|
|
- * correctness.
|
|
- *
|
|
- * The callee is responsible for destroying ph with
|
|
- * fuse_pollhandle_destroy() when no longer in use.
|
|
- */
|
|
- int (*poll) (const char *, struct fuse_file_info *,
|
|
- struct fuse_pollhandle *ph, unsigned *reventsp);
|
|
-
|
|
- /** Write contents of buffer to an open file
|
|
- *
|
|
- * Similar to the write() method, but data is supplied in a
|
|
- * generic buffer. Use fuse_buf_copy() to transfer data to
|
|
- * the destination.
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- */
|
|
- int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
|
|
- struct fuse_file_info *);
|
|
-
|
|
- /** Store data from an open file in a buffer
|
|
- *
|
|
- * Similar to the read() method, but data is stored and
|
|
- * returned in a generic buffer.
|
|
- *
|
|
- * No actual copying of data has to take place, the source
|
|
- * file descriptor may simply be stored in the buffer for
|
|
- * later data transfer.
|
|
- *
|
|
- * The buffer must be allocated dynamically and stored at the
|
|
- * location pointed to by bufp. If the buffer contains memory
|
|
- * regions, they too must be allocated using malloc(). The
|
|
- * allocated memory will be freed by the caller.
|
|
- */
|
|
- int (*read_buf) (const char *, struct fuse_bufvec **bufp,
|
|
- size_t size, off_t off, struct fuse_file_info *);
|
|
- /**
|
|
- * Perform BSD file locking operation
|
|
- *
|
|
- * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
|
|
- *
|
|
- * Nonblocking requests will be indicated by ORing LOCK_NB to
|
|
- * the above operations
|
|
- *
|
|
- * For more information see the flock(2) manual page.
|
|
- *
|
|
- * Additionally fi->owner will be set to a value unique to
|
|
- * this open file. This same value will be supplied to
|
|
- * ->release() when the file is released.
|
|
- *
|
|
- * Note: if this method is not implemented, the kernel will still
|
|
- * allow file locking to work locally. Hence it is only
|
|
- * interesting for network filesystems and similar.
|
|
- */
|
|
- int (*flock) (const char *, struct fuse_file_info *, int op);
|
|
-
|
|
- /**
|
|
- * Allocates space for an open file
|
|
- *
|
|
- * This function ensures that required space is allocated for specified
|
|
- * file. If this function returns success then any subsequent write
|
|
- * request to specified range is guaranteed not to fail because of lack
|
|
- * of space on the file system media.
|
|
- */
|
|
- int (*fallocate) (const char *, int, off_t, off_t,
|
|
- struct fuse_file_info *);
|
|
-
|
|
- /**
|
|
- * Copy a range of data from one file to another
|
|
- *
|
|
- * Performs an optimized copy between two file descriptors without the
|
|
- * additional cost of transferring data through the FUSE kernel module
|
|
- * to user space (glibc) and then back into the FUSE filesystem again.
|
|
- *
|
|
- * In case this method is not implemented, glibc falls back to reading
|
|
- * data from the source and writing to the destination. Effectively
|
|
- * doing an inefficient copy of the data.
|
|
- */
|
|
- ssize_t (*copy_file_range) (const char *path_in,
|
|
- struct fuse_file_info *fi_in,
|
|
- off_t offset_in, const char *path_out,
|
|
- struct fuse_file_info *fi_out,
|
|
- off_t offset_out, size_t size, int flags);
|
|
-
|
|
- /**
|
|
- * Find next data or hole after the specified offset
|
|
- */
|
|
- off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
|
|
+ /**
|
|
+ * Get file attributes.
|
|
+ *
|
|
+ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
|
|
+ * ignored. The 'st_ino' field is ignored except if the 'use_ino'
|
|
+ * mount option is given. In that case it is passed to userspace,
|
|
+ * but libfuse and the kernel will still assign a different
|
|
+ * inode for internal use (called the "nodeid").
|
|
+ *
|
|
+ * `fi` will always be NULL if the file is not currently open, but
|
|
+ * may also be NULL if the file is open.
|
|
+ */
|
|
+ int (*getattr)(const char *, struct stat *, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Read the target of a symbolic link
|
|
+ *
|
|
+ * The buffer should be filled with a null terminated string. The
|
|
+ * buffer size argument includes the space for the terminating
|
|
+ * null character. If the linkname is too long to fit in the
|
|
+ * buffer, it should be truncated. The return value should be 0
|
|
+ * for success.
|
|
+ */
|
|
+ int (*readlink)(const char *, char *, size_t);
|
|
+
|
|
+ /**
|
|
+ * Create a file node
|
|
+ *
|
|
+ * This is called for creation of all non-directory, non-symlink
|
|
+ * nodes. If the filesystem defines a create() method, then for
|
|
+ * regular files that will be called instead.
|
|
+ */
|
|
+ int (*mknod)(const char *, mode_t, dev_t);
|
|
+
|
|
+ /**
|
|
+ * Create a directory
|
|
+ *
|
|
+ * Note that the mode argument may not have the type specification
|
|
+ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
|
|
+ * correct directory type bits use mode|S_IFDIR
|
|
+ */
|
|
+ int (*mkdir)(const char *, mode_t);
|
|
+
|
|
+ /** Remove a file */
|
|
+ int (*unlink)(const char *);
|
|
+
|
|
+ /** Remove a directory */
|
|
+ int (*rmdir)(const char *);
|
|
+
|
|
+ /** Create a symbolic link */
|
|
+ int (*symlink)(const char *, const char *);
|
|
+
|
|
+ /**
|
|
+ * Rename a file
|
|
+ *
|
|
+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
|
|
+ * RENAME_NOREPLACE is specified, the filesystem must not
|
|
+ * overwrite *newname* if it exists and return an error
|
|
+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem
|
|
+ * must atomically exchange the two files, i.e. both must
|
|
+ * exist and neither may be deleted.
|
|
+ */
|
|
+ int (*rename)(const char *, const char *, unsigned int flags);
|
|
+
|
|
+ /** Create a hard link to a file */
|
|
+ int (*link)(const char *, const char *);
|
|
+
|
|
+ /**
|
|
+ * Change the permission bits of a file
|
|
+ *
|
|
+ * `fi` will always be NULL if the file is not currenlty open, but
|
|
+ * may also be NULL if the file is open.
|
|
+ */
|
|
+ int (*chmod)(const char *, mode_t, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Change the owner and group of a file
|
|
+ *
|
|
+ * `fi` will always be NULL if the file is not currenlty open, but
|
|
+ * may also be NULL if the file is open.
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ */
|
|
+ int (*chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Change the size of a file
|
|
+ *
|
|
+ * `fi` will always be NULL if the file is not currenlty open, but
|
|
+ * may also be NULL if the file is open.
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ */
|
|
+ int (*truncate)(const char *, off_t, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Open a file
|
|
+ *
|
|
+ * Open flags are available in fi->flags. The following rules
|
|
+ * apply.
|
|
+ *
|
|
+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be
|
|
+ * filtered out / handled by the kernel.
|
|
+ *
|
|
+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH)
|
|
+ * should be used by the filesystem to check if the operation is
|
|
+ * permitted. If the ``-o default_permissions`` mount option is
|
|
+ * given, this check is already done by the kernel before calling
|
|
+ * open() and may thus be omitted by the filesystem.
|
|
+ *
|
|
+ * - When writeback caching is enabled, the kernel may send
|
|
+ * read requests even for files opened with O_WRONLY. The
|
|
+ * filesystem should be prepared to handle this.
|
|
+ *
|
|
+ * - When writeback caching is disabled, the filesystem is
|
|
+ * expected to properly handle the O_APPEND flag and ensure
|
|
+ * that each write is appending to the end of the file.
|
|
+ *
|
|
+ * - When writeback caching is enabled, the kernel will
|
|
+ * handle O_APPEND. However, unless all changes to the file
|
|
+ * come through the kernel this will not work reliably. The
|
|
+ * filesystem should thus either ignore the O_APPEND flag
|
|
+ * (and let the kernel handle it), or return an error
|
|
+ * (indicating that reliably O_APPEND is not available).
|
|
+ *
|
|
+ * Filesystem may store an arbitrary file handle (pointer,
|
|
+ * index, etc) in fi->fh, and use this in other all other file
|
|
+ * operations (read, write, flush, release, fsync).
|
|
+ *
|
|
+ * Filesystem may also implement stateless file I/O and not store
|
|
+ * anything in fi->fh.
|
|
+ *
|
|
+ * There are also some flags (direct_io, keep_cache) which the
|
|
+ * filesystem may set in fi, to change the way the file is opened.
|
|
+ * See fuse_file_info structure in <fuse_common.h> for more details.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS
|
|
+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in
|
|
+ * `fuse_conn_info.capable`, this is treated as success and
|
|
+ * future calls to open will also succeed without being send
|
|
+ * to the filesystem process.
|
|
+ *
|
|
+ */
|
|
+ int (*open)(const char *, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Read data from an open file
|
|
+ *
|
|
+ * Read should return exactly the number of bytes requested except
|
|
+ * on EOF or error, otherwise the rest of the data will be
|
|
+ * substituted with zeroes. An exception to this is when the
|
|
+ * 'direct_io' mount option is specified, in which case the return
|
|
+ * value of the read system call will reflect the return value of
|
|
+ * this operation.
|
|
+ */
|
|
+ int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Write data to an open file
|
|
+ *
|
|
+ * Write should return exactly the number of bytes requested
|
|
+ * except on error. An exception to this is when the 'direct_io'
|
|
+ * mount option is specified (see read operation).
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ */
|
|
+ int (*write)(const char *, const char *, size_t, off_t,
|
|
+ struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Get file system statistics
|
|
+ *
|
|
+ * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
|
|
+ */
|
|
+ int (*statfs)(const char *, struct statvfs *);
|
|
+
|
|
+ /**
|
|
+ * Possibly flush cached data
|
|
+ *
|
|
+ * BIG NOTE: This is not equivalent to fsync(). It's not a
|
|
+ * request to sync dirty data.
|
|
+ *
|
|
+ * Flush is called on each close() of a file descriptor, as opposed to
|
|
+ * release which is called on the close of the last file descriptor for
|
|
+ * a file. Under Linux, errors returned by flush() will be passed to
|
|
+ * userspace as errors from close(), so flush() is a good place to write
|
|
+ * back any cached dirty data. However, many applications ignore errors
|
|
+ * on close(), and on non-Linux systems, close() may succeed even if flush()
|
|
+ * returns an error. For these reasons, filesystems should not assume
|
|
+ * that errors returned by flush will ever be noticed or even
|
|
+ * delivered.
|
|
+ *
|
|
+ * NOTE: The flush() method may be called more than once for each
|
|
+ * open(). This happens if more than one file descriptor refers to an
|
|
+ * open file handle, e.g. due to dup(), dup2() or fork() calls. It is
|
|
+ * not possible to determine if a flush is final, so each flush should
|
|
+ * be treated equally. Multiple write-flush sequences are relatively
|
|
+ * rare, so this shouldn't be a problem.
|
|
+ *
|
|
+ * Filesystems shouldn't assume that flush will be called at any
|
|
+ * particular point. It may be called more times than expected, or not
|
|
+ * at all.
|
|
+ *
|
|
+ * [close]:
|
|
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
|
|
+ */
|
|
+ int (*flush)(const char *, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Release an open file
|
|
+ *
|
|
+ * Release is called when there are no more references to an open
|
|
+ * file: all file descriptors are closed and all memory mappings
|
|
+ * are unmapped.
|
|
+ *
|
|
+ * For every open() call there will be exactly one release() call
|
|
+ * with the same flags and file handle. It is possible to
|
|
+ * have a file opened more than once, in which case only the last
|
|
+ * release will mean, that no more reads/writes will happen on the
|
|
+ * file. The return value of release is ignored.
|
|
+ */
|
|
+ int (*release)(const char *, struct fuse_file_info *);
|
|
+
|
|
+ /*
|
|
+ * Synchronize file contents
|
|
+ *
|
|
+ * If the datasync parameter is non-zero, then only the user data
|
|
+ * should be flushed, not the meta data.
|
|
+ */
|
|
+ int (*fsync)(const char *, int, struct fuse_file_info *);
|
|
+
|
|
+ /** Set extended attributes */
|
|
+ int (*setxattr)(const char *, const char *, const char *, size_t, int);
|
|
+
|
|
+ /** Get extended attributes */
|
|
+ int (*getxattr)(const char *, const char *, char *, size_t);
|
|
+
|
|
+ /** List extended attributes */
|
|
+ int (*listxattr)(const char *, char *, size_t);
|
|
+
|
|
+ /** Remove extended attributes */
|
|
+ int (*removexattr)(const char *, const char *);
|
|
+
|
|
+ /*
|
|
+ * Open directory
|
|
+ *
|
|
+ * Unless the 'default_permissions' mount option is given,
|
|
+ * this method should check if opendir is permitted for this
|
|
+ * directory. Optionally opendir may also return an arbitrary
|
|
+ * filehandle in the fuse_file_info structure, which will be
|
|
+ * passed to readdir, releasedir and fsyncdir.
|
|
+ */
|
|
+ int (*opendir)(const char *, struct fuse_file_info *);
|
|
+
|
|
+ /*
|
|
+ * Read directory
|
|
+ *
|
|
+ * The filesystem may choose between two modes of operation:
|
|
+ *
|
|
+ * 1) The readdir implementation ignores the offset parameter, and
|
|
+ * passes zero to the filler function's offset. The filler
|
|
+ * function will not return '1' (unless an error happens), so the
|
|
+ * whole directory is read in a single readdir operation.
|
|
+ *
|
|
+ * 2) The readdir implementation keeps track of the offsets of the
|
|
+ * directory entries. It uses the offset parameter and always
|
|
+ * passes non-zero offset to the filler function. When the buffer
|
|
+ * is full (or an error happens) the filler function will return
|
|
+ * '1'.
|
|
+ */
|
|
+ int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t,
|
|
+ struct fuse_file_info *, enum fuse_readdir_flags);
|
|
+
|
|
+ /**
|
|
+ * Release directory
|
|
+ */
|
|
+ int (*releasedir)(const char *, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Synchronize directory contents
|
|
+ *
|
|
+ * If the datasync parameter is non-zero, then only the user data
|
|
+ * should be flushed, not the meta data
|
|
+ */
|
|
+ int (*fsyncdir)(const char *, int, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Initialize filesystem
|
|
+ *
|
|
+ * The return value will passed in the `private_data` field of
|
|
+ * `struct fuse_context` to all file operations, and as a
|
|
+ * parameter to the destroy() method. It overrides the initial
|
|
+ * value provided to fuse_main() / fuse_new().
|
|
+ */
|
|
+ void *(*init)(struct fuse_conn_info *conn, struct fuse_config *cfg);
|
|
+
|
|
+ /**
|
|
+ * Clean up filesystem
|
|
+ *
|
|
+ * Called on filesystem exit.
|
|
+ */
|
|
+ void (*destroy)(void *private_data);
|
|
+
|
|
+ /**
|
|
+ * Check file access permissions
|
|
+ *
|
|
+ * This will be called for the access() system call. If the
|
|
+ * 'default_permissions' mount option is given, this method is not
|
|
+ * called.
|
|
+ *
|
|
+ * This method is not called under Linux kernel versions 2.4.x
|
|
+ */
|
|
+ int (*access)(const char *, int);
|
|
+
|
|
+ /**
|
|
+ * Create and open a file
|
|
+ *
|
|
+ * If the file does not exist, first create it with the specified
|
|
+ * mode, and then open it.
|
|
+ *
|
|
+ * If this method is not implemented or under Linux kernel
|
|
+ * versions earlier than 2.6.15, the mknod() and open() methods
|
|
+ * will be called instead.
|
|
+ */
|
|
+ int (*create)(const char *, mode_t, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Perform POSIX file locking operation
|
|
+ *
|
|
+ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
|
|
+ *
|
|
+ * For the meaning of fields in 'struct flock' see the man page
|
|
+ * for fcntl(2). The l_whence field will always be set to
|
|
+ * SEEK_SET.
|
|
+ *
|
|
+ * For checking lock ownership, the 'fuse_file_info->owner'
|
|
+ * argument must be used.
|
|
+ *
|
|
+ * For F_GETLK operation, the library will first check currently
|
|
+ * held locks, and if a conflicting lock is found it will return
|
|
+ * information without calling this method. This ensures, that
|
|
+ * for local locks the l_pid field is correctly filled in. The
|
|
+ * results may not be accurate in case of race conditions and in
|
|
+ * the presence of hard links, but it's unlikely that an
|
|
+ * application would rely on accurate GETLK results in these
|
|
+ * cases. If a conflicting lock is not found, this method will be
|
|
+ * called, and the filesystem may fill out l_pid by a meaningful
|
|
+ * value, or it may leave this field zero.
|
|
+ *
|
|
+ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
|
|
+ * of the process performing the locking operation.
|
|
+ *
|
|
+ * Note: if this method is not implemented, the kernel will still
|
|
+ * allow file locking to work locally. Hence it is only
|
|
+ * interesting for network filesystems and similar.
|
|
+ */
|
|
+ int (*lock)(const char *, struct fuse_file_info *, int cmd, struct flock *);
|
|
+
|
|
+ /**
|
|
+ * Change the access and modification times of a file with
|
|
+ * nanosecond resolution
|
|
+ *
|
|
+ * This supersedes the old utime() interface. New applications
|
|
+ * should use this.
|
|
+ *
|
|
+ * `fi` will always be NULL if the file is not currenlty open, but
|
|
+ * may also be NULL if the file is open.
|
|
+ *
|
|
+ * See the utimensat(2) man page for details.
|
|
+ */
|
|
+ int (*utimens)(const char *, const struct timespec tv[2],
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Map block index within file to block index within device
|
|
+ *
|
|
+ * Note: This makes sense only for block device backed filesystems
|
|
+ * mounted with the 'blkdev' option
|
|
+ */
|
|
+ int (*bmap)(const char *, size_t blocksize, uint64_t *idx);
|
|
+
|
|
+ /**
|
|
+ * Ioctl
|
|
+ *
|
|
+ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
|
|
+ * 64bit environment. The size and direction of data is
|
|
+ * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
|
|
+ * data will be NULL, for _IOC_WRITE data is out area, for
|
|
+ * _IOC_READ in area and if both are set in/out area. In all
|
|
+ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
|
|
+ *
|
|
+ * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a
|
|
+ * directory file handle.
|
|
+ *
|
|
+ * Note : the unsigned long request submitted by the application
|
|
+ * is truncated to 32 bits.
|
|
+ */
|
|
+ int (*ioctl)(const char *, unsigned int cmd, void *arg,
|
|
+ struct fuse_file_info *, unsigned int flags, void *data);
|
|
+
|
|
+ /**
|
|
+ * Poll for IO readiness events
|
|
+ *
|
|
+ * Note: If ph is non-NULL, the client should notify
|
|
+ * when IO readiness events occur by calling
|
|
+ * fuse_notify_poll() with the specified ph.
|
|
+ *
|
|
+ * Regardless of the number of times poll with a non-NULL ph
|
|
+ * is received, single notification is enough to clear all.
|
|
+ * Notifying more times incurs overhead but doesn't harm
|
|
+ * correctness.
|
|
+ *
|
|
+ * The callee is responsible for destroying ph with
|
|
+ * fuse_pollhandle_destroy() when no longer in use.
|
|
+ */
|
|
+ int (*poll)(const char *, struct fuse_file_info *,
|
|
+ struct fuse_pollhandle *ph, unsigned *reventsp);
|
|
+
|
|
+ /*
|
|
+ * Write contents of buffer to an open file
|
|
+ *
|
|
+ * Similar to the write() method, but data is supplied in a
|
|
+ * generic buffer. Use fuse_buf_copy() to transfer data to
|
|
+ * the destination.
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ */
|
|
+ int (*write_buf)(const char *, struct fuse_bufvec *buf, off_t off,
|
|
+ struct fuse_file_info *);
|
|
+
|
|
+ /*
|
|
+ * Store data from an open file in a buffer
|
|
+ *
|
|
+ * Similar to the read() method, but data is stored and
|
|
+ * returned in a generic buffer.
|
|
+ *
|
|
+ * No actual copying of data has to take place, the source
|
|
+ * file descriptor may simply be stored in the buffer for
|
|
+ * later data transfer.
|
|
+ *
|
|
+ * The buffer must be allocated dynamically and stored at the
|
|
+ * location pointed to by bufp. If the buffer contains memory
|
|
+ * regions, they too must be allocated using malloc(). The
|
|
+ * allocated memory will be freed by the caller.
|
|
+ */
|
|
+ int (*read_buf)(const char *, struct fuse_bufvec **bufp, size_t size,
|
|
+ off_t off, struct fuse_file_info *);
|
|
+ /**
|
|
+ * Perform BSD file locking operation
|
|
+ *
|
|
+ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
|
|
+ *
|
|
+ * Nonblocking requests will be indicated by ORing LOCK_NB to
|
|
+ * the above operations
|
|
+ *
|
|
+ * For more information see the flock(2) manual page.
|
|
+ *
|
|
+ * Additionally fi->owner will be set to a value unique to
|
|
+ * this open file. This same value will be supplied to
|
|
+ * ->release() when the file is released.
|
|
+ *
|
|
+ * Note: if this method is not implemented, the kernel will still
|
|
+ * allow file locking to work locally. Hence it is only
|
|
+ * interesting for network filesystems and similar.
|
|
+ */
|
|
+ int (*flock)(const char *, struct fuse_file_info *, int op);
|
|
+
|
|
+ /**
|
|
+ * Allocates space for an open file
|
|
+ *
|
|
+ * This function ensures that required space is allocated for specified
|
|
+ * file. If this function returns success then any subsequent write
|
|
+ * request to specified range is guaranteed not to fail because of lack
|
|
+ * of space on the file system media.
|
|
+ */
|
|
+ int (*fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *);
|
|
+
|
|
+ /**
|
|
+ * Copy a range of data from one file to another
|
|
+ *
|
|
+ * Performs an optimized copy between two file descriptors without the
|
|
+ * additional cost of transferring data through the FUSE kernel module
|
|
+ * to user space (glibc) and then back into the FUSE filesystem again.
|
|
+ *
|
|
+ * In case this method is not implemented, glibc falls back to reading
|
|
+ * data from the source and writing to the destination. Effectively
|
|
+ * doing an inefficient copy of the data.
|
|
+ */
|
|
+ ssize_t (*copy_file_range)(const char *path_in,
|
|
+ struct fuse_file_info *fi_in, off_t offset_in,
|
|
+ const char *path_out,
|
|
+ struct fuse_file_info *fi_out, off_t offset_out,
|
|
+ size_t size, int flags);
|
|
+
|
|
+ /**
|
|
+ * Find next data or hole after the specified offset
|
|
+ */
|
|
+ off_t (*lseek)(const char *, off_t off, int whence,
|
|
+ struct fuse_file_info *);
|
|
};
|
|
|
|
-/** Extra context that may be needed by some filesystems
|
|
+/*
|
|
+ * Extra context that may be needed by some filesystems
|
|
*
|
|
* The uid, gid and pid fields are not filled in case of a writepage
|
|
* operation.
|
|
*/
|
|
struct fuse_context {
|
|
- /** Pointer to the fuse object */
|
|
- struct fuse *fuse;
|
|
+ /** Pointer to the fuse object */
|
|
+ struct fuse *fuse;
|
|
|
|
- /** User ID of the calling process */
|
|
- uid_t uid;
|
|
+ /** User ID of the calling process */
|
|
+ uid_t uid;
|
|
|
|
- /** Group ID of the calling process */
|
|
- gid_t gid;
|
|
+ /** Group ID of the calling process */
|
|
+ gid_t gid;
|
|
|
|
- /** Process ID of the calling thread */
|
|
- pid_t pid;
|
|
+ /** Process ID of the calling thread */
|
|
+ pid_t pid;
|
|
|
|
- /** Private filesystem data */
|
|
- void *private_data;
|
|
+ /** Private filesystem data */
|
|
+ void *private_data;
|
|
|
|
- /** Umask of the calling process */
|
|
- mode_t umask;
|
|
+ /** Umask of the calling process */
|
|
+ mode_t umask;
|
|
};
|
|
|
|
/**
|
|
@@ -859,15 +880,15 @@ struct fuse_context {
|
|
* Example usage, see hello.c
|
|
*/
|
|
/*
|
|
- int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
|
|
- void *private_data);
|
|
-*/
|
|
-#define fuse_main(argc, argv, op, private_data) \
|
|
- fuse_main_real(argc, argv, op, sizeof(*(op)), private_data)
|
|
+ * int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
|
|
+ * void *private_data);
|
|
+ */
|
|
+#define fuse_main(argc, argv, op, private_data) \
|
|
+ fuse_main_real(argc, argv, op, sizeof(*(op)), private_data)
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * More detailed API *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * More detailed API
|
|
+ */
|
|
|
|
/**
|
|
* Print available options (high- and low-level) to stdout. This is
|
|
@@ -910,12 +931,13 @@ void fuse_lib_help(struct fuse_args *args);
|
|
* @return the created FUSE handle
|
|
*/
|
|
#if FUSE_USE_VERSION == 30
|
|
-struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
|
|
- size_t op_size, void *private_data);
|
|
+struct fuse *fuse_new_30(struct fuse_args *args,
|
|
+ const struct fuse_operations *op, size_t op_size,
|
|
+ void *private_data);
|
|
#define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data)
|
|
#else
|
|
struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op,
|
|
- size_t op_size, void *private_data);
|
|
+ size_t op_size, void *private_data);
|
|
#endif
|
|
|
|
/**
|
|
@@ -940,7 +962,7 @@ void fuse_unmount(struct fuse *f);
|
|
/**
|
|
* Destroy the FUSE handle.
|
|
*
|
|
- * NOTE: This function does not unmount the filesystem. If this is
|
|
+ * NOTE: This function does not unmount the filesystem. If this is
|
|
* needed, call fuse_unmount() before calling this function.
|
|
*
|
|
* @param f the FUSE handle
|
|
@@ -1030,7 +1052,7 @@ int fuse_invalidate_path(struct fuse *f, const char *path);
|
|
* Do not call this directly, use fuse_main()
|
|
*/
|
|
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
|
|
- size_t op_size, void *private_data);
|
|
+ size_t op_size, void *private_data);
|
|
|
|
/**
|
|
* Start the cleanup thread when using option "remember".
|
|
@@ -1081,89 +1103,87 @@ struct fuse_fs;
|
|
*/
|
|
|
|
int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
|
|
- struct fuse_file_info *fi);
|
|
-int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
|
|
- const char *newpath, unsigned int flags);
|
|
+ struct fuse_file_info *fi);
|
|
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath,
|
|
+ unsigned int flags);
|
|
int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
|
|
int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
|
|
-int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
|
|
- const char *path);
|
|
+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path);
|
|
int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
|
|
-int fuse_fs_release(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi);
|
|
+int fuse_fs_release(struct fuse_fs *fs, const char *path,
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_open(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
|
|
- off_t off, struct fuse_file_info *fi);
|
|
+ off_t off, struct fuse_file_info *fi);
|
|
int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_bufvec **bufp, size_t size, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_bufvec **bufp, size_t size, off_t off,
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
|
|
- size_t size, off_t off, struct fuse_file_info *fi);
|
|
+ size_t size, off_t off, struct fuse_file_info *fi);
|
|
int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_bufvec *buf, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_bufvec *buf, off_t off,
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_flush(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
|
|
int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
|
|
- fuse_fill_dir_t filler, off_t off,
|
|
- struct fuse_file_info *fi, enum fuse_readdir_flags flags);
|
|
+ fuse_fill_dir_t filler, off_t off,
|
|
+ struct fuse_file_info *fi, enum fuse_readdir_flags flags);
|
|
int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_lock(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi, int cmd, struct flock *lock);
|
|
+ struct fuse_file_info *fi, int cmd, struct flock *lock);
|
|
int fuse_fs_flock(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi, int op);
|
|
+ struct fuse_file_info *fi, int op);
|
|
int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
|
|
- const struct timespec tv[2], struct fuse_file_info *fi);
|
|
+ const struct timespec tv[2], struct fuse_file_info *fi);
|
|
int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
|
|
int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
|
|
- size_t len);
|
|
+ size_t len);
|
|
int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
|
|
- dev_t rdev);
|
|
+ dev_t rdev);
|
|
int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
|
|
int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
|
|
- const char *value, size_t size, int flags);
|
|
+ const char *value, size_t size, int flags);
|
|
int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
|
|
- char *value, size_t size);
|
|
+ char *value, size_t size);
|
|
int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
|
|
- size_t size);
|
|
-int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
|
|
- const char *name);
|
|
+ size_t size);
|
|
+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name);
|
|
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
|
|
- uint64_t *idx);
|
|
+ uint64_t *idx);
|
|
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
|
|
- void *arg, struct fuse_file_info *fi, unsigned int flags,
|
|
- void *data);
|
|
+ void *arg, struct fuse_file_info *fi, unsigned int flags,
|
|
+ void *data);
|
|
int fuse_fs_poll(struct fuse_fs *fs, const char *path,
|
|
- struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
|
- unsigned *reventsp);
|
|
+ struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
|
+ unsigned *reventsp);
|
|
int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
|
|
- off_t offset, off_t length, struct fuse_file_info *fi);
|
|
+ off_t offset, off_t length, struct fuse_file_info *fi);
|
|
ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
|
|
- struct fuse_file_info *fi_in, off_t off_in,
|
|
- const char *path_out,
|
|
- struct fuse_file_info *fi_out, off_t off_out,
|
|
- size_t len, int flags);
|
|
+ struct fuse_file_info *fi_in, off_t off_in,
|
|
+ const char *path_out,
|
|
+ struct fuse_file_info *fi_out, off_t off_out,
|
|
+ size_t len, int flags);
|
|
off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
|
|
- struct fuse_file_info *fi);
|
|
+ struct fuse_file_info *fi);
|
|
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
|
|
- struct fuse_config *cfg);
|
|
+ struct fuse_config *cfg);
|
|
void fuse_fs_destroy(struct fuse_fs *fs);
|
|
|
|
int fuse_notify_poll(struct fuse_pollhandle *ph);
|
|
@@ -1182,7 +1202,7 @@ int fuse_notify_poll(struct fuse_pollhandle *ph);
|
|
* @return a new filesystem object
|
|
*/
|
|
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
|
|
- void *private_data);
|
|
+ void *private_data);
|
|
|
|
/**
|
|
* Factory for creating filesystem objects
|
|
@@ -1199,7 +1219,7 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
|
|
* @return the new filesystem object
|
|
*/
|
|
typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
|
|
- struct fuse_fs *fs[]);
|
|
+ struct fuse_fs *fs[]);
|
|
/**
|
|
* Register filesystem module
|
|
*
|
|
@@ -1211,7 +1231,7 @@ typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
|
|
* @param factory_ the factory function for this filesystem module
|
|
*/
|
|
#define FUSE_REGISTER_MODULE(name_, factory_) \
|
|
- fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
|
|
+ fuse_module_factory_t fuse_module_##name_##_factory = factory_
|
|
|
|
/** Get session from fuse object */
|
|
struct fuse_session *fuse_get_session(struct fuse *f);
|
|
diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h
|
|
index bf8f8cc865..bd9bf861f0 100644
|
|
--- a/tools/virtiofsd/fuse_common.h
|
|
+++ b/tools/virtiofsd/fuse_common.h
|
|
@@ -1,21 +1,23 @@
|
|
-/* FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+/*
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
/** @file */
|
|
|
|
#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_)
|
|
-#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
|
|
+#error \
|
|
+ "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
|
|
#endif
|
|
|
|
#ifndef FUSE_COMMON_H_
|
|
#define FUSE_COMMON_H_
|
|
|
|
-#include "fuse_opt.h"
|
|
#include "fuse_log.h"
|
|
+#include "fuse_opt.h"
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
@@ -25,7 +27,7 @@
|
|
/** Minor version of FUSE library interface */
|
|
#define FUSE_MINOR_VERSION 2
|
|
|
|
-#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
|
|
+#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
|
|
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
|
|
|
|
/**
|
|
@@ -38,67 +40,83 @@
|
|
* descriptors can share a single file handle.
|
|
*/
|
|
struct fuse_file_info {
|
|
- /** Open flags. Available in open() and release() */
|
|
- int flags;
|
|
-
|
|
- /** In case of a write operation indicates if this was caused
|
|
- by a delayed write from the page cache. If so, then the
|
|
- context's pid, uid, and gid fields will not be valid, and
|
|
- the *fh* value may not match the *fh* value that would
|
|
- have been sent with the corresponding individual write
|
|
- requests if write caching had been disabled. */
|
|
- unsigned int writepage : 1;
|
|
-
|
|
- /** Can be filled in by open, to use direct I/O on this file. */
|
|
- unsigned int direct_io : 1;
|
|
-
|
|
- /** Can be filled in by open. It signals the kernel that any
|
|
- currently cached file data (ie., data that the filesystem
|
|
- provided the last time the file was open) need not be
|
|
- invalidated. Has no effect when set in other contexts (in
|
|
- particular it does nothing when set by opendir()). */
|
|
- unsigned int keep_cache : 1;
|
|
-
|
|
- /** Indicates a flush operation. Set in flush operation, also
|
|
- maybe set in highlevel lock operation and lowlevel release
|
|
- operation. */
|
|
- unsigned int flush : 1;
|
|
-
|
|
- /** Can be filled in by open, to indicate that the file is not
|
|
- seekable. */
|
|
- unsigned int nonseekable : 1;
|
|
-
|
|
- /* Indicates that flock locks for this file should be
|
|
- released. If set, lock_owner shall contain a valid value.
|
|
- May only be set in ->release(). */
|
|
- unsigned int flock_release : 1;
|
|
-
|
|
- /** Can be filled in by opendir. It signals the kernel to
|
|
- enable caching of entries returned by readdir(). Has no
|
|
- effect when set in other contexts (in particular it does
|
|
- nothing when set by open()). */
|
|
- unsigned int cache_readdir : 1;
|
|
-
|
|
- /** Padding. Reserved for future use*/
|
|
- unsigned int padding : 25;
|
|
- unsigned int padding2 : 32;
|
|
-
|
|
- /** File handle id. May be filled in by filesystem in create,
|
|
- * open, and opendir(). Available in most other file operations on the
|
|
- * same file handle. */
|
|
- uint64_t fh;
|
|
-
|
|
- /** Lock owner id. Available in locking operations and flush */
|
|
- uint64_t lock_owner;
|
|
-
|
|
- /** Requested poll events. Available in ->poll. Only set on kernels
|
|
- which support it. If unsupported, this field is set to zero. */
|
|
- uint32_t poll_events;
|
|
+ /** Open flags. Available in open() and release() */
|
|
+ int flags;
|
|
+
|
|
+ /*
|
|
+ * In case of a write operation indicates if this was caused
|
|
+ * by a delayed write from the page cache. If so, then the
|
|
+ * context's pid, uid, and gid fields will not be valid, and
|
|
+ * the *fh* value may not match the *fh* value that would
|
|
+ * have been sent with the corresponding individual write
|
|
+ * requests if write caching had been disabled.
|
|
+ */
|
|
+ unsigned int writepage:1;
|
|
+
|
|
+ /** Can be filled in by open, to use direct I/O on this file. */
|
|
+ unsigned int direct_io:1;
|
|
+
|
|
+ /*
|
|
+ * Can be filled in by open. It signals the kernel that any
|
|
+ * currently cached file data (ie., data that the filesystem
|
|
+ * provided the last time the file was open) need not be
|
|
+ * invalidated. Has no effect when set in other contexts (in
|
|
+ * particular it does nothing when set by opendir()).
|
|
+ */
|
|
+ unsigned int keep_cache:1;
|
|
+
|
|
+ /*
|
|
+ * Indicates a flush operation. Set in flush operation, also
|
|
+ * maybe set in highlevel lock operation and lowlevel release
|
|
+ * operation.
|
|
+ */
|
|
+ unsigned int flush:1;
|
|
+
|
|
+ /*
|
|
+ * Can be filled in by open, to indicate that the file is not
|
|
+ * seekable.
|
|
+ */
|
|
+ unsigned int nonseekable:1;
|
|
+
|
|
+ /*
|
|
+ * Indicates that flock locks for this file should be
|
|
+ * released. If set, lock_owner shall contain a valid value.
|
|
+ * May only be set in ->release().
|
|
+ */
|
|
+ unsigned int flock_release:1;
|
|
+
|
|
+ /*
|
|
+ * Can be filled in by opendir. It signals the kernel to
|
|
+ * enable caching of entries returned by readdir(). Has no
|
|
+ * effect when set in other contexts (in particular it does
|
|
+ * nothing when set by open()).
|
|
+ */
|
|
+ unsigned int cache_readdir:1;
|
|
+
|
|
+ /** Padding. Reserved for future use*/
|
|
+ unsigned int padding:25;
|
|
+ unsigned int padding2:32;
|
|
+
|
|
+ /*
|
|
+ * File handle id. May be filled in by filesystem in create,
|
|
+ * open, and opendir(). Available in most other file operations on the
|
|
+ * same file handle.
|
|
+ */
|
|
+ uint64_t fh;
|
|
+
|
|
+ /** Lock owner id. Available in locking operations and flush */
|
|
+ uint64_t lock_owner;
|
|
+
|
|
+ /*
|
|
+ * Requested poll events. Available in ->poll. Only set on kernels
|
|
+ * which support it. If unsupported, this field is set to zero.
|
|
+ */
|
|
+ uint32_t poll_events;
|
|
};
|
|
|
|
-/**************************************************************************
|
|
- * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
|
|
- **************************************************************************/
|
|
+/*
|
|
+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
|
|
+ */
|
|
|
|
/**
|
|
* Indicates that the filesystem supports asynchronous read requests.
|
|
@@ -110,7 +128,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_ASYNC_READ (1 << 0)
|
|
+#define FUSE_CAP_ASYNC_READ (1 << 0)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports "remote" locking.
|
|
@@ -118,7 +136,7 @@ struct fuse_file_info {
|
|
* This feature is enabled by default when supported by the kernel,
|
|
* and if getlk() and setlk() handlers are implemented.
|
|
*/
|
|
-#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
|
+#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports the O_TRUNC open flag. If
|
|
@@ -127,14 +145,14 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
|
+#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports lookups of "." and "..".
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
|
+#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
|
|
|
/**
|
|
* Indicates that the kernel should not apply the umask to the
|
|
@@ -142,7 +160,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_DONT_MASK (1 << 6)
|
|
+#define FUSE_CAP_DONT_MASK (1 << 6)
|
|
|
|
/**
|
|
* Indicates that libfuse should try to use splice() when writing to
|
|
@@ -150,7 +168,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_SPLICE_WRITE (1 << 7)
|
|
+#define FUSE_CAP_SPLICE_WRITE (1 << 7)
|
|
|
|
/**
|
|
* Indicates that libfuse should try to move pages instead of copying when
|
|
@@ -158,7 +176,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_SPLICE_MOVE (1 << 8)
|
|
+#define FUSE_CAP_SPLICE_MOVE (1 << 8)
|
|
|
|
/**
|
|
* Indicates that libfuse should try to use splice() when reading from
|
|
@@ -167,7 +185,7 @@ struct fuse_file_info {
|
|
* This feature is enabled by default when supported by the kernel and
|
|
* if the filesystem implements a write_buf() handler.
|
|
*/
|
|
-#define FUSE_CAP_SPLICE_READ (1 << 9)
|
|
+#define FUSE_CAP_SPLICE_READ (1 << 9)
|
|
|
|
/**
|
|
* If set, the calls to flock(2) will be emulated using POSIX locks and must
|
|
@@ -180,14 +198,14 @@ struct fuse_file_info {
|
|
* This feature is enabled by default when supported by the kernel and
|
|
* if the filesystem implements a flock() handler.
|
|
*/
|
|
-#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
|
+#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports ioctl's on directories.
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
|
+#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
|
|
|
/**
|
|
* Traditionally, while a file is open the FUSE kernel module only
|
|
@@ -209,7 +227,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
|
|
+#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports readdirplus.
|
|
@@ -217,7 +235,7 @@ struct fuse_file_info {
|
|
* This feature is enabled by default when supported by the kernel and if the
|
|
* filesystem implements a readdirplus() handler.
|
|
*/
|
|
-#define FUSE_CAP_READDIRPLUS (1 << 13)
|
|
+#define FUSE_CAP_READDIRPLUS (1 << 13)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports adaptive readdirplus.
|
|
@@ -245,7 +263,7 @@ struct fuse_file_info {
|
|
* if the filesystem implements both a readdirplus() and a readdir()
|
|
* handler.
|
|
*/
|
|
-#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
|
|
+#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
|
|
|
|
/**
|
|
* Indicates that the filesystem supports asynchronous direct I/O submission.
|
|
@@ -256,7 +274,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_ASYNC_DIO (1 << 15)
|
|
+#define FUSE_CAP_ASYNC_DIO (1 << 15)
|
|
|
|
/**
|
|
* Indicates that writeback caching should be enabled. This means that
|
|
@@ -265,7 +283,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
|
|
+#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
|
|
|
|
/**
|
|
* Indicates support for zero-message opens. If this flag is set in
|
|
@@ -278,7 +296,7 @@ struct fuse_file_info {
|
|
* Setting (or unsetting) this flag in the `want` field has *no
|
|
* effect*.
|
|
*/
|
|
-#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
|
|
+#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
|
|
|
|
/**
|
|
* Indicates support for parallel directory operations. If this flag
|
|
@@ -288,7 +306,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
|
|
+#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
|
|
|
|
/**
|
|
* Indicates support for POSIX ACLs.
|
|
@@ -307,7 +325,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is disabled by default.
|
|
*/
|
|
-#define FUSE_CAP_POSIX_ACL (1 << 19)
|
|
+#define FUSE_CAP_POSIX_ACL (1 << 19)
|
|
|
|
/**
|
|
* Indicates that the filesystem is responsible for unsetting
|
|
@@ -316,7 +334,7 @@ struct fuse_file_info {
|
|
*
|
|
* This feature is enabled by default when supported by the kernel.
|
|
*/
|
|
-#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
|
|
+#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
|
|
|
|
/**
|
|
* Indicates support for zero-message opendirs. If this flag is set in
|
|
@@ -328,7 +346,7 @@ struct fuse_file_info {
|
|
*
|
|
* Setting (or unsetting) this flag in the `want` field has *no effect*.
|
|
*/
|
|
-#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
|
|
+#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
|
|
|
|
/**
|
|
* Ioctl flags
|
|
@@ -340,12 +358,12 @@ struct fuse_file_info {
|
|
*
|
|
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
|
*/
|
|
-#define FUSE_IOCTL_COMPAT (1 << 0)
|
|
-#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
|
|
-#define FUSE_IOCTL_RETRY (1 << 2)
|
|
-#define FUSE_IOCTL_DIR (1 << 4)
|
|
+#define FUSE_IOCTL_COMPAT (1 << 0)
|
|
+#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
|
|
+#define FUSE_IOCTL_RETRY (1 << 2)
|
|
+#define FUSE_IOCTL_DIR (1 << 4)
|
|
|
|
-#define FUSE_IOCTL_MAX_IOV 256
|
|
+#define FUSE_IOCTL_MAX_IOV 256
|
|
|
|
/**
|
|
* Connection information, passed to the ->init() method
|
|
@@ -355,114 +373,114 @@ struct fuse_file_info {
|
|
* value must usually be smaller than the indicated value.
|
|
*/
|
|
struct fuse_conn_info {
|
|
- /**
|
|
- * Major version of the protocol (read-only)
|
|
- */
|
|
- unsigned proto_major;
|
|
-
|
|
- /**
|
|
- * Minor version of the protocol (read-only)
|
|
- */
|
|
- unsigned proto_minor;
|
|
-
|
|
- /**
|
|
- * Maximum size of the write buffer
|
|
- */
|
|
- unsigned max_write;
|
|
-
|
|
- /**
|
|
- * Maximum size of read requests. A value of zero indicates no
|
|
- * limit. However, even if the filesystem does not specify a
|
|
- * limit, the maximum size of read requests will still be
|
|
- * limited by the kernel.
|
|
- *
|
|
- * NOTE: For the time being, the maximum size of read requests
|
|
- * must be set both here *and* passed to fuse_session_new()
|
|
- * using the ``-o max_read=<n>`` mount option. At some point
|
|
- * in the future, specifying the mount option will no longer
|
|
- * be necessary.
|
|
- */
|
|
- unsigned max_read;
|
|
-
|
|
- /**
|
|
- * Maximum readahead
|
|
- */
|
|
- unsigned max_readahead;
|
|
-
|
|
- /**
|
|
- * Capability flags that the kernel supports (read-only)
|
|
- */
|
|
- unsigned capable;
|
|
-
|
|
- /**
|
|
- * Capability flags that the filesystem wants to enable.
|
|
- *
|
|
- * libfuse attempts to initialize this field with
|
|
- * reasonable default values before calling the init() handler.
|
|
- */
|
|
- unsigned want;
|
|
-
|
|
- /**
|
|
- * Maximum number of pending "background" requests. A
|
|
- * background request is any type of request for which the
|
|
- * total number is not limited by other means. As of kernel
|
|
- * 4.8, only two types of requests fall into this category:
|
|
- *
|
|
- * 1. Read-ahead requests
|
|
- * 2. Asynchronous direct I/O requests
|
|
- *
|
|
- * Read-ahead requests are generated (if max_readahead is
|
|
- * non-zero) by the kernel to preemptively fill its caches
|
|
- * when it anticipates that userspace will soon read more
|
|
- * data.
|
|
- *
|
|
- * Asynchronous direct I/O requests are generated if
|
|
- * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large
|
|
- * direct I/O request. In this case the kernel will internally
|
|
- * split it up into multiple smaller requests and submit them
|
|
- * to the filesystem concurrently.
|
|
- *
|
|
- * Note that the following requests are *not* background
|
|
- * requests: writeback requests (limited by the kernel's
|
|
- * flusher algorithm), regular (i.e., synchronous and
|
|
- * buffered) userspace read/write requests (limited to one per
|
|
- * thread), asynchronous read requests (Linux's io_submit(2)
|
|
- * call actually blocks, so these are also limited to one per
|
|
- * thread).
|
|
- */
|
|
- unsigned max_background;
|
|
-
|
|
- /**
|
|
- * Kernel congestion threshold parameter. If the number of pending
|
|
- * background requests exceeds this number, the FUSE kernel module will
|
|
- * mark the filesystem as "congested". This instructs the kernel to
|
|
- * expect that queued requests will take some time to complete, and to
|
|
- * adjust its algorithms accordingly (e.g. by putting a waiting thread
|
|
- * to sleep instead of using a busy-loop).
|
|
- */
|
|
- unsigned congestion_threshold;
|
|
-
|
|
- /**
|
|
- * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible
|
|
- * for updating mtime and ctime when write requests are received. The
|
|
- * updated values are passed to the filesystem with setattr() requests.
|
|
- * However, if the filesystem does not support the full resolution of
|
|
- * the kernel timestamps (nanoseconds), the mtime and ctime values used
|
|
- * by kernel and filesystem will differ (and result in an apparent
|
|
- * change of times after a cache flush).
|
|
- *
|
|
- * To prevent this problem, this variable can be used to inform the
|
|
- * kernel about the timestamp granularity supported by the file-system.
|
|
- * The value should be power of 10. The default is 1, i.e. full
|
|
- * nano-second resolution. Filesystems supporting only second resolution
|
|
- * should set this to 1000000000.
|
|
- */
|
|
- unsigned time_gran;
|
|
-
|
|
- /**
|
|
- * For future use.
|
|
- */
|
|
- unsigned reserved[22];
|
|
+ /**
|
|
+ * Major version of the protocol (read-only)
|
|
+ */
|
|
+ unsigned proto_major;
|
|
+
|
|
+ /**
|
|
+ * Minor version of the protocol (read-only)
|
|
+ */
|
|
+ unsigned proto_minor;
|
|
+
|
|
+ /**
|
|
+ * Maximum size of the write buffer
|
|
+ */
|
|
+ unsigned max_write;
|
|
+
|
|
+ /**
|
|
+ * Maximum size of read requests. A value of zero indicates no
|
|
+ * limit. However, even if the filesystem does not specify a
|
|
+ * limit, the maximum size of read requests will still be
|
|
+ * limited by the kernel.
|
|
+ *
|
|
+ * NOTE: For the time being, the maximum size of read requests
|
|
+ * must be set both here *and* passed to fuse_session_new()
|
|
+ * using the ``-o max_read=<n>`` mount option. At some point
|
|
+ * in the future, specifying the mount option will no longer
|
|
+ * be necessary.
|
|
+ */
|
|
+ unsigned max_read;
|
|
+
|
|
+ /**
|
|
+ * Maximum readahead
|
|
+ */
|
|
+ unsigned max_readahead;
|
|
+
|
|
+ /**
|
|
+ * Capability flags that the kernel supports (read-only)
|
|
+ */
|
|
+ unsigned capable;
|
|
+
|
|
+ /**
|
|
+ * Capability flags that the filesystem wants to enable.
|
|
+ *
|
|
+ * libfuse attempts to initialize this field with
|
|
+ * reasonable default values before calling the init() handler.
|
|
+ */
|
|
+ unsigned want;
|
|
+
|
|
+ /**
|
|
+ * Maximum number of pending "background" requests. A
|
|
+ * background request is any type of request for which the
|
|
+ * total number is not limited by other means. As of kernel
|
|
+ * 4.8, only two types of requests fall into this category:
|
|
+ *
|
|
+ * 1. Read-ahead requests
|
|
+ * 2. Asynchronous direct I/O requests
|
|
+ *
|
|
+ * Read-ahead requests are generated (if max_readahead is
|
|
+ * non-zero) by the kernel to preemptively fill its caches
|
|
+ * when it anticipates that userspace will soon read more
|
|
+ * data.
|
|
+ *
|
|
+ * Asynchronous direct I/O requests are generated if
|
|
+ * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large
|
|
+ * direct I/O request. In this case the kernel will internally
|
|
+ * split it up into multiple smaller requests and submit them
|
|
+ * to the filesystem concurrently.
|
|
+ *
|
|
+ * Note that the following requests are *not* background
|
|
+ * requests: writeback requests (limited by the kernel's
|
|
+ * flusher algorithm), regular (i.e., synchronous and
|
|
+ * buffered) userspace read/write requests (limited to one per
|
|
+ * thread), asynchronous read requests (Linux's io_submit(2)
|
|
+ * call actually blocks, so these are also limited to one per
|
|
+ * thread).
|
|
+ */
|
|
+ unsigned max_background;
|
|
+
|
|
+ /**
|
|
+ * Kernel congestion threshold parameter. If the number of pending
|
|
+ * background requests exceeds this number, the FUSE kernel module will
|
|
+ * mark the filesystem as "congested". This instructs the kernel to
|
|
+ * expect that queued requests will take some time to complete, and to
|
|
+ * adjust its algorithms accordingly (e.g. by putting a waiting thread
|
|
+ * to sleep instead of using a busy-loop).
|
|
+ */
|
|
+ unsigned congestion_threshold;
|
|
+
|
|
+ /**
|
|
+ * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible
|
|
+ * for updating mtime and ctime when write requests are received. The
|
|
+ * updated values are passed to the filesystem with setattr() requests.
|
|
+ * However, if the filesystem does not support the full resolution of
|
|
+ * the kernel timestamps (nanoseconds), the mtime and ctime values used
|
|
+ * by kernel and filesystem will differ (and result in an apparent
|
|
+ * change of times after a cache flush).
|
|
+ *
|
|
+ * To prevent this problem, this variable can be used to inform the
|
|
+ * kernel about the timestamp granularity supported by the file-system.
|
|
+ * The value should be power of 10. The default is 1, i.e. full
|
|
+ * nano-second resolution. Filesystems supporting only second resolution
|
|
+ * should set this to 1000000000.
|
|
+ */
|
|
+ unsigned time_gran;
|
|
+
|
|
+ /**
|
|
+ * For future use.
|
|
+ */
|
|
+ unsigned reserved[22];
|
|
};
|
|
|
|
struct fuse_session;
|
|
@@ -489,21 +507,20 @@ struct fuse_conn_info_opts;
|
|
* -o async_read sets FUSE_CAP_ASYNC_READ in conn->want
|
|
* -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want
|
|
* -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want
|
|
- * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock
|
|
- * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want
|
|
- * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want
|
|
- * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want
|
|
- * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want
|
|
- * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want
|
|
- * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want
|
|
- * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want
|
|
- * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets
|
|
- * FUSE_CAP_READDIRPLUS_AUTO in conn->want
|
|
- * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and
|
|
- * FUSE_CAP_READDIRPLUS_AUTO in conn->want
|
|
- * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want
|
|
- * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want
|
|
- * -o time_gran=N sets conn->time_gran
|
|
+ * -o no_remote_lock Equivalent to -o
|
|
+ *no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets
|
|
+ *FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets
|
|
+ *FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets
|
|
+ *FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets
|
|
+ *FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets
|
|
+ *FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets
|
|
+ *FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets
|
|
+ *FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets
|
|
+ *FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o
|
|
+ *readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO
|
|
+ *in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in
|
|
+ *conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in
|
|
+ *conn->want -o time_gran=N sets conn->time_gran
|
|
*
|
|
* Known options will be removed from *args*, unknown options will be
|
|
* passed through unchanged.
|
|
@@ -511,7 +528,7 @@ struct fuse_conn_info_opts;
|
|
* @param args argument vector (input+output)
|
|
* @return parsed options
|
|
**/
|
|
-struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
|
|
+struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args);
|
|
|
|
/**
|
|
* This function applies the (parsed) parameters in *opts* to the
|
|
@@ -521,7 +538,7 @@ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
|
|
* option has been explicitly set.
|
|
*/
|
|
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
|
|
- struct fuse_conn_info *conn);
|
|
+ struct fuse_conn_info *conn);
|
|
|
|
/**
|
|
* Go into the background
|
|
@@ -552,81 +569,81 @@ const char *fuse_pkgversion(void);
|
|
*/
|
|
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Data buffer *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Data buffer
|
|
+ */
|
|
|
|
/**
|
|
* Buffer flags
|
|
*/
|
|
enum fuse_buf_flags {
|
|
- /**
|
|
- * Buffer contains a file descriptor
|
|
- *
|
|
- * If this flag is set, the .fd field is valid, otherwise the
|
|
- * .mem fields is valid.
|
|
- */
|
|
- FUSE_BUF_IS_FD = (1 << 1),
|
|
-
|
|
- /**
|
|
- * Seek on the file descriptor
|
|
- *
|
|
- * If this flag is set then the .pos field is valid and is
|
|
- * used to seek to the given offset before performing
|
|
- * operation on file descriptor.
|
|
- */
|
|
- FUSE_BUF_FD_SEEK = (1 << 2),
|
|
-
|
|
- /**
|
|
- * Retry operation on file descriptor
|
|
- *
|
|
- * If this flag is set then retry operation on file descriptor
|
|
- * until .size bytes have been copied or an error or EOF is
|
|
- * detected.
|
|
- */
|
|
- FUSE_BUF_FD_RETRY = (1 << 3),
|
|
+ /**
|
|
+ * Buffer contains a file descriptor
|
|
+ *
|
|
+ * If this flag is set, the .fd field is valid, otherwise the
|
|
+ * .mem fields is valid.
|
|
+ */
|
|
+ FUSE_BUF_IS_FD = (1 << 1),
|
|
+
|
|
+ /**
|
|
+ * Seek on the file descriptor
|
|
+ *
|
|
+ * If this flag is set then the .pos field is valid and is
|
|
+ * used to seek to the given offset before performing
|
|
+ * operation on file descriptor.
|
|
+ */
|
|
+ FUSE_BUF_FD_SEEK = (1 << 2),
|
|
+
|
|
+ /**
|
|
+ * Retry operation on file descriptor
|
|
+ *
|
|
+ * If this flag is set then retry operation on file descriptor
|
|
+ * until .size bytes have been copied or an error or EOF is
|
|
+ * detected.
|
|
+ */
|
|
+ FUSE_BUF_FD_RETRY = (1 << 3),
|
|
};
|
|
|
|
/**
|
|
* Buffer copy flags
|
|
*/
|
|
enum fuse_buf_copy_flags {
|
|
- /**
|
|
- * Don't use splice(2)
|
|
- *
|
|
- * Always fall back to using read and write instead of
|
|
- * splice(2) to copy data from one file descriptor to another.
|
|
- *
|
|
- * If this flag is not set, then only fall back if splice is
|
|
- * unavailable.
|
|
- */
|
|
- FUSE_BUF_NO_SPLICE = (1 << 1),
|
|
-
|
|
- /**
|
|
- * Force splice
|
|
- *
|
|
- * Always use splice(2) to copy data from one file descriptor
|
|
- * to another. If splice is not available, return -EINVAL.
|
|
- */
|
|
- FUSE_BUF_FORCE_SPLICE = (1 << 2),
|
|
-
|
|
- /**
|
|
- * Try to move data with splice.
|
|
- *
|
|
- * If splice is used, try to move pages from the source to the
|
|
- * destination instead of copying. See documentation of
|
|
- * SPLICE_F_MOVE in splice(2) man page.
|
|
- */
|
|
- FUSE_BUF_SPLICE_MOVE = (1 << 3),
|
|
-
|
|
- /**
|
|
- * Don't block on the pipe when copying data with splice
|
|
- *
|
|
- * Makes the operations on the pipe non-blocking (if the pipe
|
|
- * is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
|
|
- * man page.
|
|
- */
|
|
- FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
|
|
+ /**
|
|
+ * Don't use splice(2)
|
|
+ *
|
|
+ * Always fall back to using read and write instead of
|
|
+ * splice(2) to copy data from one file descriptor to another.
|
|
+ *
|
|
+ * If this flag is not set, then only fall back if splice is
|
|
+ * unavailable.
|
|
+ */
|
|
+ FUSE_BUF_NO_SPLICE = (1 << 1),
|
|
+
|
|
+ /**
|
|
+ * Force splice
|
|
+ *
|
|
+ * Always use splice(2) to copy data from one file descriptor
|
|
+ * to another. If splice is not available, return -EINVAL.
|
|
+ */
|
|
+ FUSE_BUF_FORCE_SPLICE = (1 << 2),
|
|
+
|
|
+ /**
|
|
+ * Try to move data with splice.
|
|
+ *
|
|
+ * If splice is used, try to move pages from the source to the
|
|
+ * destination instead of copying. See documentation of
|
|
+ * SPLICE_F_MOVE in splice(2) man page.
|
|
+ */
|
|
+ FUSE_BUF_SPLICE_MOVE = (1 << 3),
|
|
+
|
|
+ /**
|
|
+ * Don't block on the pipe when copying data with splice
|
|
+ *
|
|
+ * Makes the operations on the pipe non-blocking (if the pipe
|
|
+ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
|
|
+ * man page.
|
|
+ */
|
|
+ FUSE_BUF_SPLICE_NONBLOCK = (1 << 4),
|
|
};
|
|
|
|
/**
|
|
@@ -636,36 +653,36 @@ enum fuse_buf_copy_flags {
|
|
* be supplied as a memory pointer or as a file descriptor
|
|
*/
|
|
struct fuse_buf {
|
|
- /**
|
|
- * Size of data in bytes
|
|
- */
|
|
- size_t size;
|
|
-
|
|
- /**
|
|
- * Buffer flags
|
|
- */
|
|
- enum fuse_buf_flags flags;
|
|
-
|
|
- /**
|
|
- * Memory pointer
|
|
- *
|
|
- * Used unless FUSE_BUF_IS_FD flag is set.
|
|
- */
|
|
- void *mem;
|
|
-
|
|
- /**
|
|
- * File descriptor
|
|
- *
|
|
- * Used if FUSE_BUF_IS_FD flag is set.
|
|
- */
|
|
- int fd;
|
|
-
|
|
- /**
|
|
- * File position
|
|
- *
|
|
- * Used if FUSE_BUF_FD_SEEK flag is set.
|
|
- */
|
|
- off_t pos;
|
|
+ /**
|
|
+ * Size of data in bytes
|
|
+ */
|
|
+ size_t size;
|
|
+
|
|
+ /**
|
|
+ * Buffer flags
|
|
+ */
|
|
+ enum fuse_buf_flags flags;
|
|
+
|
|
+ /**
|
|
+ * Memory pointer
|
|
+ *
|
|
+ * Used unless FUSE_BUF_IS_FD flag is set.
|
|
+ */
|
|
+ void *mem;
|
|
+
|
|
+ /**
|
|
+ * File descriptor
|
|
+ *
|
|
+ * Used if FUSE_BUF_IS_FD flag is set.
|
|
+ */
|
|
+ int fd;
|
|
+
|
|
+ /**
|
|
+ * File position
|
|
+ *
|
|
+ * Used if FUSE_BUF_FD_SEEK flag is set.
|
|
+ */
|
|
+ off_t pos;
|
|
};
|
|
|
|
/**
|
|
@@ -677,41 +694,39 @@ struct fuse_buf {
|
|
* Allocate dynamically to add more than one buffer.
|
|
*/
|
|
struct fuse_bufvec {
|
|
- /**
|
|
- * Number of buffers in the array
|
|
- */
|
|
- size_t count;
|
|
-
|
|
- /**
|
|
- * Index of current buffer within the array
|
|
- */
|
|
- size_t idx;
|
|
-
|
|
- /**
|
|
- * Current offset within the current buffer
|
|
- */
|
|
- size_t off;
|
|
-
|
|
- /**
|
|
- * Array of buffers
|
|
- */
|
|
- struct fuse_buf buf[1];
|
|
+ /**
|
|
+ * Number of buffers in the array
|
|
+ */
|
|
+ size_t count;
|
|
+
|
|
+ /**
|
|
+ * Index of current buffer within the array
|
|
+ */
|
|
+ size_t idx;
|
|
+
|
|
+ /**
|
|
+ * Current offset within the current buffer
|
|
+ */
|
|
+ size_t off;
|
|
+
|
|
+ /**
|
|
+ * Array of buffers
|
|
+ */
|
|
+ struct fuse_buf buf[1];
|
|
};
|
|
|
|
/* Initialize bufvec with a single buffer of given size */
|
|
-#define FUSE_BUFVEC_INIT(size__) \
|
|
- ((struct fuse_bufvec) { \
|
|
- /* .count= */ 1, \
|
|
- /* .idx = */ 0, \
|
|
- /* .off = */ 0, \
|
|
- /* .buf = */ { /* [0] = */ { \
|
|
- /* .size = */ (size__), \
|
|
- /* .flags = */ (enum fuse_buf_flags) 0, \
|
|
- /* .mem = */ NULL, \
|
|
- /* .fd = */ -1, \
|
|
- /* .pos = */ 0, \
|
|
- } } \
|
|
- } )
|
|
+#define FUSE_BUFVEC_INIT(size__) \
|
|
+ ((struct fuse_bufvec){ /* .count= */ 1, \
|
|
+ /* .idx = */ 0, \
|
|
+ /* .off = */ 0, /* .buf = */ \
|
|
+ { /* [0] = */ { \
|
|
+ /* .size = */ (size__), \
|
|
+ /* .flags = */ (enum fuse_buf_flags)0, \
|
|
+ /* .mem = */ NULL, \
|
|
+ /* .fd = */ -1, \
|
|
+ /* .pos = */ 0, \
|
|
+ } } })
|
|
|
|
/**
|
|
* Get total size of data in a fuse buffer vector
|
|
@@ -730,16 +745,16 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv);
|
|
* @return actual number of bytes copied or -errno on error
|
|
*/
|
|
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
|
|
- enum fuse_buf_copy_flags flags);
|
|
+ enum fuse_buf_copy_flags flags);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Signal handling *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Signal handling
|
|
+ */
|
|
|
|
/**
|
|
* Exit session on HUP, TERM and INT signals and ignore PIPE signal
|
|
*
|
|
- * Stores session in a global variable. May only be called once per
|
|
+ * Stores session in a global variable. May only be called once per
|
|
* process until fuse_remove_signal_handlers() is called.
|
|
*
|
|
* Once either of the POSIX signals arrives, the signal handler calls
|
|
@@ -766,12 +781,12 @@ int fuse_set_signal_handlers(struct fuse_session *se);
|
|
*/
|
|
void fuse_remove_signal_handlers(struct fuse_session *se);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Compatibility stuff *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Compatibility stuff
|
|
+ */
|
|
|
|
#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
|
|
-# error only API version 30 or greater is supported
|
|
+#error only API version 30 or greater is supported
|
|
#endif
|
|
|
|
|
|
@@ -781,11 +796,14 @@ void fuse_remove_signal_handlers(struct fuse_session *se);
|
|
* On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
|
|
*/
|
|
|
|
-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
|
|
+#if defined(__GNUC__) && \
|
|
+ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \
|
|
+ !defined __cplusplus
|
|
_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
|
|
#else
|
|
-struct _fuse_off_t_must_be_64bit_dummy_struct \
|
|
- { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
|
|
+struct _fuse_off_t_must_be_64bit_dummy_struct {
|
|
+ unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1);
|
|
+};
|
|
#endif
|
|
|
|
#endif /* FUSE_COMMON_H_ */
|
|
diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h
|
|
index b39522e3ca..e63cb58388 100644
|
|
--- a/tools/virtiofsd/fuse_i.h
|
|
+++ b/tools/virtiofsd/fuse_i.h
|
|
@@ -1,71 +1,71 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#include "fuse.h"
|
|
#include "fuse_lowlevel.h"
|
|
|
|
struct fuse_req {
|
|
- struct fuse_session *se;
|
|
- uint64_t unique;
|
|
- int ctr;
|
|
- pthread_mutex_t lock;
|
|
- struct fuse_ctx ctx;
|
|
- struct fuse_chan *ch;
|
|
- int interrupted;
|
|
- unsigned int ioctl_64bit : 1;
|
|
- union {
|
|
- struct {
|
|
- uint64_t unique;
|
|
- } i;
|
|
- struct {
|
|
- fuse_interrupt_func_t func;
|
|
- void *data;
|
|
- } ni;
|
|
- } u;
|
|
- struct fuse_req *next;
|
|
- struct fuse_req *prev;
|
|
+ struct fuse_session *se;
|
|
+ uint64_t unique;
|
|
+ int ctr;
|
|
+ pthread_mutex_t lock;
|
|
+ struct fuse_ctx ctx;
|
|
+ struct fuse_chan *ch;
|
|
+ int interrupted;
|
|
+ unsigned int ioctl_64bit:1;
|
|
+ union {
|
|
+ struct {
|
|
+ uint64_t unique;
|
|
+ } i;
|
|
+ struct {
|
|
+ fuse_interrupt_func_t func;
|
|
+ void *data;
|
|
+ } ni;
|
|
+ } u;
|
|
+ struct fuse_req *next;
|
|
+ struct fuse_req *prev;
|
|
};
|
|
|
|
struct fuse_notify_req {
|
|
- uint64_t unique;
|
|
- void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
|
|
- const void *, const struct fuse_buf *);
|
|
- struct fuse_notify_req *next;
|
|
- struct fuse_notify_req *prev;
|
|
+ uint64_t unique;
|
|
+ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
|
|
+ const void *, const struct fuse_buf *);
|
|
+ struct fuse_notify_req *next;
|
|
+ struct fuse_notify_req *prev;
|
|
};
|
|
|
|
struct fuse_session {
|
|
- char *mountpoint;
|
|
- volatile int exited;
|
|
- int fd;
|
|
- int debug;
|
|
- int deny_others;
|
|
- struct fuse_lowlevel_ops op;
|
|
- int got_init;
|
|
- struct cuse_data *cuse_data;
|
|
- void *userdata;
|
|
- uid_t owner;
|
|
- struct fuse_conn_info conn;
|
|
- struct fuse_req list;
|
|
- struct fuse_req interrupts;
|
|
- pthread_mutex_t lock;
|
|
- int got_destroy;
|
|
- int broken_splice_nonblock;
|
|
- uint64_t notify_ctr;
|
|
- struct fuse_notify_req notify_list;
|
|
- size_t bufsize;
|
|
- int error;
|
|
+ char *mountpoint;
|
|
+ volatile int exited;
|
|
+ int fd;
|
|
+ int debug;
|
|
+ int deny_others;
|
|
+ struct fuse_lowlevel_ops op;
|
|
+ int got_init;
|
|
+ struct cuse_data *cuse_data;
|
|
+ void *userdata;
|
|
+ uid_t owner;
|
|
+ struct fuse_conn_info conn;
|
|
+ struct fuse_req list;
|
|
+ struct fuse_req interrupts;
|
|
+ pthread_mutex_t lock;
|
|
+ int got_destroy;
|
|
+ int broken_splice_nonblock;
|
|
+ uint64_t notify_ctr;
|
|
+ struct fuse_notify_req notify_list;
|
|
+ size_t bufsize;
|
|
+ int error;
|
|
};
|
|
|
|
struct fuse_chan {
|
|
- pthread_mutex_t lock;
|
|
- int ctr;
|
|
- int fd;
|
|
+ pthread_mutex_t lock;
|
|
+ int ctr;
|
|
+ int fd;
|
|
};
|
|
|
|
/**
|
|
@@ -76,19 +76,20 @@ struct fuse_chan {
|
|
*
|
|
*/
|
|
struct fuse_module {
|
|
- char *name;
|
|
- fuse_module_factory_t factory;
|
|
- struct fuse_module *next;
|
|
- struct fusemod_so *so;
|
|
- int ctr;
|
|
+ char *name;
|
|
+ fuse_module_factory_t factory;
|
|
+ struct fuse_module *next;
|
|
+ struct fusemod_so *so;
|
|
+ int ctr;
|
|
};
|
|
|
|
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
|
|
- int count);
|
|
+ int count);
|
|
void fuse_free_req(fuse_req_t req);
|
|
|
|
void fuse_session_process_buf_int(struct fuse_session *se,
|
|
- const struct fuse_buf *buf, struct fuse_chan *ch);
|
|
+ const struct fuse_buf *buf,
|
|
+ struct fuse_chan *ch);
|
|
|
|
|
|
#define FUSE_MAX_MAX_PAGES 256
|
|
diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c
|
|
index 0d268ab014..11345f9ec8 100644
|
|
--- a/tools/virtiofsd/fuse_log.c
|
|
+++ b/tools/virtiofsd/fuse_log.c
|
|
@@ -1,40 +1,40 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2019 Red Hat, Inc.
|
|
-
|
|
- Logging API.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2019 Red Hat, Inc.
|
|
+ *
|
|
+ * Logging API.
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#include "fuse_log.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
-static void default_log_func(
|
|
- __attribute__(( unused )) enum fuse_log_level level,
|
|
- const char *fmt, va_list ap)
|
|
+static void default_log_func(__attribute__((unused)) enum fuse_log_level level,
|
|
+ const char *fmt, va_list ap)
|
|
{
|
|
- vfprintf(stderr, fmt, ap);
|
|
+ vfprintf(stderr, fmt, ap);
|
|
}
|
|
|
|
static fuse_log_func_t log_func = default_log_func;
|
|
|
|
void fuse_set_log_func(fuse_log_func_t func)
|
|
{
|
|
- if (!func)
|
|
- func = default_log_func;
|
|
+ if (!func) {
|
|
+ func = default_log_func;
|
|
+ }
|
|
|
|
- log_func = func;
|
|
+ log_func = func;
|
|
}
|
|
|
|
void fuse_log(enum fuse_log_level level, const char *fmt, ...)
|
|
{
|
|
- va_list ap;
|
|
+ va_list ap;
|
|
|
|
- va_start(ap, fmt);
|
|
- log_func(level, fmt, ap);
|
|
- va_end(ap);
|
|
+ va_start(ap, fmt);
|
|
+ log_func(level, fmt, ap);
|
|
+ va_end(ap);
|
|
}
|
|
diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h
|
|
index 0af700da6b..bf6c11ff11 100644
|
|
--- a/tools/virtiofsd/fuse_log.h
|
|
+++ b/tools/virtiofsd/fuse_log.h
|
|
@@ -1,10 +1,10 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2019 Red Hat, Inc.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2019 Red Hat, Inc.
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
#ifndef FUSE_LOG_H_
|
|
#define FUSE_LOG_H_
|
|
@@ -22,14 +22,14 @@
|
|
* These levels correspond to syslog(2) log levels since they are widely used.
|
|
*/
|
|
enum fuse_log_level {
|
|
- FUSE_LOG_EMERG,
|
|
- FUSE_LOG_ALERT,
|
|
- FUSE_LOG_CRIT,
|
|
- FUSE_LOG_ERR,
|
|
- FUSE_LOG_WARNING,
|
|
- FUSE_LOG_NOTICE,
|
|
- FUSE_LOG_INFO,
|
|
- FUSE_LOG_DEBUG
|
|
+ FUSE_LOG_EMERG,
|
|
+ FUSE_LOG_ALERT,
|
|
+ FUSE_LOG_CRIT,
|
|
+ FUSE_LOG_ERR,
|
|
+ FUSE_LOG_WARNING,
|
|
+ FUSE_LOG_NOTICE,
|
|
+ FUSE_LOG_INFO,
|
|
+ FUSE_LOG_DEBUG
|
|
};
|
|
|
|
/**
|
|
@@ -45,8 +45,8 @@ enum fuse_log_level {
|
|
* @param fmt sprintf-style format string including newline
|
|
* @param ap format string arguments
|
|
*/
|
|
-typedef void (*fuse_log_func_t)(enum fuse_log_level level,
|
|
- const char *fmt, va_list ap);
|
|
+typedef void (*fuse_log_func_t)(enum fuse_log_level level, const char *fmt,
|
|
+ va_list ap);
|
|
|
|
/**
|
|
* Install a custom log handler function.
|
|
diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
|
|
index e6fa247924..5c9cb52f2a 100644
|
|
--- a/tools/virtiofsd/fuse_lowlevel.c
|
|
+++ b/tools/virtiofsd/fuse_lowlevel.c
|
|
@@ -1,2380 +1,2515 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- Implementation of (most of) the low-level FUSE API. The session loop
|
|
- functions are implemented in separate files.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * Implementation of (most of) the low-level FUSE API. The session loop
|
|
+ * functions are implemented in separate files.
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "config.h"
|
|
#include "fuse_i.h"
|
|
#include "fuse_kernel.h"
|
|
-#include "fuse_opt.h"
|
|
#include "fuse_misc.h"
|
|
+#include "fuse_opt.h"
|
|
|
|
+#include <assert.h>
|
|
+#include <errno.h>
|
|
+#include <limits.h>
|
|
+#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
-#include <stddef.h>
|
|
#include <string.h>
|
|
-#include <unistd.h>
|
|
-#include <limits.h>
|
|
-#include <errno.h>
|
|
-#include <assert.h>
|
|
#include <sys/file.h>
|
|
-
|
|
+#include <unistd.h>
|
|
|
|
|
|
#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
|
|
#define OFFSET_MAX 0x7fffffffffffffffLL
|
|
|
|
-#define container_of(ptr, type, member) ({ \
|
|
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
|
- (type *)( (char *)__mptr - offsetof(type,member) );})
|
|
+#define container_of(ptr, type, member) \
|
|
+ ({ \
|
|
+ const typeof(((type *)0)->member) *__mptr = (ptr); \
|
|
+ (type *)((char *)__mptr - offsetof(type, member)); \
|
|
+ })
|
|
|
|
struct fuse_pollhandle {
|
|
- uint64_t kh;
|
|
- struct fuse_session *se;
|
|
+ uint64_t kh;
|
|
+ struct fuse_session *se;
|
|
};
|
|
|
|
static size_t pagesize;
|
|
|
|
static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
|
|
{
|
|
- pagesize = getpagesize();
|
|
+ pagesize = getpagesize();
|
|
}
|
|
|
|
static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
|
|
{
|
|
- attr->ino = stbuf->st_ino;
|
|
- attr->mode = stbuf->st_mode;
|
|
- attr->nlink = stbuf->st_nlink;
|
|
- attr->uid = stbuf->st_uid;
|
|
- attr->gid = stbuf->st_gid;
|
|
- attr->rdev = stbuf->st_rdev;
|
|
- attr->size = stbuf->st_size;
|
|
- attr->blksize = stbuf->st_blksize;
|
|
- attr->blocks = stbuf->st_blocks;
|
|
- attr->atime = stbuf->st_atime;
|
|
- attr->mtime = stbuf->st_mtime;
|
|
- attr->ctime = stbuf->st_ctime;
|
|
- attr->atimensec = ST_ATIM_NSEC(stbuf);
|
|
- attr->mtimensec = ST_MTIM_NSEC(stbuf);
|
|
- attr->ctimensec = ST_CTIM_NSEC(stbuf);
|
|
+ attr->ino = stbuf->st_ino;
|
|
+ attr->mode = stbuf->st_mode;
|
|
+ attr->nlink = stbuf->st_nlink;
|
|
+ attr->uid = stbuf->st_uid;
|
|
+ attr->gid = stbuf->st_gid;
|
|
+ attr->rdev = stbuf->st_rdev;
|
|
+ attr->size = stbuf->st_size;
|
|
+ attr->blksize = stbuf->st_blksize;
|
|
+ attr->blocks = stbuf->st_blocks;
|
|
+ attr->atime = stbuf->st_atime;
|
|
+ attr->mtime = stbuf->st_mtime;
|
|
+ attr->ctime = stbuf->st_ctime;
|
|
+ attr->atimensec = ST_ATIM_NSEC(stbuf);
|
|
+ attr->mtimensec = ST_MTIM_NSEC(stbuf);
|
|
+ attr->ctimensec = ST_CTIM_NSEC(stbuf);
|
|
}
|
|
|
|
static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
|
|
{
|
|
- stbuf->st_mode = attr->mode;
|
|
- stbuf->st_uid = attr->uid;
|
|
- stbuf->st_gid = attr->gid;
|
|
- stbuf->st_size = attr->size;
|
|
- stbuf->st_atime = attr->atime;
|
|
- stbuf->st_mtime = attr->mtime;
|
|
- stbuf->st_ctime = attr->ctime;
|
|
- ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
|
|
- ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
|
|
- ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
|
|
+ stbuf->st_mode = attr->mode;
|
|
+ stbuf->st_uid = attr->uid;
|
|
+ stbuf->st_gid = attr->gid;
|
|
+ stbuf->st_size = attr->size;
|
|
+ stbuf->st_atime = attr->atime;
|
|
+ stbuf->st_mtime = attr->mtime;
|
|
+ stbuf->st_ctime = attr->ctime;
|
|
+ ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
|
|
+ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
|
|
+ ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
|
|
}
|
|
|
|
-static size_t iov_length(const struct iovec *iov, size_t count)
|
|
+static size_t iov_length(const struct iovec *iov, size_t count)
|
|
{
|
|
- size_t seg;
|
|
- size_t ret = 0;
|
|
+ size_t seg;
|
|
+ size_t ret = 0;
|
|
|
|
- for (seg = 0; seg < count; seg++)
|
|
- ret += iov[seg].iov_len;
|
|
- return ret;
|
|
+ for (seg = 0; seg < count; seg++) {
|
|
+ ret += iov[seg].iov_len;
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
|
|
static void list_init_req(struct fuse_req *req)
|
|
{
|
|
- req->next = req;
|
|
- req->prev = req;
|
|
+ req->next = req;
|
|
+ req->prev = req;
|
|
}
|
|
|
|
static void list_del_req(struct fuse_req *req)
|
|
{
|
|
- struct fuse_req *prev = req->prev;
|
|
- struct fuse_req *next = req->next;
|
|
- prev->next = next;
|
|
- next->prev = prev;
|
|
+ struct fuse_req *prev = req->prev;
|
|
+ struct fuse_req *next = req->next;
|
|
+ prev->next = next;
|
|
+ next->prev = prev;
|
|
}
|
|
|
|
static void list_add_req(struct fuse_req *req, struct fuse_req *next)
|
|
{
|
|
- struct fuse_req *prev = next->prev;
|
|
- req->next = next;
|
|
- req->prev = prev;
|
|
- prev->next = req;
|
|
- next->prev = req;
|
|
+ struct fuse_req *prev = next->prev;
|
|
+ req->next = next;
|
|
+ req->prev = prev;
|
|
+ prev->next = req;
|
|
+ next->prev = req;
|
|
}
|
|
|
|
static void destroy_req(fuse_req_t req)
|
|
{
|
|
- pthread_mutex_destroy(&req->lock);
|
|
- free(req);
|
|
+ pthread_mutex_destroy(&req->lock);
|
|
+ free(req);
|
|
}
|
|
|
|
void fuse_free_req(fuse_req_t req)
|
|
{
|
|
- int ctr;
|
|
- struct fuse_session *se = req->se;
|
|
+ int ctr;
|
|
+ struct fuse_session *se = req->se;
|
|
|
|
- pthread_mutex_lock(&se->lock);
|
|
- req->u.ni.func = NULL;
|
|
- req->u.ni.data = NULL;
|
|
- list_del_req(req);
|
|
- ctr = --req->ctr;
|
|
- req->ch = NULL;
|
|
- pthread_mutex_unlock(&se->lock);
|
|
- if (!ctr)
|
|
- destroy_req(req);
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ req->u.ni.func = NULL;
|
|
+ req->u.ni.data = NULL;
|
|
+ list_del_req(req);
|
|
+ ctr = --req->ctr;
|
|
+ req->ch = NULL;
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
+ if (!ctr) {
|
|
+ destroy_req(req);
|
|
+ }
|
|
}
|
|
|
|
static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
|
|
{
|
|
- struct fuse_req *req;
|
|
+ struct fuse_req *req;
|
|
|
|
- req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
|
|
- if (req == NULL) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
|
|
- } else {
|
|
- req->se = se;
|
|
- req->ctr = 1;
|
|
- list_init_req(req);
|
|
- fuse_mutex_init(&req->lock);
|
|
- }
|
|
+ req = (struct fuse_req *)calloc(1, sizeof(struct fuse_req));
|
|
+ if (req == NULL) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
|
|
+ } else {
|
|
+ req->se = se;
|
|
+ req->ctr = 1;
|
|
+ list_init_req(req);
|
|
+ fuse_mutex_init(&req->lock);
|
|
+ }
|
|
|
|
- return req;
|
|
+ return req;
|
|
}
|
|
|
|
/* Send data. If *ch* is NULL, send via session master fd */
|
|
static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
|
|
- struct iovec *iov, int count)
|
|
+ struct iovec *iov, int count)
|
|
{
|
|
- struct fuse_out_header *out = iov[0].iov_base;
|
|
+ struct fuse_out_header *out = iov[0].iov_base;
|
|
|
|
- out->len = iov_length(iov, count);
|
|
- if (se->debug) {
|
|
- if (out->unique == 0) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
|
|
- out->error, out->len);
|
|
- } else if (out->error) {
|
|
- fuse_log(FUSE_LOG_DEBUG,
|
|
- " unique: %llu, error: %i (%s), outsize: %i\n",
|
|
- (unsigned long long) out->unique, out->error,
|
|
- strerror(-out->error), out->len);
|
|
- } else {
|
|
- fuse_log(FUSE_LOG_DEBUG,
|
|
- " unique: %llu, success, outsize: %i\n",
|
|
- (unsigned long long) out->unique, out->len);
|
|
- }
|
|
- }
|
|
+ out->len = iov_length(iov, count);
|
|
+ if (se->debug) {
|
|
+ if (out->unique == 0) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error,
|
|
+ out->len);
|
|
+ } else if (out->error) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ " unique: %llu, error: %i (%s), outsize: %i\n",
|
|
+ (unsigned long long)out->unique, out->error,
|
|
+ strerror(-out->error), out->len);
|
|
+ } else {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n",
|
|
+ (unsigned long long)out->unique, out->len);
|
|
+ }
|
|
+ }
|
|
|
|
- abort(); /* virtio should have taken it before here */
|
|
- return 0;
|
|
+ abort(); /* virtio should have taken it before here */
|
|
+ return 0;
|
|
}
|
|
|
|
|
|
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
|
|
- int count)
|
|
+ int count)
|
|
{
|
|
- struct fuse_out_header out;
|
|
+ struct fuse_out_header out;
|
|
|
|
- if (error <= -1000 || error > 0) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
|
|
- error = -ERANGE;
|
|
- }
|
|
+ if (error <= -1000 || error > 0) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
|
|
+ error = -ERANGE;
|
|
+ }
|
|
|
|
- out.unique = req->unique;
|
|
- out.error = error;
|
|
+ out.unique = req->unique;
|
|
+ out.error = error;
|
|
|
|
- iov[0].iov_base = &out;
|
|
- iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
+ iov[0].iov_base = &out;
|
|
+ iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
|
- return fuse_send_msg(req->se, req->ch, iov, count);
|
|
+ return fuse_send_msg(req->se, req->ch, iov, count);
|
|
}
|
|
|
|
static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
|
|
- int count)
|
|
+ int count)
|
|
{
|
|
- int res;
|
|
+ int res;
|
|
|
|
- res = fuse_send_reply_iov_nofree(req, error, iov, count);
|
|
- fuse_free_req(req);
|
|
- return res;
|
|
+ res = fuse_send_reply_iov_nofree(req, error, iov, count);
|
|
+ fuse_free_req(req);
|
|
+ return res;
|
|
}
|
|
|
|
static int send_reply(fuse_req_t req, int error, const void *arg,
|
|
- size_t argsize)
|
|
+ size_t argsize)
|
|
{
|
|
- struct iovec iov[2];
|
|
- int count = 1;
|
|
- if (argsize) {
|
|
- iov[1].iov_base = (void *) arg;
|
|
- iov[1].iov_len = argsize;
|
|
- count++;
|
|
- }
|
|
- return send_reply_iov(req, error, iov, count);
|
|
+ struct iovec iov[2];
|
|
+ int count = 1;
|
|
+ if (argsize) {
|
|
+ iov[1].iov_base = (void *)arg;
|
|
+ iov[1].iov_len = argsize;
|
|
+ count++;
|
|
+ }
|
|
+ return send_reply_iov(req, error, iov, count);
|
|
}
|
|
|
|
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
|
|
{
|
|
- int res;
|
|
- struct iovec *padded_iov;
|
|
+ int res;
|
|
+ struct iovec *padded_iov;
|
|
|
|
- padded_iov = malloc((count + 1) * sizeof(struct iovec));
|
|
- if (padded_iov == NULL)
|
|
- return fuse_reply_err(req, ENOMEM);
|
|
+ padded_iov = malloc((count + 1) * sizeof(struct iovec));
|
|
+ if (padded_iov == NULL) {
|
|
+ return fuse_reply_err(req, ENOMEM);
|
|
+ }
|
|
|
|
- memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
|
|
- count++;
|
|
+ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
|
|
+ count++;
|
|
|
|
- res = send_reply_iov(req, 0, padded_iov, count);
|
|
- free(padded_iov);
|
|
+ res = send_reply_iov(req, 0, padded_iov, count);
|
|
+ free(padded_iov);
|
|
|
|
- return res;
|
|
+ return res;
|
|
}
|
|
|
|
|
|
-/* `buf` is allowed to be empty so that the proper size may be
|
|
- allocated by the caller */
|
|
+/*
|
|
+ * 'buf` is allowed to be empty so that the proper size may be
|
|
+ * allocated by the caller
|
|
+ */
|
|
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
|
|
- const char *name, const struct stat *stbuf, off_t off)
|
|
+ const char *name, const struct stat *stbuf, off_t off)
|
|
{
|
|
- (void)req;
|
|
- size_t namelen;
|
|
- size_t entlen;
|
|
- size_t entlen_padded;
|
|
- struct fuse_dirent *dirent;
|
|
+ (void)req;
|
|
+ size_t namelen;
|
|
+ size_t entlen;
|
|
+ size_t entlen_padded;
|
|
+ struct fuse_dirent *dirent;
|
|
|
|
- namelen = strlen(name);
|
|
- entlen = FUSE_NAME_OFFSET + namelen;
|
|
- entlen_padded = FUSE_DIRENT_ALIGN(entlen);
|
|
+ namelen = strlen(name);
|
|
+ entlen = FUSE_NAME_OFFSET + namelen;
|
|
+ entlen_padded = FUSE_DIRENT_ALIGN(entlen);
|
|
|
|
- if ((buf == NULL) || (entlen_padded > bufsize))
|
|
- return entlen_padded;
|
|
+ if ((buf == NULL) || (entlen_padded > bufsize)) {
|
|
+ return entlen_padded;
|
|
+ }
|
|
|
|
- dirent = (struct fuse_dirent*) buf;
|
|
- dirent->ino = stbuf->st_ino;
|
|
- dirent->off = off;
|
|
- dirent->namelen = namelen;
|
|
- dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
|
|
- memcpy(dirent->name, name, namelen);
|
|
- memset(dirent->name + namelen, 0, entlen_padded - entlen);
|
|
+ dirent = (struct fuse_dirent *)buf;
|
|
+ dirent->ino = stbuf->st_ino;
|
|
+ dirent->off = off;
|
|
+ dirent->namelen = namelen;
|
|
+ dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
|
|
+ memcpy(dirent->name, name, namelen);
|
|
+ memset(dirent->name + namelen, 0, entlen_padded - entlen);
|
|
|
|
- return entlen_padded;
|
|
+ return entlen_padded;
|
|
}
|
|
|
|
static void convert_statfs(const struct statvfs *stbuf,
|
|
- struct fuse_kstatfs *kstatfs)
|
|
+ struct fuse_kstatfs *kstatfs)
|
|
{
|
|
- kstatfs->bsize = stbuf->f_bsize;
|
|
- kstatfs->frsize = stbuf->f_frsize;
|
|
- kstatfs->blocks = stbuf->f_blocks;
|
|
- kstatfs->bfree = stbuf->f_bfree;
|
|
- kstatfs->bavail = stbuf->f_bavail;
|
|
- kstatfs->files = stbuf->f_files;
|
|
- kstatfs->ffree = stbuf->f_ffree;
|
|
- kstatfs->namelen = stbuf->f_namemax;
|
|
+ kstatfs->bsize = stbuf->f_bsize;
|
|
+ kstatfs->frsize = stbuf->f_frsize;
|
|
+ kstatfs->blocks = stbuf->f_blocks;
|
|
+ kstatfs->bfree = stbuf->f_bfree;
|
|
+ kstatfs->bavail = stbuf->f_bavail;
|
|
+ kstatfs->files = stbuf->f_files;
|
|
+ kstatfs->ffree = stbuf->f_ffree;
|
|
+ kstatfs->namelen = stbuf->f_namemax;
|
|
}
|
|
|
|
static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
|
|
{
|
|
- return send_reply(req, 0, arg, argsize);
|
|
+ return send_reply(req, 0, arg, argsize);
|
|
}
|
|
|
|
int fuse_reply_err(fuse_req_t req, int err)
|
|
{
|
|
- return send_reply(req, -err, NULL, 0);
|
|
+ return send_reply(req, -err, NULL, 0);
|
|
}
|
|
|
|
void fuse_reply_none(fuse_req_t req)
|
|
{
|
|
- fuse_free_req(req);
|
|
+ fuse_free_req(req);
|
|
}
|
|
|
|
static unsigned long calc_timeout_sec(double t)
|
|
{
|
|
- if (t > (double) ULONG_MAX)
|
|
- return ULONG_MAX;
|
|
- else if (t < 0.0)
|
|
- return 0;
|
|
- else
|
|
- return (unsigned long) t;
|
|
+ if (t > (double)ULONG_MAX) {
|
|
+ return ULONG_MAX;
|
|
+ } else if (t < 0.0) {
|
|
+ return 0;
|
|
+ } else {
|
|
+ return (unsigned long)t;
|
|
+ }
|
|
}
|
|
|
|
static unsigned int calc_timeout_nsec(double t)
|
|
{
|
|
- double f = t - (double) calc_timeout_sec(t);
|
|
- if (f < 0.0)
|
|
- return 0;
|
|
- else if (f >= 0.999999999)
|
|
- return 999999999;
|
|
- else
|
|
- return (unsigned int) (f * 1.0e9);
|
|
+ double f = t - (double)calc_timeout_sec(t);
|
|
+ if (f < 0.0) {
|
|
+ return 0;
|
|
+ } else if (f >= 0.999999999) {
|
|
+ return 999999999;
|
|
+ } else {
|
|
+ return (unsigned int)(f * 1.0e9);
|
|
+ }
|
|
}
|
|
|
|
static void fill_entry(struct fuse_entry_out *arg,
|
|
- const struct fuse_entry_param *e)
|
|
+ const struct fuse_entry_param *e)
|
|
{
|
|
- arg->nodeid = e->ino;
|
|
- arg->generation = e->generation;
|
|
- arg->entry_valid = calc_timeout_sec(e->entry_timeout);
|
|
- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
|
|
- arg->attr_valid = calc_timeout_sec(e->attr_timeout);
|
|
- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
|
|
- convert_stat(&e->attr, &arg->attr);
|
|
+ arg->nodeid = e->ino;
|
|
+ arg->generation = e->generation;
|
|
+ arg->entry_valid = calc_timeout_sec(e->entry_timeout);
|
|
+ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
|
|
+ arg->attr_valid = calc_timeout_sec(e->attr_timeout);
|
|
+ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
|
|
+ convert_stat(&e->attr, &arg->attr);
|
|
}
|
|
|
|
-/* `buf` is allowed to be empty so that the proper size may be
|
|
- allocated by the caller */
|
|
+/*
|
|
+ * `buf` is allowed to be empty so that the proper size may be
|
|
+ * allocated by the caller
|
|
+ */
|
|
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
|
- const char *name,
|
|
- const struct fuse_entry_param *e, off_t off)
|
|
-{
|
|
- (void)req;
|
|
- size_t namelen;
|
|
- size_t entlen;
|
|
- size_t entlen_padded;
|
|
-
|
|
- namelen = strlen(name);
|
|
- entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
|
|
- entlen_padded = FUSE_DIRENT_ALIGN(entlen);
|
|
- if ((buf == NULL) || (entlen_padded > bufsize))
|
|
- return entlen_padded;
|
|
-
|
|
- struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
|
|
- memset(&dp->entry_out, 0, sizeof(dp->entry_out));
|
|
- fill_entry(&dp->entry_out, e);
|
|
-
|
|
- struct fuse_dirent *dirent = &dp->dirent;
|
|
- dirent->ino = e->attr.st_ino;
|
|
- dirent->off = off;
|
|
- dirent->namelen = namelen;
|
|
- dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
|
|
- memcpy(dirent->name, name, namelen);
|
|
- memset(dirent->name + namelen, 0, entlen_padded - entlen);
|
|
-
|
|
- return entlen_padded;
|
|
-}
|
|
-
|
|
-static void fill_open(struct fuse_open_out *arg,
|
|
- const struct fuse_file_info *f)
|
|
-{
|
|
- arg->fh = f->fh;
|
|
- if (f->direct_io)
|
|
- arg->open_flags |= FOPEN_DIRECT_IO;
|
|
- if (f->keep_cache)
|
|
- arg->open_flags |= FOPEN_KEEP_CACHE;
|
|
- if (f->cache_readdir)
|
|
- arg->open_flags |= FOPEN_CACHE_DIR;
|
|
- if (f->nonseekable)
|
|
- arg->open_flags |= FOPEN_NONSEEKABLE;
|
|
+ const char *name,
|
|
+ const struct fuse_entry_param *e, off_t off)
|
|
+{
|
|
+ (void)req;
|
|
+ size_t namelen;
|
|
+ size_t entlen;
|
|
+ size_t entlen_padded;
|
|
+
|
|
+ namelen = strlen(name);
|
|
+ entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
|
|
+ entlen_padded = FUSE_DIRENT_ALIGN(entlen);
|
|
+ if ((buf == NULL) || (entlen_padded > bufsize)) {
|
|
+ return entlen_padded;
|
|
+ }
|
|
+
|
|
+ struct fuse_direntplus *dp = (struct fuse_direntplus *)buf;
|
|
+ memset(&dp->entry_out, 0, sizeof(dp->entry_out));
|
|
+ fill_entry(&dp->entry_out, e);
|
|
+
|
|
+ struct fuse_dirent *dirent = &dp->dirent;
|
|
+ dirent->ino = e->attr.st_ino;
|
|
+ dirent->off = off;
|
|
+ dirent->namelen = namelen;
|
|
+ dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
|
|
+ memcpy(dirent->name, name, namelen);
|
|
+ memset(dirent->name + namelen, 0, entlen_padded - entlen);
|
|
+
|
|
+ return entlen_padded;
|
|
+}
|
|
+
|
|
+static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f)
|
|
+{
|
|
+ arg->fh = f->fh;
|
|
+ if (f->direct_io) {
|
|
+ arg->open_flags |= FOPEN_DIRECT_IO;
|
|
+ }
|
|
+ if (f->keep_cache) {
|
|
+ arg->open_flags |= FOPEN_KEEP_CACHE;
|
|
+ }
|
|
+ if (f->cache_readdir) {
|
|
+ arg->open_flags |= FOPEN_CACHE_DIR;
|
|
+ }
|
|
+ if (f->nonseekable) {
|
|
+ arg->open_flags |= FOPEN_NONSEEKABLE;
|
|
+ }
|
|
}
|
|
|
|
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
|
|
{
|
|
- struct fuse_entry_out arg;
|
|
- size_t size = req->se->conn.proto_minor < 9 ?
|
|
- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
|
|
+ struct fuse_entry_out arg;
|
|
+ size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE :
|
|
+ sizeof(arg);
|
|
|
|
- /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
|
|
- negative entry */
|
|
- if (!e->ino && req->se->conn.proto_minor < 4)
|
|
- return fuse_reply_err(req, ENOENT);
|
|
+ /*
|
|
+ * before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
|
|
+ * negative entry
|
|
+ */
|
|
+ if (!e->ino && req->se->conn.proto_minor < 4) {
|
|
+ return fuse_reply_err(req, ENOENT);
|
|
+ }
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- fill_entry(&arg, e);
|
|
- return send_reply_ok(req, &arg, size);
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ fill_entry(&arg, e);
|
|
+ return send_reply_ok(req, &arg, size);
|
|
}
|
|
|
|
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
|
- const struct fuse_file_info *f)
|
|
+ const struct fuse_file_info *f)
|
|
{
|
|
- char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
|
|
- size_t entrysize = req->se->conn.proto_minor < 9 ?
|
|
- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
|
|
- struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
|
|
- struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
|
|
+ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
|
|
+ size_t entrysize = req->se->conn.proto_minor < 9 ?
|
|
+ FUSE_COMPAT_ENTRY_OUT_SIZE :
|
|
+ sizeof(struct fuse_entry_out);
|
|
+ struct fuse_entry_out *earg = (struct fuse_entry_out *)buf;
|
|
+ struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize);
|
|
|
|
- memset(buf, 0, sizeof(buf));
|
|
- fill_entry(earg, e);
|
|
- fill_open(oarg, f);
|
|
- return send_reply_ok(req, buf,
|
|
- entrysize + sizeof(struct fuse_open_out));
|
|
+ memset(buf, 0, sizeof(buf));
|
|
+ fill_entry(earg, e);
|
|
+ fill_open(oarg, f);
|
|
+ return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out));
|
|
}
|
|
|
|
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
|
- double attr_timeout)
|
|
+ double attr_timeout)
|
|
{
|
|
- struct fuse_attr_out arg;
|
|
- size_t size = req->se->conn.proto_minor < 9 ?
|
|
- FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
|
|
+ struct fuse_attr_out arg;
|
|
+ size_t size =
|
|
+ req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.attr_valid = calc_timeout_sec(attr_timeout);
|
|
- arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
|
|
- convert_stat(attr, &arg.attr);
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.attr_valid = calc_timeout_sec(attr_timeout);
|
|
+ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
|
|
+ convert_stat(attr, &arg.attr);
|
|
|
|
- return send_reply_ok(req, &arg, size);
|
|
+ return send_reply_ok(req, &arg, size);
|
|
}
|
|
|
|
int fuse_reply_readlink(fuse_req_t req, const char *linkname)
|
|
{
|
|
- return send_reply_ok(req, linkname, strlen(linkname));
|
|
+ return send_reply_ok(req, linkname, strlen(linkname));
|
|
}
|
|
|
|
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
|
|
{
|
|
- struct fuse_open_out arg;
|
|
+ struct fuse_open_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- fill_open(&arg, f);
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ fill_open(&arg, f);
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
int fuse_reply_write(fuse_req_t req, size_t count)
|
|
{
|
|
- struct fuse_write_out arg;
|
|
+ struct fuse_write_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.size = count;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.size = count;
|
|
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
|
|
{
|
|
- return send_reply_ok(req, buf, size);
|
|
+ return send_reply_ok(req, buf, size);
|
|
}
|
|
|
|
static int fuse_send_data_iov_fallback(struct fuse_session *se,
|
|
- struct fuse_chan *ch,
|
|
- struct iovec *iov, int iov_count,
|
|
- struct fuse_bufvec *buf,
|
|
- size_t len)
|
|
+ struct fuse_chan *ch, struct iovec *iov,
|
|
+ int iov_count, struct fuse_bufvec *buf,
|
|
+ size_t len)
|
|
{
|
|
- /* Optimize common case */
|
|
- if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
|
|
- !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
|
|
- /* FIXME: also avoid memory copy if there are multiple buffers
|
|
- but none of them contain an fd */
|
|
+ /* Optimize common case */
|
|
+ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
|
|
+ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
|
|
+ /*
|
|
+ * FIXME: also avoid memory copy if there are multiple buffers
|
|
+ * but none of them contain an fd
|
|
+ */
|
|
|
|
- iov[iov_count].iov_base = buf->buf[0].mem;
|
|
- iov[iov_count].iov_len = len;
|
|
- iov_count++;
|
|
- return fuse_send_msg(se, ch, iov, iov_count);
|
|
- }
|
|
+ iov[iov_count].iov_base = buf->buf[0].mem;
|
|
+ iov[iov_count].iov_len = len;
|
|
+ iov_count++;
|
|
+ return fuse_send_msg(se, ch, iov, iov_count);
|
|
+ }
|
|
|
|
- abort(); /* Will have taken vhost path */
|
|
- return 0;
|
|
+ abort(); /* Will have taken vhost path */
|
|
+ return 0;
|
|
}
|
|
|
|
static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
|
|
- struct iovec *iov, int iov_count,
|
|
- struct fuse_bufvec *buf, unsigned int flags)
|
|
+ struct iovec *iov, int iov_count,
|
|
+ struct fuse_bufvec *buf, unsigned int flags)
|
|
{
|
|
- size_t len = fuse_buf_size(buf);
|
|
- (void) flags;
|
|
+ size_t len = fuse_buf_size(buf);
|
|
+ (void)flags;
|
|
|
|
- return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
|
|
+ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
|
|
}
|
|
|
|
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
|
|
- enum fuse_buf_copy_flags flags)
|
|
+ enum fuse_buf_copy_flags flags)
|
|
{
|
|
- struct iovec iov[2];
|
|
- struct fuse_out_header out;
|
|
- int res;
|
|
+ struct iovec iov[2];
|
|
+ struct fuse_out_header out;
|
|
+ int res;
|
|
|
|
- iov[0].iov_base = &out;
|
|
- iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
+ iov[0].iov_base = &out;
|
|
+ iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
|
- out.unique = req->unique;
|
|
- out.error = 0;
|
|
+ out.unique = req->unique;
|
|
+ out.error = 0;
|
|
|
|
- res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
|
|
- if (res <= 0) {
|
|
- fuse_free_req(req);
|
|
- return res;
|
|
- } else {
|
|
- return fuse_reply_err(req, res);
|
|
- }
|
|
+ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
|
|
+ if (res <= 0) {
|
|
+ fuse_free_req(req);
|
|
+ return res;
|
|
+ } else {
|
|
+ return fuse_reply_err(req, res);
|
|
+ }
|
|
}
|
|
|
|
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
|
|
{
|
|
- struct fuse_statfs_out arg;
|
|
- size_t size = req->se->conn.proto_minor < 4 ?
|
|
- FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
|
|
+ struct fuse_statfs_out arg;
|
|
+ size_t size =
|
|
+ req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- convert_statfs(stbuf, &arg.st);
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ convert_statfs(stbuf, &arg.st);
|
|
|
|
- return send_reply_ok(req, &arg, size);
|
|
+ return send_reply_ok(req, &arg, size);
|
|
}
|
|
|
|
int fuse_reply_xattr(fuse_req_t req, size_t count)
|
|
{
|
|
- struct fuse_getxattr_out arg;
|
|
+ struct fuse_getxattr_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.size = count;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.size = count;
|
|
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
|
|
{
|
|
- struct fuse_lk_out arg;
|
|
+ struct fuse_lk_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.lk.type = lock->l_type;
|
|
- if (lock->l_type != F_UNLCK) {
|
|
- arg.lk.start = lock->l_start;
|
|
- if (lock->l_len == 0)
|
|
- arg.lk.end = OFFSET_MAX;
|
|
- else
|
|
- arg.lk.end = lock->l_start + lock->l_len - 1;
|
|
- }
|
|
- arg.lk.pid = lock->l_pid;
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.lk.type = lock->l_type;
|
|
+ if (lock->l_type != F_UNLCK) {
|
|
+ arg.lk.start = lock->l_start;
|
|
+ if (lock->l_len == 0) {
|
|
+ arg.lk.end = OFFSET_MAX;
|
|
+ } else {
|
|
+ arg.lk.end = lock->l_start + lock->l_len - 1;
|
|
+ }
|
|
+ }
|
|
+ arg.lk.pid = lock->l_pid;
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
|
|
{
|
|
- struct fuse_bmap_out arg;
|
|
+ struct fuse_bmap_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.block = idx;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.block = idx;
|
|
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
|
|
- size_t count)
|
|
-{
|
|
- struct fuse_ioctl_iovec *fiov;
|
|
- size_t i;
|
|
-
|
|
- fiov = malloc(sizeof(fiov[0]) * count);
|
|
- if (!fiov)
|
|
- return NULL;
|
|
-
|
|
- for (i = 0; i < count; i++) {
|
|
- fiov[i].base = (uintptr_t) iov[i].iov_base;
|
|
- fiov[i].len = iov[i].iov_len;
|
|
- }
|
|
-
|
|
- return fiov;
|
|
-}
|
|
-
|
|
-int fuse_reply_ioctl_retry(fuse_req_t req,
|
|
- const struct iovec *in_iov, size_t in_count,
|
|
- const struct iovec *out_iov, size_t out_count)
|
|
-{
|
|
- struct fuse_ioctl_out arg;
|
|
- struct fuse_ioctl_iovec *in_fiov = NULL;
|
|
- struct fuse_ioctl_iovec *out_fiov = NULL;
|
|
- struct iovec iov[4];
|
|
- size_t count = 1;
|
|
- int res;
|
|
-
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.flags |= FUSE_IOCTL_RETRY;
|
|
- arg.in_iovs = in_count;
|
|
- arg.out_iovs = out_count;
|
|
- iov[count].iov_base = &arg;
|
|
- iov[count].iov_len = sizeof(arg);
|
|
- count++;
|
|
-
|
|
- if (req->se->conn.proto_minor < 16) {
|
|
- if (in_count) {
|
|
- iov[count].iov_base = (void *)in_iov;
|
|
- iov[count].iov_len = sizeof(in_iov[0]) * in_count;
|
|
- count++;
|
|
- }
|
|
-
|
|
- if (out_count) {
|
|
- iov[count].iov_base = (void *)out_iov;
|
|
- iov[count].iov_len = sizeof(out_iov[0]) * out_count;
|
|
- count++;
|
|
- }
|
|
- } else {
|
|
- /* Can't handle non-compat 64bit ioctls on 32bit */
|
|
- if (sizeof(void *) == 4 && req->ioctl_64bit) {
|
|
- res = fuse_reply_err(req, EINVAL);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if (in_count) {
|
|
- in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
|
|
- if (!in_fiov)
|
|
- goto enomem;
|
|
-
|
|
- iov[count].iov_base = (void *)in_fiov;
|
|
- iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
|
|
- count++;
|
|
- }
|
|
- if (out_count) {
|
|
- out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
|
|
- if (!out_fiov)
|
|
- goto enomem;
|
|
-
|
|
- iov[count].iov_base = (void *)out_fiov;
|
|
- iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
|
|
- count++;
|
|
- }
|
|
- }
|
|
-
|
|
- res = send_reply_iov(req, 0, iov, count);
|
|
+ size_t count)
|
|
+{
|
|
+ struct fuse_ioctl_iovec *fiov;
|
|
+ size_t i;
|
|
+
|
|
+ fiov = malloc(sizeof(fiov[0]) * count);
|
|
+ if (!fiov) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ fiov[i].base = (uintptr_t)iov[i].iov_base;
|
|
+ fiov[i].len = iov[i].iov_len;
|
|
+ }
|
|
+
|
|
+ return fiov;
|
|
+}
|
|
+
|
|
+int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov,
|
|
+ size_t in_count, const struct iovec *out_iov,
|
|
+ size_t out_count)
|
|
+{
|
|
+ struct fuse_ioctl_out arg;
|
|
+ struct fuse_ioctl_iovec *in_fiov = NULL;
|
|
+ struct fuse_ioctl_iovec *out_fiov = NULL;
|
|
+ struct iovec iov[4];
|
|
+ size_t count = 1;
|
|
+ int res;
|
|
+
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.flags |= FUSE_IOCTL_RETRY;
|
|
+ arg.in_iovs = in_count;
|
|
+ arg.out_iovs = out_count;
|
|
+ iov[count].iov_base = &arg;
|
|
+ iov[count].iov_len = sizeof(arg);
|
|
+ count++;
|
|
+
|
|
+ if (req->se->conn.proto_minor < 16) {
|
|
+ if (in_count) {
|
|
+ iov[count].iov_base = (void *)in_iov;
|
|
+ iov[count].iov_len = sizeof(in_iov[0]) * in_count;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ if (out_count) {
|
|
+ iov[count].iov_base = (void *)out_iov;
|
|
+ iov[count].iov_len = sizeof(out_iov[0]) * out_count;
|
|
+ count++;
|
|
+ }
|
|
+ } else {
|
|
+ /* Can't handle non-compat 64bit ioctls on 32bit */
|
|
+ if (sizeof(void *) == 4 && req->ioctl_64bit) {
|
|
+ res = fuse_reply_err(req, EINVAL);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (in_count) {
|
|
+ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
|
|
+ if (!in_fiov) {
|
|
+ goto enomem;
|
|
+ }
|
|
+
|
|
+ iov[count].iov_base = (void *)in_fiov;
|
|
+ iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
|
|
+ count++;
|
|
+ }
|
|
+ if (out_count) {
|
|
+ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
|
|
+ if (!out_fiov) {
|
|
+ goto enomem;
|
|
+ }
|
|
+
|
|
+ iov[count].iov_base = (void *)out_fiov;
|
|
+ iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ res = send_reply_iov(req, 0, iov, count);
|
|
out:
|
|
- free(in_fiov);
|
|
- free(out_fiov);
|
|
+ free(in_fiov);
|
|
+ free(out_fiov);
|
|
|
|
- return res;
|
|
+ return res;
|
|
|
|
enomem:
|
|
- res = fuse_reply_err(req, ENOMEM);
|
|
- goto out;
|
|
+ res = fuse_reply_err(req, ENOMEM);
|
|
+ goto out;
|
|
}
|
|
|
|
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
|
|
{
|
|
- struct fuse_ioctl_out arg;
|
|
- struct iovec iov[3];
|
|
- size_t count = 1;
|
|
+ struct fuse_ioctl_out arg;
|
|
+ struct iovec iov[3];
|
|
+ size_t count = 1;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.result = result;
|
|
- iov[count].iov_base = &arg;
|
|
- iov[count].iov_len = sizeof(arg);
|
|
- count++;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.result = result;
|
|
+ iov[count].iov_base = &arg;
|
|
+ iov[count].iov_len = sizeof(arg);
|
|
+ count++;
|
|
|
|
- if (size) {
|
|
- iov[count].iov_base = (char *) buf;
|
|
- iov[count].iov_len = size;
|
|
- count++;
|
|
- }
|
|
+ if (size) {
|
|
+ iov[count].iov_base = (char *)buf;
|
|
+ iov[count].iov_len = size;
|
|
+ count++;
|
|
+ }
|
|
|
|
- return send_reply_iov(req, 0, iov, count);
|
|
+ return send_reply_iov(req, 0, iov, count);
|
|
}
|
|
|
|
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
|
|
- int count)
|
|
+ int count)
|
|
{
|
|
- struct iovec *padded_iov;
|
|
- struct fuse_ioctl_out arg;
|
|
- int res;
|
|
+ struct iovec *padded_iov;
|
|
+ struct fuse_ioctl_out arg;
|
|
+ int res;
|
|
|
|
- padded_iov = malloc((count + 2) * sizeof(struct iovec));
|
|
- if (padded_iov == NULL)
|
|
- return fuse_reply_err(req, ENOMEM);
|
|
+ padded_iov = malloc((count + 2) * sizeof(struct iovec));
|
|
+ if (padded_iov == NULL) {
|
|
+ return fuse_reply_err(req, ENOMEM);
|
|
+ }
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.result = result;
|
|
- padded_iov[1].iov_base = &arg;
|
|
- padded_iov[1].iov_len = sizeof(arg);
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.result = result;
|
|
+ padded_iov[1].iov_base = &arg;
|
|
+ padded_iov[1].iov_len = sizeof(arg);
|
|
|
|
- memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
|
|
+ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
|
|
|
|
- res = send_reply_iov(req, 0, padded_iov, count + 2);
|
|
- free(padded_iov);
|
|
+ res = send_reply_iov(req, 0, padded_iov, count + 2);
|
|
+ free(padded_iov);
|
|
|
|
- return res;
|
|
+ return res;
|
|
}
|
|
|
|
int fuse_reply_poll(fuse_req_t req, unsigned revents)
|
|
{
|
|
- struct fuse_poll_out arg;
|
|
+ struct fuse_poll_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.revents = revents;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.revents = revents;
|
|
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
int fuse_reply_lseek(fuse_req_t req, off_t off)
|
|
{
|
|
- struct fuse_lseek_out arg;
|
|
+ struct fuse_lseek_out arg;
|
|
|
|
- memset(&arg, 0, sizeof(arg));
|
|
- arg.offset = off;
|
|
+ memset(&arg, 0, sizeof(arg));
|
|
+ arg.offset = off;
|
|
|
|
- return send_reply_ok(req, &arg, sizeof(arg));
|
|
+ return send_reply_ok(req, &arg, sizeof(arg));
|
|
}
|
|
|
|
static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- char *name = (char *) inarg;
|
|
+ char *name = (char *)inarg;
|
|
|
|
- if (req->se->op.lookup)
|
|
- req->se->op.lookup(req, nodeid, name);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.lookup) {
|
|
+ req->se->op.lookup(req, nodeid, name);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
|
|
+ struct fuse_forget_in *arg = (struct fuse_forget_in *)inarg;
|
|
|
|
- if (req->se->op.forget)
|
|
- req->se->op.forget(req, nodeid, arg->nlookup);
|
|
- else
|
|
- fuse_reply_none(req);
|
|
+ if (req->se->op.forget) {
|
|
+ req->se->op.forget(req, nodeid, arg->nlookup);
|
|
+ } else {
|
|
+ fuse_reply_none(req);
|
|
+ }
|
|
}
|
|
|
|
static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
|
|
- const void *inarg)
|
|
+ const void *inarg)
|
|
{
|
|
- struct fuse_batch_forget_in *arg = (void *) inarg;
|
|
- struct fuse_forget_one *param = (void *) PARAM(arg);
|
|
- unsigned int i;
|
|
+ struct fuse_batch_forget_in *arg = (void *)inarg;
|
|
+ struct fuse_forget_one *param = (void *)PARAM(arg);
|
|
+ unsigned int i;
|
|
|
|
- (void) nodeid;
|
|
+ (void)nodeid;
|
|
|
|
- if (req->se->op.forget_multi) {
|
|
- req->se->op.forget_multi(req, arg->count,
|
|
- (struct fuse_forget_data *) param);
|
|
- } else if (req->se->op.forget) {
|
|
- for (i = 0; i < arg->count; i++) {
|
|
- struct fuse_forget_one *forget = ¶m[i];
|
|
- struct fuse_req *dummy_req;
|
|
+ if (req->se->op.forget_multi) {
|
|
+ req->se->op.forget_multi(req, arg->count,
|
|
+ (struct fuse_forget_data *)param);
|
|
+ } else if (req->se->op.forget) {
|
|
+ for (i = 0; i < arg->count; i++) {
|
|
+ struct fuse_forget_one *forget = ¶m[i];
|
|
+ struct fuse_req *dummy_req;
|
|
|
|
- dummy_req = fuse_ll_alloc_req(req->se);
|
|
- if (dummy_req == NULL)
|
|
- break;
|
|
+ dummy_req = fuse_ll_alloc_req(req->se);
|
|
+ if (dummy_req == NULL) {
|
|
+ break;
|
|
+ }
|
|
|
|
- dummy_req->unique = req->unique;
|
|
- dummy_req->ctx = req->ctx;
|
|
- dummy_req->ch = NULL;
|
|
+ dummy_req->unique = req->unique;
|
|
+ dummy_req->ctx = req->ctx;
|
|
+ dummy_req->ch = NULL;
|
|
|
|
- req->se->op.forget(dummy_req, forget->nodeid,
|
|
- forget->nlookup);
|
|
- }
|
|
- fuse_reply_none(req);
|
|
- } else {
|
|
- fuse_reply_none(req);
|
|
- }
|
|
+ req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup);
|
|
+ }
|
|
+ fuse_reply_none(req);
|
|
+ } else {
|
|
+ fuse_reply_none(req);
|
|
+ }
|
|
}
|
|
|
|
static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_file_info *fip = NULL;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_file_info *fip = NULL;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- if (req->se->conn.proto_minor >= 9) {
|
|
- struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
|
|
+ if (req->se->conn.proto_minor >= 9) {
|
|
+ struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg;
|
|
|
|
- if (arg->getattr_flags & FUSE_GETATTR_FH) {
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fip = &fi;
|
|
- }
|
|
- }
|
|
+ if (arg->getattr_flags & FUSE_GETATTR_FH) {
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fip = &fi;
|
|
+ }
|
|
+ }
|
|
|
|
- if (req->se->op.getattr)
|
|
- req->se->op.getattr(req, nodeid, fip);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.getattr) {
|
|
+ req->se->op.getattr(req, nodeid, fip);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
|
|
-
|
|
- if (req->se->op.setattr) {
|
|
- struct fuse_file_info *fi = NULL;
|
|
- struct fuse_file_info fi_store;
|
|
- struct stat stbuf;
|
|
- memset(&stbuf, 0, sizeof(stbuf));
|
|
- convert_attr(arg, &stbuf);
|
|
- if (arg->valid & FATTR_FH) {
|
|
- arg->valid &= ~FATTR_FH;
|
|
- memset(&fi_store, 0, sizeof(fi_store));
|
|
- fi = &fi_store;
|
|
- fi->fh = arg->fh;
|
|
- }
|
|
- arg->valid &=
|
|
- FUSE_SET_ATTR_MODE |
|
|
- FUSE_SET_ATTR_UID |
|
|
- FUSE_SET_ATTR_GID |
|
|
- FUSE_SET_ATTR_SIZE |
|
|
- FUSE_SET_ATTR_ATIME |
|
|
- FUSE_SET_ATTR_MTIME |
|
|
- FUSE_SET_ATTR_ATIME_NOW |
|
|
- FUSE_SET_ATTR_MTIME_NOW |
|
|
- FUSE_SET_ATTR_CTIME;
|
|
-
|
|
- req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
|
|
- } else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inarg;
|
|
+
|
|
+ if (req->se->op.setattr) {
|
|
+ struct fuse_file_info *fi = NULL;
|
|
+ struct fuse_file_info fi_store;
|
|
+ struct stat stbuf;
|
|
+ memset(&stbuf, 0, sizeof(stbuf));
|
|
+ convert_attr(arg, &stbuf);
|
|
+ if (arg->valid & FATTR_FH) {
|
|
+ arg->valid &= ~FATTR_FH;
|
|
+ memset(&fi_store, 0, sizeof(fi_store));
|
|
+ fi = &fi_store;
|
|
+ fi->fh = arg->fh;
|
|
+ }
|
|
+ arg->valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID |
|
|
+ FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE |
|
|
+ FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME |
|
|
+ FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW |
|
|
+ FUSE_SET_ATTR_CTIME;
|
|
+
|
|
+ req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
|
|
+ struct fuse_access_in *arg = (struct fuse_access_in *)inarg;
|
|
|
|
- if (req->se->op.access)
|
|
- req->se->op.access(req, nodeid, arg->mask);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.access) {
|
|
+ req->se->op.access(req, nodeid, arg->mask);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- (void) inarg;
|
|
+ (void)inarg;
|
|
|
|
- if (req->se->op.readlink)
|
|
- req->se->op.readlink(req, nodeid);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.readlink) {
|
|
+ req->se->op.readlink(req, nodeid);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
|
|
- char *name = PARAM(arg);
|
|
+ struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg;
|
|
+ char *name = PARAM(arg);
|
|
|
|
- if (req->se->conn.proto_minor >= 12)
|
|
- req->ctx.umask = arg->umask;
|
|
- else
|
|
- name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
|
|
+ if (req->se->conn.proto_minor >= 12) {
|
|
+ req->ctx.umask = arg->umask;
|
|
+ } else {
|
|
+ name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
|
|
+ }
|
|
|
|
- if (req->se->op.mknod)
|
|
- req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.mknod) {
|
|
+ req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
|
|
+ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg;
|
|
|
|
- if (req->se->conn.proto_minor >= 12)
|
|
- req->ctx.umask = arg->umask;
|
|
+ if (req->se->conn.proto_minor >= 12) {
|
|
+ req->ctx.umask = arg->umask;
|
|
+ }
|
|
|
|
- if (req->se->op.mkdir)
|
|
- req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.mkdir) {
|
|
+ req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- char *name = (char *) inarg;
|
|
+ char *name = (char *)inarg;
|
|
|
|
- if (req->se->op.unlink)
|
|
- req->se->op.unlink(req, nodeid, name);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.unlink) {
|
|
+ req->se->op.unlink(req, nodeid, name);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- char *name = (char *) inarg;
|
|
+ char *name = (char *)inarg;
|
|
|
|
- if (req->se->op.rmdir)
|
|
- req->se->op.rmdir(req, nodeid, name);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.rmdir) {
|
|
+ req->se->op.rmdir(req, nodeid, name);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- char *name = (char *) inarg;
|
|
- char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
|
|
+ char *name = (char *)inarg;
|
|
+ char *linkname = ((char *)inarg) + strlen((char *)inarg) + 1;
|
|
|
|
- if (req->se->op.symlink)
|
|
- req->se->op.symlink(req, linkname, nodeid, name);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.symlink) {
|
|
+ req->se->op.symlink(req, linkname, nodeid, name);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
|
|
- char *oldname = PARAM(arg);
|
|
- char *newname = oldname + strlen(oldname) + 1;
|
|
+ struct fuse_rename_in *arg = (struct fuse_rename_in *)inarg;
|
|
+ char *oldname = PARAM(arg);
|
|
+ char *newname = oldname + strlen(oldname) + 1;
|
|
|
|
- if (req->se->op.rename)
|
|
- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
|
|
- 0);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.rename) {
|
|
+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
|
|
- char *oldname = PARAM(arg);
|
|
- char *newname = oldname + strlen(oldname) + 1;
|
|
+ struct fuse_rename2_in *arg = (struct fuse_rename2_in *)inarg;
|
|
+ char *oldname = PARAM(arg);
|
|
+ char *newname = oldname + strlen(oldname) + 1;
|
|
|
|
- if (req->se->op.rename)
|
|
- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
|
|
- arg->flags);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.rename) {
|
|
+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
|
|
+ arg->flags);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
|
|
+ struct fuse_link_in *arg = (struct fuse_link_in *)inarg;
|
|
|
|
- if (req->se->op.link)
|
|
- req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.link) {
|
|
+ req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
|
|
+ struct fuse_create_in *arg = (struct fuse_create_in *)inarg;
|
|
|
|
- if (req->se->op.create) {
|
|
- struct fuse_file_info fi;
|
|
- char *name = PARAM(arg);
|
|
+ if (req->se->op.create) {
|
|
+ struct fuse_file_info fi;
|
|
+ char *name = PARAM(arg);
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.flags = arg->flags;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.flags = arg->flags;
|
|
|
|
- if (req->se->conn.proto_minor >= 12)
|
|
- req->ctx.umask = arg->umask;
|
|
- else
|
|
- name = (char *) inarg + sizeof(struct fuse_open_in);
|
|
+ if (req->se->conn.proto_minor >= 12) {
|
|
+ req->ctx.umask = arg->umask;
|
|
+ } else {
|
|
+ name = (char *)inarg + sizeof(struct fuse_open_in);
|
|
+ }
|
|
|
|
- req->se->op.create(req, nodeid, name, arg->mode, &fi);
|
|
- } else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ req->se->op.create(req, nodeid, name, arg->mode, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_open_in *arg = (struct fuse_open_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.flags = arg->flags;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.flags = arg->flags;
|
|
|
|
- if (req->se->op.open)
|
|
- req->se->op.open(req, nodeid, &fi);
|
|
- else
|
|
- fuse_reply_open(req, &fi);
|
|
+ if (req->se->op.open) {
|
|
+ req->se->op.open(req, nodeid, &fi);
|
|
+ } else {
|
|
+ fuse_reply_open(req, &fi);
|
|
+ }
|
|
}
|
|
|
|
static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
|
|
+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg;
|
|
|
|
- if (req->se->op.read) {
|
|
- struct fuse_file_info fi;
|
|
+ if (req->se->op.read) {
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- if (req->se->conn.proto_minor >= 9) {
|
|
- fi.lock_owner = arg->lock_owner;
|
|
- fi.flags = arg->flags;
|
|
- }
|
|
- req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
|
|
- } else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ if (req->se->conn.proto_minor >= 9) {
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ fi.flags = arg->flags;
|
|
+ }
|
|
+ req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
- char *param;
|
|
+ struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+ char *param;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
|
|
|
|
- if (req->se->conn.proto_minor < 9) {
|
|
- param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
|
|
- } else {
|
|
- fi.lock_owner = arg->lock_owner;
|
|
- fi.flags = arg->flags;
|
|
- param = PARAM(arg);
|
|
- }
|
|
+ if (req->se->conn.proto_minor < 9) {
|
|
+ param = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
|
|
+ } else {
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ fi.flags = arg->flags;
|
|
+ param = PARAM(arg);
|
|
+ }
|
|
|
|
- if (req->se->op.write)
|
|
- req->se->op.write(req, nodeid, param, arg->size,
|
|
- arg->offset, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.write) {
|
|
+ req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
|
|
- const struct fuse_buf *ibuf)
|
|
-{
|
|
- struct fuse_session *se = req->se;
|
|
- struct fuse_bufvec bufv = {
|
|
- .buf[0] = *ibuf,
|
|
- .count = 1,
|
|
- };
|
|
- struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
-
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
|
|
-
|
|
- if (se->conn.proto_minor < 9) {
|
|
- bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
|
|
- bufv.buf[0].size -= sizeof(struct fuse_in_header) +
|
|
- FUSE_COMPAT_WRITE_IN_SIZE;
|
|
- assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
|
|
- } else {
|
|
- fi.lock_owner = arg->lock_owner;
|
|
- fi.flags = arg->flags;
|
|
- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
|
|
- bufv.buf[0].mem = PARAM(arg);
|
|
-
|
|
- bufv.buf[0].size -= sizeof(struct fuse_in_header) +
|
|
- sizeof(struct fuse_write_in);
|
|
- }
|
|
- if (bufv.buf[0].size < arg->size) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
|
|
- fuse_reply_err(req, EIO);
|
|
- return;
|
|
- }
|
|
- bufv.buf[0].size = arg->size;
|
|
-
|
|
- se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
|
|
+ const struct fuse_buf *ibuf)
|
|
+{
|
|
+ struct fuse_session *se = req->se;
|
|
+ struct fuse_bufvec bufv = {
|
|
+ .buf[0] = *ibuf,
|
|
+ .count = 1,
|
|
+ };
|
|
+ struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
|
|
+
|
|
+ if (se->conn.proto_minor < 9) {
|
|
+ bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
|
|
+ bufv.buf[0].size -=
|
|
+ sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE;
|
|
+ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
|
|
+ } else {
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ fi.flags = arg->flags;
|
|
+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) {
|
|
+ bufv.buf[0].mem = PARAM(arg);
|
|
+ }
|
|
+
|
|
+ bufv.buf[0].size -=
|
|
+ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
|
|
+ }
|
|
+ if (bufv.buf[0].size < arg->size) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
|
|
+ fuse_reply_err(req, EIO);
|
|
+ return;
|
|
+ }
|
|
+ bufv.buf[0].size = arg->size;
|
|
+
|
|
+ se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
|
|
}
|
|
|
|
static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_flush_in *arg = (struct fuse_flush_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.flush = 1;
|
|
- if (req->se->conn.proto_minor >= 7)
|
|
- fi.lock_owner = arg->lock_owner;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.flush = 1;
|
|
+ if (req->se->conn.proto_minor >= 7) {
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ }
|
|
|
|
- if (req->se->op.flush)
|
|
- req->se->op.flush(req, nodeid, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.flush) {
|
|
+ req->se->op.flush(req, nodeid, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_release_in *arg = (struct fuse_release_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.flags = arg->flags;
|
|
- fi.fh = arg->fh;
|
|
- if (req->se->conn.proto_minor >= 8) {
|
|
- fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
|
|
- fi.lock_owner = arg->lock_owner;
|
|
- }
|
|
- if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
|
|
- fi.flock_release = 1;
|
|
- fi.lock_owner = arg->lock_owner;
|
|
- }
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.flags = arg->flags;
|
|
+ fi.fh = arg->fh;
|
|
+ if (req->se->conn.proto_minor >= 8) {
|
|
+ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ }
|
|
+ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
|
|
+ fi.flock_release = 1;
|
|
+ fi.lock_owner = arg->lock_owner;
|
|
+ }
|
|
|
|
- if (req->se->op.release)
|
|
- req->se->op.release(req, nodeid, &fi);
|
|
- else
|
|
- fuse_reply_err(req, 0);
|
|
+ if (req->se->op.release) {
|
|
+ req->se->op.release(req, nodeid, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, 0);
|
|
+ }
|
|
}
|
|
|
|
static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
- int datasync = arg->fsync_flags & 1;
|
|
+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+ int datasync = arg->fsync_flags & 1;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.fsync)
|
|
- req->se->op.fsync(req, nodeid, datasync, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.fsync) {
|
|
+ req->se->op.fsync(req, nodeid, datasync, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_open_in *arg = (struct fuse_open_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.flags = arg->flags;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.flags = arg->flags;
|
|
|
|
- if (req->se->op.opendir)
|
|
- req->se->op.opendir(req, nodeid, &fi);
|
|
- else
|
|
- fuse_reply_open(req, &fi);
|
|
+ if (req->se->op.opendir) {
|
|
+ req->se->op.opendir(req, nodeid, &fi);
|
|
+ } else {
|
|
+ fuse_reply_open(req, &fi);
|
|
+ }
|
|
}
|
|
|
|
static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.readdir)
|
|
- req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.readdir) {
|
|
+ req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.readdirplus)
|
|
- req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.readdirplus) {
|
|
+ req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_release_in *arg = (struct fuse_release_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.flags = arg->flags;
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.flags = arg->flags;
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.releasedir)
|
|
- req->se->op.releasedir(req, nodeid, &fi);
|
|
- else
|
|
- fuse_reply_err(req, 0);
|
|
+ if (req->se->op.releasedir) {
|
|
+ req->se->op.releasedir(req, nodeid, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, 0);
|
|
+ }
|
|
}
|
|
|
|
static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
- int datasync = arg->fsync_flags & 1;
|
|
+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+ int datasync = arg->fsync_flags & 1;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.fsyncdir)
|
|
- req->se->op.fsyncdir(req, nodeid, datasync, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.fsyncdir) {
|
|
+ req->se->op.fsyncdir(req, nodeid, datasync, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- (void) nodeid;
|
|
- (void) inarg;
|
|
+ (void)nodeid;
|
|
+ (void)inarg;
|
|
|
|
- if (req->se->op.statfs)
|
|
- req->se->op.statfs(req, nodeid);
|
|
- else {
|
|
- struct statvfs buf = {
|
|
- .f_namemax = 255,
|
|
- .f_bsize = 512,
|
|
- };
|
|
- fuse_reply_statfs(req, &buf);
|
|
- }
|
|
+ if (req->se->op.statfs) {
|
|
+ req->se->op.statfs(req, nodeid);
|
|
+ } else {
|
|
+ struct statvfs buf = {
|
|
+ .f_namemax = 255,
|
|
+ .f_bsize = 512,
|
|
+ };
|
|
+ fuse_reply_statfs(req, &buf);
|
|
+ }
|
|
}
|
|
|
|
static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
|
|
- char *name = PARAM(arg);
|
|
- char *value = name + strlen(name) + 1;
|
|
+ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)inarg;
|
|
+ char *name = PARAM(arg);
|
|
+ char *value = name + strlen(name) + 1;
|
|
|
|
- if (req->se->op.setxattr)
|
|
- req->se->op.setxattr(req, nodeid, name, value, arg->size,
|
|
- arg->flags);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.setxattr) {
|
|
+ req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
|
|
+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg;
|
|
|
|
- if (req->se->op.getxattr)
|
|
- req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.getxattr) {
|
|
+ req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
|
|
+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg;
|
|
|
|
- if (req->se->op.listxattr)
|
|
- req->se->op.listxattr(req, nodeid, arg->size);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.listxattr) {
|
|
+ req->se->op.listxattr(req, nodeid, arg->size);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- char *name = (char *) inarg;
|
|
+ char *name = (char *)inarg;
|
|
|
|
- if (req->se->op.removexattr)
|
|
- req->se->op.removexattr(req, nodeid, name);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.removexattr) {
|
|
+ req->se->op.removexattr(req, nodeid, name);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void convert_fuse_file_lock(struct fuse_file_lock *fl,
|
|
- struct flock *flock)
|
|
+ struct flock *flock)
|
|
{
|
|
- memset(flock, 0, sizeof(struct flock));
|
|
- flock->l_type = fl->type;
|
|
- flock->l_whence = SEEK_SET;
|
|
- flock->l_start = fl->start;
|
|
- if (fl->end == OFFSET_MAX)
|
|
- flock->l_len = 0;
|
|
- else
|
|
- flock->l_len = fl->end - fl->start + 1;
|
|
- flock->l_pid = fl->pid;
|
|
+ memset(flock, 0, sizeof(struct flock));
|
|
+ flock->l_type = fl->type;
|
|
+ flock->l_whence = SEEK_SET;
|
|
+ flock->l_start = fl->start;
|
|
+ if (fl->end == OFFSET_MAX) {
|
|
+ flock->l_len = 0;
|
|
+ } else {
|
|
+ flock->l_len = fl->end - fl->start + 1;
|
|
+ }
|
|
+ flock->l_pid = fl->pid;
|
|
}
|
|
|
|
static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
- struct flock flock;
|
|
+ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+ struct flock flock;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.lock_owner = arg->owner;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.lock_owner = arg->owner;
|
|
|
|
- convert_fuse_file_lock(&arg->lk, &flock);
|
|
- if (req->se->op.getlk)
|
|
- req->se->op.getlk(req, nodeid, &fi, &flock);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ convert_fuse_file_lock(&arg->lk, &flock);
|
|
+ if (req->se->op.getlk) {
|
|
+ req->se->op.getlk(req, nodeid, &fi, &flock);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
|
|
- const void *inarg, int sleep)
|
|
-{
|
|
- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
- struct flock flock;
|
|
-
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.lock_owner = arg->owner;
|
|
-
|
|
- if (arg->lk_flags & FUSE_LK_FLOCK) {
|
|
- int op = 0;
|
|
-
|
|
- switch (arg->lk.type) {
|
|
- case F_RDLCK:
|
|
- op = LOCK_SH;
|
|
- break;
|
|
- case F_WRLCK:
|
|
- op = LOCK_EX;
|
|
- break;
|
|
- case F_UNLCK:
|
|
- op = LOCK_UN;
|
|
- break;
|
|
- }
|
|
- if (!sleep)
|
|
- op |= LOCK_NB;
|
|
-
|
|
- if (req->se->op.flock)
|
|
- req->se->op.flock(req, nodeid, &fi, op);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
- } else {
|
|
- convert_fuse_file_lock(&arg->lk, &flock);
|
|
- if (req->se->op.setlk)
|
|
- req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
- }
|
|
+ const void *inarg, int sleep)
|
|
+{
|
|
+ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
+ struct flock flock;
|
|
+
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.lock_owner = arg->owner;
|
|
+
|
|
+ if (arg->lk_flags & FUSE_LK_FLOCK) {
|
|
+ int op = 0;
|
|
+
|
|
+ switch (arg->lk.type) {
|
|
+ case F_RDLCK:
|
|
+ op = LOCK_SH;
|
|
+ break;
|
|
+ case F_WRLCK:
|
|
+ op = LOCK_EX;
|
|
+ break;
|
|
+ case F_UNLCK:
|
|
+ op = LOCK_UN;
|
|
+ break;
|
|
+ }
|
|
+ if (!sleep) {
|
|
+ op |= LOCK_NB;
|
|
+ }
|
|
+
|
|
+ if (req->se->op.flock) {
|
|
+ req->se->op.flock(req, nodeid, &fi, op);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
+ } else {
|
|
+ convert_fuse_file_lock(&arg->lk, &flock);
|
|
+ if (req->se->op.setlk) {
|
|
+ req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- do_setlk_common(req, nodeid, inarg, 0);
|
|
+ do_setlk_common(req, nodeid, inarg, 0);
|
|
}
|
|
|
|
static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- do_setlk_common(req, nodeid, inarg, 1);
|
|
+ do_setlk_common(req, nodeid, inarg, 1);
|
|
}
|
|
|
|
static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
|
|
{
|
|
- struct fuse_req *curr;
|
|
-
|
|
- for (curr = se->list.next; curr != &se->list; curr = curr->next) {
|
|
- if (curr->unique == req->u.i.unique) {
|
|
- fuse_interrupt_func_t func;
|
|
- void *data;
|
|
-
|
|
- curr->ctr++;
|
|
- pthread_mutex_unlock(&se->lock);
|
|
-
|
|
- /* Ugh, ugly locking */
|
|
- pthread_mutex_lock(&curr->lock);
|
|
- pthread_mutex_lock(&se->lock);
|
|
- curr->interrupted = 1;
|
|
- func = curr->u.ni.func;
|
|
- data = curr->u.ni.data;
|
|
- pthread_mutex_unlock(&se->lock);
|
|
- if (func)
|
|
- func(curr, data);
|
|
- pthread_mutex_unlock(&curr->lock);
|
|
-
|
|
- pthread_mutex_lock(&se->lock);
|
|
- curr->ctr--;
|
|
- if (!curr->ctr)
|
|
- destroy_req(curr);
|
|
-
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- for (curr = se->interrupts.next; curr != &se->interrupts;
|
|
- curr = curr->next) {
|
|
- if (curr->u.i.unique == req->u.i.unique)
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
+ struct fuse_req *curr;
|
|
+
|
|
+ for (curr = se->list.next; curr != &se->list; curr = curr->next) {
|
|
+ if (curr->unique == req->u.i.unique) {
|
|
+ fuse_interrupt_func_t func;
|
|
+ void *data;
|
|
+
|
|
+ curr->ctr++;
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
+
|
|
+ /* Ugh, ugly locking */
|
|
+ pthread_mutex_lock(&curr->lock);
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ curr->interrupted = 1;
|
|
+ func = curr->u.ni.func;
|
|
+ data = curr->u.ni.data;
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
+ if (func) {
|
|
+ func(curr, data);
|
|
+ }
|
|
+ pthread_mutex_unlock(&curr->lock);
|
|
+
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ curr->ctr--;
|
|
+ if (!curr->ctr) {
|
|
+ destroy_req(curr);
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ for (curr = se->interrupts.next; curr != &se->interrupts;
|
|
+ curr = curr->next) {
|
|
+ if (curr->u.i.unique == req->u.i.unique) {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
|
|
- struct fuse_session *se = req->se;
|
|
+ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *)inarg;
|
|
+ struct fuse_session *se = req->se;
|
|
|
|
- (void) nodeid;
|
|
- if (se->debug)
|
|
- fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
|
|
- (unsigned long long) arg->unique);
|
|
+ (void)nodeid;
|
|
+ if (se->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
|
|
+ (unsigned long long)arg->unique);
|
|
+ }
|
|
|
|
- req->u.i.unique = arg->unique;
|
|
+ req->u.i.unique = arg->unique;
|
|
|
|
- pthread_mutex_lock(&se->lock);
|
|
- if (find_interrupted(se, req))
|
|
- destroy_req(req);
|
|
- else
|
|
- list_add_req(req, &se->interrupts);
|
|
- pthread_mutex_unlock(&se->lock);
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ if (find_interrupted(se, req)) {
|
|
+ destroy_req(req);
|
|
+ } else {
|
|
+ list_add_req(req, &se->interrupts);
|
|
+ }
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
}
|
|
|
|
static struct fuse_req *check_interrupt(struct fuse_session *se,
|
|
- struct fuse_req *req)
|
|
-{
|
|
- struct fuse_req *curr;
|
|
-
|
|
- for (curr = se->interrupts.next; curr != &se->interrupts;
|
|
- curr = curr->next) {
|
|
- if (curr->u.i.unique == req->unique) {
|
|
- req->interrupted = 1;
|
|
- list_del_req(curr);
|
|
- free(curr);
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
- curr = se->interrupts.next;
|
|
- if (curr != &se->interrupts) {
|
|
- list_del_req(curr);
|
|
- list_init_req(curr);
|
|
- return curr;
|
|
- } else
|
|
- return NULL;
|
|
+ struct fuse_req *req)
|
|
+{
|
|
+ struct fuse_req *curr;
|
|
+
|
|
+ for (curr = se->interrupts.next; curr != &se->interrupts;
|
|
+ curr = curr->next) {
|
|
+ if (curr->u.i.unique == req->unique) {
|
|
+ req->interrupted = 1;
|
|
+ list_del_req(curr);
|
|
+ free(curr);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+ curr = se->interrupts.next;
|
|
+ if (curr != &se->interrupts) {
|
|
+ list_del_req(curr);
|
|
+ list_init_req(curr);
|
|
+ return curr;
|
|
+ } else {
|
|
+ return NULL;
|
|
+ }
|
|
}
|
|
|
|
static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
|
|
+ struct fuse_bmap_in *arg = (struct fuse_bmap_in *)inarg;
|
|
|
|
- if (req->se->op.bmap)
|
|
- req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.bmap) {
|
|
+ req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
|
|
- unsigned int flags = arg->flags;
|
|
- void *in_buf = arg->in_size ? PARAM(arg) : NULL;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)inarg;
|
|
+ unsigned int flags = arg->flags;
|
|
+ void *in_buf = arg->in_size ? PARAM(arg) : NULL;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- if (flags & FUSE_IOCTL_DIR &&
|
|
- !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
|
|
- fuse_reply_err(req, ENOTTY);
|
|
- return;
|
|
- }
|
|
+ if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
|
|
+ fuse_reply_err(req, ENOTTY);
|
|
+ return;
|
|
+ }
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
|
|
- !(flags & FUSE_IOCTL_32BIT)) {
|
|
- req->ioctl_64bit = 1;
|
|
- }
|
|
+ if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
|
|
+ !(flags & FUSE_IOCTL_32BIT)) {
|
|
+ req->ioctl_64bit = 1;
|
|
+ }
|
|
|
|
- if (req->se->op.ioctl)
|
|
- req->se->op.ioctl(req, nodeid, arg->cmd,
|
|
- (void *)(uintptr_t)arg->arg, &fi, flags,
|
|
- in_buf, arg->in_size, arg->out_size);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.ioctl) {
|
|
+ req->se->op.ioctl(req, nodeid, arg->cmd, (void *)(uintptr_t)arg->arg,
|
|
+ &fi, flags, in_buf, arg->in_size, arg->out_size);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
|
|
{
|
|
- free(ph);
|
|
+ free(ph);
|
|
}
|
|
|
|
static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_poll_in *arg = (struct fuse_poll_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
- fi.poll_events = arg->events;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
+ fi.poll_events = arg->events;
|
|
|
|
- if (req->se->op.poll) {
|
|
- struct fuse_pollhandle *ph = NULL;
|
|
+ if (req->se->op.poll) {
|
|
+ struct fuse_pollhandle *ph = NULL;
|
|
|
|
- if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
|
|
- ph = malloc(sizeof(struct fuse_pollhandle));
|
|
- if (ph == NULL) {
|
|
- fuse_reply_err(req, ENOMEM);
|
|
- return;
|
|
- }
|
|
- ph->kh = arg->kh;
|
|
- ph->se = req->se;
|
|
- }
|
|
+ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
|
|
+ ph = malloc(sizeof(struct fuse_pollhandle));
|
|
+ if (ph == NULL) {
|
|
+ fuse_reply_err(req, ENOMEM);
|
|
+ return;
|
|
+ }
|
|
+ ph->kh = arg->kh;
|
|
+ ph->se = req->se;
|
|
+ }
|
|
|
|
- req->se->op.poll(req, nodeid, &fi, ph);
|
|
- } else {
|
|
- fuse_reply_err(req, ENOSYS);
|
|
- }
|
|
+ req->se->op.poll(req, nodeid, &fi, ph);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.fallocate)
|
|
- req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.fallocate) {
|
|
+ req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length,
|
|
+ &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
-static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
|
|
+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
|
|
+ const void *inarg)
|
|
{
|
|
- struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
|
|
- struct fuse_file_info fi_in, fi_out;
|
|
+ struct fuse_copy_file_range_in *arg =
|
|
+ (struct fuse_copy_file_range_in *)inarg;
|
|
+ struct fuse_file_info fi_in, fi_out;
|
|
|
|
- memset(&fi_in, 0, sizeof(fi_in));
|
|
- fi_in.fh = arg->fh_in;
|
|
+ memset(&fi_in, 0, sizeof(fi_in));
|
|
+ fi_in.fh = arg->fh_in;
|
|
|
|
- memset(&fi_out, 0, sizeof(fi_out));
|
|
- fi_out.fh = arg->fh_out;
|
|
+ memset(&fi_out, 0, sizeof(fi_out));
|
|
+ fi_out.fh = arg->fh_out;
|
|
|
|
|
|
- if (req->se->op.copy_file_range)
|
|
- req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
|
|
- &fi_in, arg->nodeid_out,
|
|
- arg->off_out, &fi_out, arg->len,
|
|
- arg->flags);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.copy_file_range) {
|
|
+ req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in,
|
|
+ arg->nodeid_out, arg->off_out, &fi_out,
|
|
+ arg->len, arg->flags);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
|
|
- struct fuse_file_info fi;
|
|
+ struct fuse_lseek_in *arg = (struct fuse_lseek_in *)inarg;
|
|
+ struct fuse_file_info fi;
|
|
|
|
- memset(&fi, 0, sizeof(fi));
|
|
- fi.fh = arg->fh;
|
|
+ memset(&fi, 0, sizeof(fi));
|
|
+ fi.fh = arg->fh;
|
|
|
|
- if (req->se->op.lseek)
|
|
- req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
|
|
- else
|
|
- fuse_reply_err(req, ENOSYS);
|
|
+ if (req->se->op.lseek) {
|
|
+ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
|
|
+ } else {
|
|
+ fuse_reply_err(req, ENOSYS);
|
|
+ }
|
|
}
|
|
|
|
static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
|
|
- struct fuse_init_out outarg;
|
|
- struct fuse_session *se = req->se;
|
|
- size_t bufsize = se->bufsize;
|
|
- size_t outargsize = sizeof(outarg);
|
|
-
|
|
- (void) nodeid;
|
|
- if (se->debug) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
|
|
- if (arg->major == 7 && arg->minor >= 6) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
|
|
- fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
|
|
- arg->max_readahead);
|
|
- }
|
|
- }
|
|
- se->conn.proto_major = arg->major;
|
|
- se->conn.proto_minor = arg->minor;
|
|
- se->conn.capable = 0;
|
|
- se->conn.want = 0;
|
|
-
|
|
- memset(&outarg, 0, sizeof(outarg));
|
|
- outarg.major = FUSE_KERNEL_VERSION;
|
|
- outarg.minor = FUSE_KERNEL_MINOR_VERSION;
|
|
-
|
|
- if (arg->major < 7) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
|
|
- arg->major, arg->minor);
|
|
- fuse_reply_err(req, EPROTO);
|
|
- return;
|
|
- }
|
|
-
|
|
- if (arg->major > 7) {
|
|
- /* Wait for a second INIT request with a 7.X version */
|
|
- send_reply_ok(req, &outarg, sizeof(outarg));
|
|
- return;
|
|
- }
|
|
-
|
|
- if (arg->minor >= 6) {
|
|
- if (arg->max_readahead < se->conn.max_readahead)
|
|
- se->conn.max_readahead = arg->max_readahead;
|
|
- if (arg->flags & FUSE_ASYNC_READ)
|
|
- se->conn.capable |= FUSE_CAP_ASYNC_READ;
|
|
- if (arg->flags & FUSE_POSIX_LOCKS)
|
|
- se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
|
|
- if (arg->flags & FUSE_ATOMIC_O_TRUNC)
|
|
- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
|
|
- if (arg->flags & FUSE_EXPORT_SUPPORT)
|
|
- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
|
|
- if (arg->flags & FUSE_DONT_MASK)
|
|
- se->conn.capable |= FUSE_CAP_DONT_MASK;
|
|
- if (arg->flags & FUSE_FLOCK_LOCKS)
|
|
- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
|
|
- if (arg->flags & FUSE_AUTO_INVAL_DATA)
|
|
- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
|
|
- if (arg->flags & FUSE_DO_READDIRPLUS)
|
|
- se->conn.capable |= FUSE_CAP_READDIRPLUS;
|
|
- if (arg->flags & FUSE_READDIRPLUS_AUTO)
|
|
- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
|
|
- if (arg->flags & FUSE_ASYNC_DIO)
|
|
- se->conn.capable |= FUSE_CAP_ASYNC_DIO;
|
|
- if (arg->flags & FUSE_WRITEBACK_CACHE)
|
|
- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
|
|
- if (arg->flags & FUSE_NO_OPEN_SUPPORT)
|
|
- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
|
|
- if (arg->flags & FUSE_PARALLEL_DIROPS)
|
|
- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
|
|
- if (arg->flags & FUSE_POSIX_ACL)
|
|
- se->conn.capable |= FUSE_CAP_POSIX_ACL;
|
|
- if (arg->flags & FUSE_HANDLE_KILLPRIV)
|
|
- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
|
|
- if (arg->flags & FUSE_NO_OPENDIR_SUPPORT)
|
|
- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
|
|
- if (!(arg->flags & FUSE_MAX_PAGES)) {
|
|
- size_t max_bufsize =
|
|
- FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
|
|
- + FUSE_BUFFER_HEADER_SIZE;
|
|
- if (bufsize > max_bufsize) {
|
|
- bufsize = max_bufsize;
|
|
- }
|
|
- }
|
|
- } else {
|
|
- se->conn.max_readahead = 0;
|
|
- }
|
|
-
|
|
- if (se->conn.proto_minor >= 14) {
|
|
+ struct fuse_init_in *arg = (struct fuse_init_in *)inarg;
|
|
+ struct fuse_init_out outarg;
|
|
+ struct fuse_session *se = req->se;
|
|
+ size_t bufsize = se->bufsize;
|
|
+ size_t outargsize = sizeof(outarg);
|
|
+
|
|
+ (void)nodeid;
|
|
+ if (se->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
|
|
+ if (arg->major == 7 && arg->minor >= 6) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
|
|
+ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
|
|
+ arg->max_readahead);
|
|
+ }
|
|
+ }
|
|
+ se->conn.proto_major = arg->major;
|
|
+ se->conn.proto_minor = arg->minor;
|
|
+ se->conn.capable = 0;
|
|
+ se->conn.want = 0;
|
|
+
|
|
+ memset(&outarg, 0, sizeof(outarg));
|
|
+ outarg.major = FUSE_KERNEL_VERSION;
|
|
+ outarg.minor = FUSE_KERNEL_MINOR_VERSION;
|
|
+
|
|
+ if (arg->major < 7) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
|
|
+ arg->major, arg->minor);
|
|
+ fuse_reply_err(req, EPROTO);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (arg->major > 7) {
|
|
+ /* Wait for a second INIT request with a 7.X version */
|
|
+ send_reply_ok(req, &outarg, sizeof(outarg));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (arg->minor >= 6) {
|
|
+ if (arg->max_readahead < se->conn.max_readahead) {
|
|
+ se->conn.max_readahead = arg->max_readahead;
|
|
+ }
|
|
+ if (arg->flags & FUSE_ASYNC_READ) {
|
|
+ se->conn.capable |= FUSE_CAP_ASYNC_READ;
|
|
+ }
|
|
+ if (arg->flags & FUSE_POSIX_LOCKS) {
|
|
+ se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
|
|
+ }
|
|
+ if (arg->flags & FUSE_ATOMIC_O_TRUNC) {
|
|
+ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
|
|
+ }
|
|
+ if (arg->flags & FUSE_EXPORT_SUPPORT) {
|
|
+ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
|
|
+ }
|
|
+ if (arg->flags & FUSE_DONT_MASK) {
|
|
+ se->conn.capable |= FUSE_CAP_DONT_MASK;
|
|
+ }
|
|
+ if (arg->flags & FUSE_FLOCK_LOCKS) {
|
|
+ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
|
|
+ }
|
|
+ if (arg->flags & FUSE_AUTO_INVAL_DATA) {
|
|
+ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
|
|
+ }
|
|
+ if (arg->flags & FUSE_DO_READDIRPLUS) {
|
|
+ se->conn.capable |= FUSE_CAP_READDIRPLUS;
|
|
+ }
|
|
+ if (arg->flags & FUSE_READDIRPLUS_AUTO) {
|
|
+ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
|
|
+ }
|
|
+ if (arg->flags & FUSE_ASYNC_DIO) {
|
|
+ se->conn.capable |= FUSE_CAP_ASYNC_DIO;
|
|
+ }
|
|
+ if (arg->flags & FUSE_WRITEBACK_CACHE) {
|
|
+ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
|
|
+ }
|
|
+ if (arg->flags & FUSE_NO_OPEN_SUPPORT) {
|
|
+ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
|
|
+ }
|
|
+ if (arg->flags & FUSE_PARALLEL_DIROPS) {
|
|
+ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
|
|
+ }
|
|
+ if (arg->flags & FUSE_POSIX_ACL) {
|
|
+ se->conn.capable |= FUSE_CAP_POSIX_ACL;
|
|
+ }
|
|
+ if (arg->flags & FUSE_HANDLE_KILLPRIV) {
|
|
+ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
|
|
+ }
|
|
+ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) {
|
|
+ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
|
|
+ }
|
|
+ if (!(arg->flags & FUSE_MAX_PAGES)) {
|
|
+ size_t max_bufsize =
|
|
+ FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() +
|
|
+ FUSE_BUFFER_HEADER_SIZE;
|
|
+ if (bufsize > max_bufsize) {
|
|
+ bufsize = max_bufsize;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ se->conn.max_readahead = 0;
|
|
+ }
|
|
+
|
|
+ if (se->conn.proto_minor >= 14) {
|
|
#ifdef HAVE_SPLICE
|
|
#ifdef HAVE_VMSPLICE
|
|
- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
|
|
+ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
|
|
#endif
|
|
- se->conn.capable |= FUSE_CAP_SPLICE_READ;
|
|
+ se->conn.capable |= FUSE_CAP_SPLICE_READ;
|
|
#endif
|
|
- }
|
|
- if (se->conn.proto_minor >= 18)
|
|
- se->conn.capable |= FUSE_CAP_IOCTL_DIR;
|
|
-
|
|
- /* Default settings for modern filesystems.
|
|
- *
|
|
- * Most of these capabilities were disabled by default in
|
|
- * libfuse2 for backwards compatibility reasons. In libfuse3,
|
|
- * we can finally enable them by default (as long as they're
|
|
- * supported by the kernel).
|
|
- */
|
|
-#define LL_SET_DEFAULT(cond, cap) \
|
|
- if ((cond) && (se->conn.capable & (cap))) \
|
|
- se->conn.want |= (cap)
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
|
|
- LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
|
|
- LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
|
|
- LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
|
|
- FUSE_CAP_POSIX_LOCKS);
|
|
- LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
|
|
- LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
|
|
- LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
|
|
- FUSE_CAP_READDIRPLUS_AUTO);
|
|
- se->conn.time_gran = 1;
|
|
-
|
|
- if (bufsize < FUSE_MIN_READ_BUFFER) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n",
|
|
- bufsize);
|
|
- bufsize = FUSE_MIN_READ_BUFFER;
|
|
- }
|
|
- se->bufsize = bufsize;
|
|
-
|
|
- if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE)
|
|
- se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE;
|
|
-
|
|
- se->got_init = 1;
|
|
- if (se->op.init)
|
|
- se->op.init(se->userdata, &se->conn);
|
|
-
|
|
- if (se->conn.want & (~se->conn.capable)) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities "
|
|
- "0x%x that are not supported by kernel, aborting.\n",
|
|
- se->conn.want & (~se->conn.capable));
|
|
- fuse_reply_err(req, EPROTO);
|
|
- se->error = -EPROTO;
|
|
- fuse_session_exit(se);
|
|
- return;
|
|
- }
|
|
-
|
|
- if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) {
|
|
- se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
|
|
- }
|
|
- if (arg->flags & FUSE_MAX_PAGES) {
|
|
- outarg.flags |= FUSE_MAX_PAGES;
|
|
- outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
|
|
- }
|
|
-
|
|
- /* Always enable big writes, this is superseded
|
|
- by the max_write option */
|
|
- outarg.flags |= FUSE_BIG_WRITES;
|
|
-
|
|
- if (se->conn.want & FUSE_CAP_ASYNC_READ)
|
|
- outarg.flags |= FUSE_ASYNC_READ;
|
|
- if (se->conn.want & FUSE_CAP_POSIX_LOCKS)
|
|
- outarg.flags |= FUSE_POSIX_LOCKS;
|
|
- if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
|
|
- outarg.flags |= FUSE_ATOMIC_O_TRUNC;
|
|
- if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT)
|
|
- outarg.flags |= FUSE_EXPORT_SUPPORT;
|
|
- if (se->conn.want & FUSE_CAP_DONT_MASK)
|
|
- outarg.flags |= FUSE_DONT_MASK;
|
|
- if (se->conn.want & FUSE_CAP_FLOCK_LOCKS)
|
|
- outarg.flags |= FUSE_FLOCK_LOCKS;
|
|
- if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA)
|
|
- outarg.flags |= FUSE_AUTO_INVAL_DATA;
|
|
- if (se->conn.want & FUSE_CAP_READDIRPLUS)
|
|
- outarg.flags |= FUSE_DO_READDIRPLUS;
|
|
- if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO)
|
|
- outarg.flags |= FUSE_READDIRPLUS_AUTO;
|
|
- if (se->conn.want & FUSE_CAP_ASYNC_DIO)
|
|
- outarg.flags |= FUSE_ASYNC_DIO;
|
|
- if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE)
|
|
- outarg.flags |= FUSE_WRITEBACK_CACHE;
|
|
- if (se->conn.want & FUSE_CAP_POSIX_ACL)
|
|
- outarg.flags |= FUSE_POSIX_ACL;
|
|
- outarg.max_readahead = se->conn.max_readahead;
|
|
- outarg.max_write = se->conn.max_write;
|
|
- if (se->conn.proto_minor >= 13) {
|
|
- if (se->conn.max_background >= (1 << 16))
|
|
- se->conn.max_background = (1 << 16) - 1;
|
|
- if (se->conn.congestion_threshold > se->conn.max_background)
|
|
- se->conn.congestion_threshold = se->conn.max_background;
|
|
- if (!se->conn.congestion_threshold) {
|
|
- se->conn.congestion_threshold =
|
|
- se->conn.max_background * 3 / 4;
|
|
- }
|
|
-
|
|
- outarg.max_background = se->conn.max_background;
|
|
- outarg.congestion_threshold = se->conn.congestion_threshold;
|
|
- }
|
|
- if (se->conn.proto_minor >= 23)
|
|
- outarg.time_gran = se->conn.time_gran;
|
|
-
|
|
- if (se->debug) {
|
|
- fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
|
|
- fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
|
|
- fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
|
|
- outarg.max_readahead);
|
|
- fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
|
|
- fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
|
|
- outarg.max_background);
|
|
- fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
|
|
- outarg.congestion_threshold);
|
|
- fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
|
|
- outarg.time_gran);
|
|
- }
|
|
- if (arg->minor < 5)
|
|
- outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
|
|
- else if (arg->minor < 23)
|
|
- outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
|
|
-
|
|
- send_reply_ok(req, &outarg, outargsize);
|
|
+ }
|
|
+ if (se->conn.proto_minor >= 18) {
|
|
+ se->conn.capable |= FUSE_CAP_IOCTL_DIR;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Default settings for modern filesystems.
|
|
+ *
|
|
+ * Most of these capabilities were disabled by default in
|
|
+ * libfuse2 for backwards compatibility reasons. In libfuse3,
|
|
+ * we can finally enable them by default (as long as they're
|
|
+ * supported by the kernel).
|
|
+ */
|
|
+#define LL_SET_DEFAULT(cond, cap) \
|
|
+ if ((cond) && (se->conn.capable & (cap))) \
|
|
+ se->conn.want |= (cap)
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
|
|
+ LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
|
|
+ LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
|
|
+ LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS);
|
|
+ LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
|
|
+ LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
|
|
+ LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
|
|
+ FUSE_CAP_READDIRPLUS_AUTO);
|
|
+ se->conn.time_gran = 1;
|
|
+
|
|
+ if (bufsize < FUSE_MIN_READ_BUFFER) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n",
|
|
+ bufsize);
|
|
+ bufsize = FUSE_MIN_READ_BUFFER;
|
|
+ }
|
|
+ se->bufsize = bufsize;
|
|
+
|
|
+ if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) {
|
|
+ se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE;
|
|
+ }
|
|
+
|
|
+ se->got_init = 1;
|
|
+ if (se->op.init) {
|
|
+ se->op.init(se->userdata, &se->conn);
|
|
+ }
|
|
+
|
|
+ if (se->conn.want & (~se->conn.capable)) {
|
|
+ fuse_log(FUSE_LOG_ERR,
|
|
+ "fuse: error: filesystem requested capabilities "
|
|
+ "0x%x that are not supported by kernel, aborting.\n",
|
|
+ se->conn.want & (~se->conn.capable));
|
|
+ fuse_reply_err(req, EPROTO);
|
|
+ se->error = -EPROTO;
|
|
+ fuse_session_exit(se);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) {
|
|
+ se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
|
|
+ }
|
|
+ if (arg->flags & FUSE_MAX_PAGES) {
|
|
+ outarg.flags |= FUSE_MAX_PAGES;
|
|
+ outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Always enable big writes, this is superseded
|
|
+ * by the max_write option
|
|
+ */
|
|
+ outarg.flags |= FUSE_BIG_WRITES;
|
|
+
|
|
+ if (se->conn.want & FUSE_CAP_ASYNC_READ) {
|
|
+ outarg.flags |= FUSE_ASYNC_READ;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_POSIX_LOCKS) {
|
|
+ outarg.flags |= FUSE_POSIX_LOCKS;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) {
|
|
+ outarg.flags |= FUSE_ATOMIC_O_TRUNC;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) {
|
|
+ outarg.flags |= FUSE_EXPORT_SUPPORT;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_DONT_MASK) {
|
|
+ outarg.flags |= FUSE_DONT_MASK;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) {
|
|
+ outarg.flags |= FUSE_FLOCK_LOCKS;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) {
|
|
+ outarg.flags |= FUSE_AUTO_INVAL_DATA;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_READDIRPLUS) {
|
|
+ outarg.flags |= FUSE_DO_READDIRPLUS;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) {
|
|
+ outarg.flags |= FUSE_READDIRPLUS_AUTO;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_ASYNC_DIO) {
|
|
+ outarg.flags |= FUSE_ASYNC_DIO;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) {
|
|
+ outarg.flags |= FUSE_WRITEBACK_CACHE;
|
|
+ }
|
|
+ if (se->conn.want & FUSE_CAP_POSIX_ACL) {
|
|
+ outarg.flags |= FUSE_POSIX_ACL;
|
|
+ }
|
|
+ outarg.max_readahead = se->conn.max_readahead;
|
|
+ outarg.max_write = se->conn.max_write;
|
|
+ if (se->conn.proto_minor >= 13) {
|
|
+ if (se->conn.max_background >= (1 << 16)) {
|
|
+ se->conn.max_background = (1 << 16) - 1;
|
|
+ }
|
|
+ if (se->conn.congestion_threshold > se->conn.max_background) {
|
|
+ se->conn.congestion_threshold = se->conn.max_background;
|
|
+ }
|
|
+ if (!se->conn.congestion_threshold) {
|
|
+ se->conn.congestion_threshold = se->conn.max_background * 3 / 4;
|
|
+ }
|
|
+
|
|
+ outarg.max_background = se->conn.max_background;
|
|
+ outarg.congestion_threshold = se->conn.congestion_threshold;
|
|
+ }
|
|
+ if (se->conn.proto_minor >= 23) {
|
|
+ outarg.time_gran = se->conn.time_gran;
|
|
+ }
|
|
+
|
|
+ if (se->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major,
|
|
+ outarg.minor);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
|
|
+ outarg.max_readahead);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
|
|
+ outarg.max_background);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
|
|
+ outarg.congestion_threshold);
|
|
+ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran);
|
|
+ }
|
|
+ if (arg->minor < 5) {
|
|
+ outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
|
|
+ } else if (arg->minor < 23) {
|
|
+ outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
|
|
+ }
|
|
+
|
|
+ send_reply_ok(req, &outarg, outargsize);
|
|
}
|
|
|
|
static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
|
{
|
|
- struct fuse_session *se = req->se;
|
|
+ struct fuse_session *se = req->se;
|
|
|
|
- (void) nodeid;
|
|
- (void) inarg;
|
|
+ (void)nodeid;
|
|
+ (void)inarg;
|
|
|
|
- se->got_destroy = 1;
|
|
- if (se->op.destroy)
|
|
- se->op.destroy(se->userdata);
|
|
+ se->got_destroy = 1;
|
|
+ if (se->op.destroy) {
|
|
+ se->op.destroy(se->userdata);
|
|
+ }
|
|
|
|
- send_reply_ok(req, NULL, 0);
|
|
+ send_reply_ok(req, NULL, 0);
|
|
}
|
|
|
|
static void list_del_nreq(struct fuse_notify_req *nreq)
|
|
{
|
|
- struct fuse_notify_req *prev = nreq->prev;
|
|
- struct fuse_notify_req *next = nreq->next;
|
|
- prev->next = next;
|
|
- next->prev = prev;
|
|
+ struct fuse_notify_req *prev = nreq->prev;
|
|
+ struct fuse_notify_req *next = nreq->next;
|
|
+ prev->next = next;
|
|
+ next->prev = prev;
|
|
}
|
|
|
|
static void list_add_nreq(struct fuse_notify_req *nreq,
|
|
- struct fuse_notify_req *next)
|
|
+ struct fuse_notify_req *next)
|
|
{
|
|
- struct fuse_notify_req *prev = next->prev;
|
|
- nreq->next = next;
|
|
- nreq->prev = prev;
|
|
- prev->next = nreq;
|
|
- next->prev = nreq;
|
|
+ struct fuse_notify_req *prev = next->prev;
|
|
+ nreq->next = next;
|
|
+ nreq->prev = prev;
|
|
+ prev->next = nreq;
|
|
+ next->prev = nreq;
|
|
}
|
|
|
|
static void list_init_nreq(struct fuse_notify_req *nreq)
|
|
{
|
|
- nreq->next = nreq;
|
|
- nreq->prev = nreq;
|
|
+ nreq->next = nreq;
|
|
+ nreq->prev = nreq;
|
|
}
|
|
|
|
static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
|
|
- const void *inarg, const struct fuse_buf *buf)
|
|
+ const void *inarg, const struct fuse_buf *buf)
|
|
{
|
|
- struct fuse_session *se = req->se;
|
|
- struct fuse_notify_req *nreq;
|
|
- struct fuse_notify_req *head;
|
|
+ struct fuse_session *se = req->se;
|
|
+ struct fuse_notify_req *nreq;
|
|
+ struct fuse_notify_req *head;
|
|
|
|
- pthread_mutex_lock(&se->lock);
|
|
- head = &se->notify_list;
|
|
- for (nreq = head->next; nreq != head; nreq = nreq->next) {
|
|
- if (nreq->unique == req->unique) {
|
|
- list_del_nreq(nreq);
|
|
- break;
|
|
- }
|
|
- }
|
|
- pthread_mutex_unlock(&se->lock);
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ head = &se->notify_list;
|
|
+ for (nreq = head->next; nreq != head; nreq = nreq->next) {
|
|
+ if (nreq->unique == req->unique) {
|
|
+ list_del_nreq(nreq);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
|
|
- if (nreq != head)
|
|
- nreq->reply(nreq, req, nodeid, inarg, buf);
|
|
+ if (nreq != head) {
|
|
+ nreq->reply(nreq, req, nodeid, inarg, buf);
|
|
+ }
|
|
}
|
|
|
|
static int send_notify_iov(struct fuse_session *se, int notify_code,
|
|
- struct iovec *iov, int count)
|
|
+ struct iovec *iov, int count)
|
|
{
|
|
- struct fuse_out_header out;
|
|
+ struct fuse_out_header out;
|
|
|
|
- if (!se->got_init)
|
|
- return -ENOTCONN;
|
|
+ if (!se->got_init) {
|
|
+ return -ENOTCONN;
|
|
+ }
|
|
|
|
- out.unique = 0;
|
|
- out.error = notify_code;
|
|
- iov[0].iov_base = &out;
|
|
- iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
+ out.unique = 0;
|
|
+ out.error = notify_code;
|
|
+ iov[0].iov_base = &out;
|
|
+ iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
|
- return fuse_send_msg(se, NULL, iov, count);
|
|
+ return fuse_send_msg(se, NULL, iov, count);
|
|
}
|
|
|
|
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
|
|
{
|
|
- if (ph != NULL) {
|
|
- struct fuse_notify_poll_wakeup_out outarg;
|
|
- struct iovec iov[2];
|
|
+ if (ph != NULL) {
|
|
+ struct fuse_notify_poll_wakeup_out outarg;
|
|
+ struct iovec iov[2];
|
|
|
|
- outarg.kh = ph->kh;
|
|
+ outarg.kh = ph->kh;
|
|
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
|
|
- return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
|
|
- } else {
|
|
- return 0;
|
|
- }
|
|
+ return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
}
|
|
|
|
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
|
|
- off_t off, off_t len)
|
|
+ off_t off, off_t len)
|
|
{
|
|
- struct fuse_notify_inval_inode_out outarg;
|
|
- struct iovec iov[2];
|
|
+ struct fuse_notify_inval_inode_out outarg;
|
|
+ struct iovec iov[2];
|
|
+
|
|
+ if (!se) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- if (!se)
|
|
- return -EINVAL;
|
|
+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) {
|
|
+ return -ENOSYS;
|
|
+ }
|
|
|
|
- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12)
|
|
- return -ENOSYS;
|
|
-
|
|
- outarg.ino = ino;
|
|
- outarg.off = off;
|
|
- outarg.len = len;
|
|
+ outarg.ino = ino;
|
|
+ outarg.off = off;
|
|
+ outarg.len = len;
|
|
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
|
|
- return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
|
|
+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
|
|
}
|
|
|
|
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
|
|
- const char *name, size_t namelen)
|
|
+ const char *name, size_t namelen)
|
|
{
|
|
- struct fuse_notify_inval_entry_out outarg;
|
|
- struct iovec iov[3];
|
|
+ struct fuse_notify_inval_entry_out outarg;
|
|
+ struct iovec iov[3];
|
|
+
|
|
+ if (!se) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- if (!se)
|
|
- return -EINVAL;
|
|
-
|
|
- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12)
|
|
- return -ENOSYS;
|
|
+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) {
|
|
+ return -ENOSYS;
|
|
+ }
|
|
|
|
- outarg.parent = parent;
|
|
- outarg.namelen = namelen;
|
|
- outarg.padding = 0;
|
|
+ outarg.parent = parent;
|
|
+ outarg.namelen = namelen;
|
|
+ outarg.padding = 0;
|
|
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
- iov[2].iov_base = (void *)name;
|
|
- iov[2].iov_len = namelen + 1;
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
+ iov[2].iov_base = (void *)name;
|
|
+ iov[2].iov_len = namelen + 1;
|
|
|
|
- return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
|
|
+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
|
|
}
|
|
|
|
-int fuse_lowlevel_notify_delete(struct fuse_session *se,
|
|
- fuse_ino_t parent, fuse_ino_t child,
|
|
- const char *name, size_t namelen)
|
|
+int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent,
|
|
+ fuse_ino_t child, const char *name,
|
|
+ size_t namelen)
|
|
{
|
|
- struct fuse_notify_delete_out outarg;
|
|
- struct iovec iov[3];
|
|
+ struct fuse_notify_delete_out outarg;
|
|
+ struct iovec iov[3];
|
|
|
|
- if (!se)
|
|
- return -EINVAL;
|
|
+ if (!se) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- if (se->conn.proto_major < 6 || se->conn.proto_minor < 18)
|
|
- return -ENOSYS;
|
|
+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) {
|
|
+ return -ENOSYS;
|
|
+ }
|
|
|
|
- outarg.parent = parent;
|
|
- outarg.child = child;
|
|
- outarg.namelen = namelen;
|
|
- outarg.padding = 0;
|
|
+ outarg.parent = parent;
|
|
+ outarg.child = child;
|
|
+ outarg.namelen = namelen;
|
|
+ outarg.padding = 0;
|
|
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
- iov[2].iov_base = (void *)name;
|
|
- iov[2].iov_len = namelen + 1;
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
+ iov[2].iov_base = (void *)name;
|
|
+ iov[2].iov_len = namelen + 1;
|
|
|
|
- return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
|
|
+ return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
|
|
}
|
|
|
|
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
|
|
- off_t offset, struct fuse_bufvec *bufv,
|
|
- enum fuse_buf_copy_flags flags)
|
|
+ off_t offset, struct fuse_bufvec *bufv,
|
|
+ enum fuse_buf_copy_flags flags)
|
|
{
|
|
- struct fuse_out_header out;
|
|
- struct fuse_notify_store_out outarg;
|
|
- struct iovec iov[3];
|
|
- size_t size = fuse_buf_size(bufv);
|
|
- int res;
|
|
+ struct fuse_out_header out;
|
|
+ struct fuse_notify_store_out outarg;
|
|
+ struct iovec iov[3];
|
|
+ size_t size = fuse_buf_size(bufv);
|
|
+ int res;
|
|
|
|
- if (!se)
|
|
- return -EINVAL;
|
|
+ if (!se) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15)
|
|
- return -ENOSYS;
|
|
+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) {
|
|
+ return -ENOSYS;
|
|
+ }
|
|
|
|
- out.unique = 0;
|
|
- out.error = FUSE_NOTIFY_STORE;
|
|
+ out.unique = 0;
|
|
+ out.error = FUSE_NOTIFY_STORE;
|
|
|
|
- outarg.nodeid = ino;
|
|
- outarg.offset = offset;
|
|
- outarg.size = size;
|
|
- outarg.padding = 0;
|
|
+ outarg.nodeid = ino;
|
|
+ outarg.offset = offset;
|
|
+ outarg.size = size;
|
|
+ outarg.padding = 0;
|
|
|
|
- iov[0].iov_base = &out;
|
|
- iov[0].iov_len = sizeof(out);
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
+ iov[0].iov_base = &out;
|
|
+ iov[0].iov_len = sizeof(out);
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
|
|
- res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
|
|
- if (res > 0)
|
|
- res = -res;
|
|
+ res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
|
|
+ if (res > 0) {
|
|
+ res = -res;
|
|
+ }
|
|
|
|
- return res;
|
|
+ return res;
|
|
}
|
|
|
|
struct fuse_retrieve_req {
|
|
- struct fuse_notify_req nreq;
|
|
- void *cookie;
|
|
+ struct fuse_notify_req nreq;
|
|
+ void *cookie;
|
|
};
|
|
|
|
-static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
|
|
- fuse_req_t req, fuse_ino_t ino,
|
|
- const void *inarg,
|
|
- const struct fuse_buf *ibuf)
|
|
-{
|
|
- struct fuse_session *se = req->se;
|
|
- struct fuse_retrieve_req *rreq =
|
|
- container_of(nreq, struct fuse_retrieve_req, nreq);
|
|
- const struct fuse_notify_retrieve_in *arg = inarg;
|
|
- struct fuse_bufvec bufv = {
|
|
- .buf[0] = *ibuf,
|
|
- .count = 1,
|
|
- };
|
|
-
|
|
- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
|
|
- bufv.buf[0].mem = PARAM(arg);
|
|
-
|
|
- bufv.buf[0].size -= sizeof(struct fuse_in_header) +
|
|
- sizeof(struct fuse_notify_retrieve_in);
|
|
-
|
|
- if (bufv.buf[0].size < arg->size) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
|
|
- fuse_reply_none(req);
|
|
- goto out;
|
|
- }
|
|
- bufv.buf[0].size = arg->size;
|
|
-
|
|
- if (se->op.retrieve_reply) {
|
|
- se->op.retrieve_reply(req, rreq->cookie, ino,
|
|
- arg->offset, &bufv);
|
|
- } else {
|
|
- fuse_reply_none(req);
|
|
- }
|
|
+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req,
|
|
+ fuse_ino_t ino, const void *inarg,
|
|
+ const struct fuse_buf *ibuf)
|
|
+{
|
|
+ struct fuse_session *se = req->se;
|
|
+ struct fuse_retrieve_req *rreq =
|
|
+ container_of(nreq, struct fuse_retrieve_req, nreq);
|
|
+ const struct fuse_notify_retrieve_in *arg = inarg;
|
|
+ struct fuse_bufvec bufv = {
|
|
+ .buf[0] = *ibuf,
|
|
+ .count = 1,
|
|
+ };
|
|
+
|
|
+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) {
|
|
+ bufv.buf[0].mem = PARAM(arg);
|
|
+ }
|
|
+
|
|
+ bufv.buf[0].size -=
|
|
+ sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in);
|
|
+
|
|
+ if (bufv.buf[0].size < arg->size) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
|
|
+ fuse_reply_none(req);
|
|
+ goto out;
|
|
+ }
|
|
+ bufv.buf[0].size = arg->size;
|
|
+
|
|
+ if (se->op.retrieve_reply) {
|
|
+ se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv);
|
|
+ } else {
|
|
+ fuse_reply_none(req);
|
|
+ }
|
|
out:
|
|
- free(rreq);
|
|
+ free(rreq);
|
|
}
|
|
|
|
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
|
|
- size_t size, off_t offset, void *cookie)
|
|
+ size_t size, off_t offset, void *cookie)
|
|
{
|
|
- struct fuse_notify_retrieve_out outarg;
|
|
- struct iovec iov[2];
|
|
- struct fuse_retrieve_req *rreq;
|
|
- int err;
|
|
+ struct fuse_notify_retrieve_out outarg;
|
|
+ struct iovec iov[2];
|
|
+ struct fuse_retrieve_req *rreq;
|
|
+ int err;
|
|
|
|
- if (!se)
|
|
- return -EINVAL;
|
|
+ if (!se) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15)
|
|
- return -ENOSYS;
|
|
+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) {
|
|
+ return -ENOSYS;
|
|
+ }
|
|
|
|
- rreq = malloc(sizeof(*rreq));
|
|
- if (rreq == NULL)
|
|
- return -ENOMEM;
|
|
+ rreq = malloc(sizeof(*rreq));
|
|
+ if (rreq == NULL) {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
|
|
- pthread_mutex_lock(&se->lock);
|
|
- rreq->cookie = cookie;
|
|
- rreq->nreq.unique = se->notify_ctr++;
|
|
- rreq->nreq.reply = fuse_ll_retrieve_reply;
|
|
- list_add_nreq(&rreq->nreq, &se->notify_list);
|
|
- pthread_mutex_unlock(&se->lock);
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ rreq->cookie = cookie;
|
|
+ rreq->nreq.unique = se->notify_ctr++;
|
|
+ rreq->nreq.reply = fuse_ll_retrieve_reply;
|
|
+ list_add_nreq(&rreq->nreq, &se->notify_list);
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
|
|
- outarg.notify_unique = rreq->nreq.unique;
|
|
- outarg.nodeid = ino;
|
|
- outarg.offset = offset;
|
|
- outarg.size = size;
|
|
- outarg.padding = 0;
|
|
+ outarg.notify_unique = rreq->nreq.unique;
|
|
+ outarg.nodeid = ino;
|
|
+ outarg.offset = offset;
|
|
+ outarg.size = size;
|
|
+ outarg.padding = 0;
|
|
|
|
- iov[1].iov_base = &outarg;
|
|
- iov[1].iov_len = sizeof(outarg);
|
|
+ iov[1].iov_base = &outarg;
|
|
+ iov[1].iov_len = sizeof(outarg);
|
|
|
|
- err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
|
|
- if (err) {
|
|
- pthread_mutex_lock(&se->lock);
|
|
- list_del_nreq(&rreq->nreq);
|
|
- pthread_mutex_unlock(&se->lock);
|
|
- free(rreq);
|
|
- }
|
|
+ err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
|
|
+ if (err) {
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ list_del_nreq(&rreq->nreq);
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
+ free(rreq);
|
|
+ }
|
|
|
|
- return err;
|
|
+ return err;
|
|
}
|
|
|
|
void *fuse_req_userdata(fuse_req_t req)
|
|
{
|
|
- return req->se->userdata;
|
|
+ return req->se->userdata;
|
|
}
|
|
|
|
const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
|
|
{
|
|
- return &req->ctx;
|
|
+ return &req->ctx;
|
|
}
|
|
|
|
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
|
|
- void *data)
|
|
+ void *data)
|
|
{
|
|
- pthread_mutex_lock(&req->lock);
|
|
- pthread_mutex_lock(&req->se->lock);
|
|
- req->u.ni.func = func;
|
|
- req->u.ni.data = data;
|
|
- pthread_mutex_unlock(&req->se->lock);
|
|
- if (req->interrupted && func)
|
|
- func(req, data);
|
|
- pthread_mutex_unlock(&req->lock);
|
|
+ pthread_mutex_lock(&req->lock);
|
|
+ pthread_mutex_lock(&req->se->lock);
|
|
+ req->u.ni.func = func;
|
|
+ req->u.ni.data = data;
|
|
+ pthread_mutex_unlock(&req->se->lock);
|
|
+ if (req->interrupted && func) {
|
|
+ func(req, data);
|
|
+ }
|
|
+ pthread_mutex_unlock(&req->lock);
|
|
}
|
|
|
|
int fuse_req_interrupted(fuse_req_t req)
|
|
{
|
|
- int interrupted;
|
|
+ int interrupted;
|
|
|
|
- pthread_mutex_lock(&req->se->lock);
|
|
- interrupted = req->interrupted;
|
|
- pthread_mutex_unlock(&req->se->lock);
|
|
+ pthread_mutex_lock(&req->se->lock);
|
|
+ interrupted = req->interrupted;
|
|
+ pthread_mutex_unlock(&req->se->lock);
|
|
|
|
- return interrupted;
|
|
+ return interrupted;
|
|
}
|
|
|
|
static struct {
|
|
- void (*func)(fuse_req_t, fuse_ino_t, const void *);
|
|
- const char *name;
|
|
+ void (*func)(fuse_req_t, fuse_ino_t, const void *);
|
|
+ const char *name;
|
|
} fuse_ll_ops[] = {
|
|
- [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
|
|
- [FUSE_FORGET] = { do_forget, "FORGET" },
|
|
- [FUSE_GETATTR] = { do_getattr, "GETATTR" },
|
|
- [FUSE_SETATTR] = { do_setattr, "SETATTR" },
|
|
- [FUSE_READLINK] = { do_readlink, "READLINK" },
|
|
- [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
|
|
- [FUSE_MKNOD] = { do_mknod, "MKNOD" },
|
|
- [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
|
|
- [FUSE_UNLINK] = { do_unlink, "UNLINK" },
|
|
- [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
|
|
- [FUSE_RENAME] = { do_rename, "RENAME" },
|
|
- [FUSE_LINK] = { do_link, "LINK" },
|
|
- [FUSE_OPEN] = { do_open, "OPEN" },
|
|
- [FUSE_READ] = { do_read, "READ" },
|
|
- [FUSE_WRITE] = { do_write, "WRITE" },
|
|
- [FUSE_STATFS] = { do_statfs, "STATFS" },
|
|
- [FUSE_RELEASE] = { do_release, "RELEASE" },
|
|
- [FUSE_FSYNC] = { do_fsync, "FSYNC" },
|
|
- [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
|
|
- [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
|
|
- [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
|
|
- [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
|
|
- [FUSE_FLUSH] = { do_flush, "FLUSH" },
|
|
- [FUSE_INIT] = { do_init, "INIT" },
|
|
- [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
|
|
- [FUSE_READDIR] = { do_readdir, "READDIR" },
|
|
- [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
|
|
- [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
|
|
- [FUSE_GETLK] = { do_getlk, "GETLK" },
|
|
- [FUSE_SETLK] = { do_setlk, "SETLK" },
|
|
- [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
|
|
- [FUSE_ACCESS] = { do_access, "ACCESS" },
|
|
- [FUSE_CREATE] = { do_create, "CREATE" },
|
|
- [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
|
|
- [FUSE_BMAP] = { do_bmap, "BMAP" },
|
|
- [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
|
|
- [FUSE_POLL] = { do_poll, "POLL" },
|
|
- [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
|
|
- [FUSE_DESTROY] = { do_destroy, "DESTROY" },
|
|
- [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
|
|
- [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
|
|
- [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
|
|
- [FUSE_RENAME2] = { do_rename2, "RENAME2" },
|
|
- [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
|
|
- [FUSE_LSEEK] = { do_lseek, "LSEEK" },
|
|
+ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
|
|
+ [FUSE_FORGET] = { do_forget, "FORGET" },
|
|
+ [FUSE_GETATTR] = { do_getattr, "GETATTR" },
|
|
+ [FUSE_SETATTR] = { do_setattr, "SETATTR" },
|
|
+ [FUSE_READLINK] = { do_readlink, "READLINK" },
|
|
+ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
|
|
+ [FUSE_MKNOD] = { do_mknod, "MKNOD" },
|
|
+ [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
|
|
+ [FUSE_UNLINK] = { do_unlink, "UNLINK" },
|
|
+ [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
|
|
+ [FUSE_RENAME] = { do_rename, "RENAME" },
|
|
+ [FUSE_LINK] = { do_link, "LINK" },
|
|
+ [FUSE_OPEN] = { do_open, "OPEN" },
|
|
+ [FUSE_READ] = { do_read, "READ" },
|
|
+ [FUSE_WRITE] = { do_write, "WRITE" },
|
|
+ [FUSE_STATFS] = { do_statfs, "STATFS" },
|
|
+ [FUSE_RELEASE] = { do_release, "RELEASE" },
|
|
+ [FUSE_FSYNC] = { do_fsync, "FSYNC" },
|
|
+ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
|
|
+ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
|
|
+ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
|
|
+ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
|
|
+ [FUSE_FLUSH] = { do_flush, "FLUSH" },
|
|
+ [FUSE_INIT] = { do_init, "INIT" },
|
|
+ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
|
|
+ [FUSE_READDIR] = { do_readdir, "READDIR" },
|
|
+ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
|
|
+ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
|
|
+ [FUSE_GETLK] = { do_getlk, "GETLK" },
|
|
+ [FUSE_SETLK] = { do_setlk, "SETLK" },
|
|
+ [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
|
|
+ [FUSE_ACCESS] = { do_access, "ACCESS" },
|
|
+ [FUSE_CREATE] = { do_create, "CREATE" },
|
|
+ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
|
|
+ [FUSE_BMAP] = { do_bmap, "BMAP" },
|
|
+ [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
|
|
+ [FUSE_POLL] = { do_poll, "POLL" },
|
|
+ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
|
|
+ [FUSE_DESTROY] = { do_destroy, "DESTROY" },
|
|
+ [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" },
|
|
+ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
|
|
+ [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" },
|
|
+ [FUSE_RENAME2] = { do_rename2, "RENAME2" },
|
|
+ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
|
|
+ [FUSE_LSEEK] = { do_lseek, "LSEEK" },
|
|
};
|
|
|
|
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
|
|
|
|
static const char *opname(enum fuse_opcode opcode)
|
|
{
|
|
- if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
|
|
- return "???";
|
|
- else
|
|
- return fuse_ll_ops[opcode].name;
|
|
+ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) {
|
|
+ return "???";
|
|
+ } else {
|
|
+ return fuse_ll_ops[opcode].name;
|
|
+ }
|
|
}
|
|
|
|
void fuse_session_process_buf(struct fuse_session *se,
|
|
- const struct fuse_buf *buf)
|
|
+ const struct fuse_buf *buf)
|
|
{
|
|
- fuse_session_process_buf_int(se, buf, NULL);
|
|
+ fuse_session_process_buf_int(se, buf, NULL);
|
|
}
|
|
|
|
void fuse_session_process_buf_int(struct fuse_session *se,
|
|
- const struct fuse_buf *buf, struct fuse_chan *ch)
|
|
-{
|
|
- struct fuse_in_header *in;
|
|
- const void *inarg;
|
|
- struct fuse_req *req;
|
|
- int err;
|
|
-
|
|
- in = buf->mem;
|
|
-
|
|
- if (se->debug) {
|
|
- fuse_log(FUSE_LOG_DEBUG,
|
|
- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
|
|
- (unsigned long long) in->unique,
|
|
- opname((enum fuse_opcode) in->opcode), in->opcode,
|
|
- (unsigned long long) in->nodeid, buf->size, in->pid);
|
|
- }
|
|
-
|
|
- req = fuse_ll_alloc_req(se);
|
|
- if (req == NULL) {
|
|
- struct fuse_out_header out = {
|
|
- .unique = in->unique,
|
|
- .error = -ENOMEM,
|
|
- };
|
|
- struct iovec iov = {
|
|
- .iov_base = &out,
|
|
- .iov_len = sizeof(struct fuse_out_header),
|
|
- };
|
|
-
|
|
- fuse_send_msg(se, ch, &iov, 1);
|
|
- return;
|
|
- }
|
|
-
|
|
- req->unique = in->unique;
|
|
- req->ctx.uid = in->uid;
|
|
- req->ctx.gid = in->gid;
|
|
- req->ctx.pid = in->pid;
|
|
- req->ch = ch;
|
|
-
|
|
- err = EIO;
|
|
- if (!se->got_init) {
|
|
- enum fuse_opcode expected;
|
|
-
|
|
- expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
|
|
- if (in->opcode != expected)
|
|
- goto reply_err;
|
|
- } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
|
|
- goto reply_err;
|
|
-
|
|
- err = EACCES;
|
|
- /* Implement -o allow_root */
|
|
- if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
|
|
- in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
|
|
- in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
|
|
- in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
|
|
- in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
|
|
- in->opcode != FUSE_NOTIFY_REPLY &&
|
|
- in->opcode != FUSE_READDIRPLUS)
|
|
- goto reply_err;
|
|
-
|
|
- err = ENOSYS;
|
|
- if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
|
|
- goto reply_err;
|
|
- if (in->opcode != FUSE_INTERRUPT) {
|
|
- struct fuse_req *intr;
|
|
- pthread_mutex_lock(&se->lock);
|
|
- intr = check_interrupt(se, req);
|
|
- list_add_req(req, &se->list);
|
|
- pthread_mutex_unlock(&se->lock);
|
|
- if (intr)
|
|
- fuse_reply_err(intr, EAGAIN);
|
|
- }
|
|
-
|
|
- inarg = (void *) &in[1];
|
|
- if (in->opcode == FUSE_WRITE && se->op.write_buf)
|
|
- do_write_buf(req, in->nodeid, inarg, buf);
|
|
- else if (in->opcode == FUSE_NOTIFY_REPLY)
|
|
- do_notify_reply(req, in->nodeid, inarg, buf);
|
|
- else
|
|
- fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
|
|
-
|
|
- return;
|
|
+ const struct fuse_buf *buf,
|
|
+ struct fuse_chan *ch)
|
|
+{
|
|
+ struct fuse_in_header *in;
|
|
+ const void *inarg;
|
|
+ struct fuse_req *req;
|
|
+ int err;
|
|
+
|
|
+ in = buf->mem;
|
|
+
|
|
+ if (se->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, "
|
|
+ "pid: %u\n",
|
|
+ (unsigned long long)in->unique,
|
|
+ opname((enum fuse_opcode)in->opcode), in->opcode,
|
|
+ (unsigned long long)in->nodeid, buf->size, in->pid);
|
|
+ }
|
|
+
|
|
+ req = fuse_ll_alloc_req(se);
|
|
+ if (req == NULL) {
|
|
+ struct fuse_out_header out = {
|
|
+ .unique = in->unique,
|
|
+ .error = -ENOMEM,
|
|
+ };
|
|
+ struct iovec iov = {
|
|
+ .iov_base = &out,
|
|
+ .iov_len = sizeof(struct fuse_out_header),
|
|
+ };
|
|
+
|
|
+ fuse_send_msg(se, ch, &iov, 1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ req->unique = in->unique;
|
|
+ req->ctx.uid = in->uid;
|
|
+ req->ctx.gid = in->gid;
|
|
+ req->ctx.pid = in->pid;
|
|
+ req->ch = ch;
|
|
+
|
|
+ err = EIO;
|
|
+ if (!se->got_init) {
|
|
+ enum fuse_opcode expected;
|
|
+
|
|
+ expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
|
|
+ if (in->opcode != expected) {
|
|
+ goto reply_err;
|
|
+ }
|
|
+ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) {
|
|
+ goto reply_err;
|
|
+ }
|
|
+
|
|
+ err = EACCES;
|
|
+ /* Implement -o allow_root */
|
|
+ if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
|
|
+ in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
|
|
+ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
|
|
+ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
|
|
+ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
|
|
+ in->opcode != FUSE_NOTIFY_REPLY && in->opcode != FUSE_READDIRPLUS) {
|
|
+ goto reply_err;
|
|
+ }
|
|
+
|
|
+ err = ENOSYS;
|
|
+ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) {
|
|
+ goto reply_err;
|
|
+ }
|
|
+ if (in->opcode != FUSE_INTERRUPT) {
|
|
+ struct fuse_req *intr;
|
|
+ pthread_mutex_lock(&se->lock);
|
|
+ intr = check_interrupt(se, req);
|
|
+ list_add_req(req, &se->list);
|
|
+ pthread_mutex_unlock(&se->lock);
|
|
+ if (intr) {
|
|
+ fuse_reply_err(intr, EAGAIN);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ inarg = (void *)&in[1];
|
|
+ if (in->opcode == FUSE_WRITE && se->op.write_buf) {
|
|
+ do_write_buf(req, in->nodeid, inarg, buf);
|
|
+ } else if (in->opcode == FUSE_NOTIFY_REPLY) {
|
|
+ do_notify_reply(req, in->nodeid, inarg, buf);
|
|
+ } else {
|
|
+ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
|
|
+ }
|
|
+
|
|
+ return;
|
|
|
|
reply_err:
|
|
- fuse_reply_err(req, err);
|
|
+ fuse_reply_err(req, err);
|
|
}
|
|
|
|
-#define LL_OPTION(n,o,v) \
|
|
- { n, offsetof(struct fuse_session, o), v }
|
|
+#define LL_OPTION(n, o, v) \
|
|
+ { \
|
|
+ n, offsetof(struct fuse_session, o), v \
|
|
+ }
|
|
|
|
static const struct fuse_opt fuse_ll_opts[] = {
|
|
- LL_OPTION("debug", debug, 1),
|
|
- LL_OPTION("-d", debug, 1),
|
|
- LL_OPTION("--debug", debug, 1),
|
|
- LL_OPTION("allow_root", deny_others, 1),
|
|
- FUSE_OPT_END
|
|
+ LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1),
|
|
+ LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1),
|
|
+ FUSE_OPT_END
|
|
};
|
|
|
|
void fuse_lowlevel_version(void)
|
|
{
|
|
- printf("using FUSE kernel interface version %i.%i\n",
|
|
- FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
|
|
+ printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION,
|
|
+ FUSE_KERNEL_MINOR_VERSION);
|
|
}
|
|
|
|
void fuse_lowlevel_help(void)
|
|
{
|
|
- /* These are not all options, but the ones that are
|
|
- potentially of interest to an end-user */
|
|
- printf(
|
|
-" -o allow_root allow access by root\n"
|
|
-);
|
|
+ /*
|
|
+ * These are not all options, but the ones that are
|
|
+ * potentially of interest to an end-user
|
|
+ */
|
|
+ printf(" -o allow_root allow access by root\n");
|
|
}
|
|
|
|
void fuse_session_destroy(struct fuse_session *se)
|
|
{
|
|
- if (se->got_init && !se->got_destroy) {
|
|
- if (se->op.destroy)
|
|
- se->op.destroy(se->userdata);
|
|
- }
|
|
- pthread_mutex_destroy(&se->lock);
|
|
- free(se->cuse_data);
|
|
- if (se->fd != -1)
|
|
- close(se->fd);
|
|
- free(se);
|
|
+ if (se->got_init && !se->got_destroy) {
|
|
+ if (se->op.destroy) {
|
|
+ se->op.destroy(se->userdata);
|
|
+ }
|
|
+ }
|
|
+ pthread_mutex_destroy(&se->lock);
|
|
+ free(se->cuse_data);
|
|
+ if (se->fd != -1) {
|
|
+ close(se->fd);
|
|
+ }
|
|
+ free(se);
|
|
}
|
|
|
|
|
|
struct fuse_session *fuse_session_new(struct fuse_args *args,
|
|
- const struct fuse_lowlevel_ops *op,
|
|
- size_t op_size, void *userdata)
|
|
-{
|
|
- struct fuse_session *se;
|
|
-
|
|
- if (sizeof(struct fuse_lowlevel_ops) < op_size) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
|
|
- op_size = sizeof(struct fuse_lowlevel_ops);
|
|
- }
|
|
-
|
|
- if (args->argc == 0) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
|
|
- if (se == NULL) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
|
|
- goto out1;
|
|
- }
|
|
- se->fd = -1;
|
|
- se->conn.max_write = UINT_MAX;
|
|
- se->conn.max_readahead = UINT_MAX;
|
|
-
|
|
- /* Parse options */
|
|
- if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
|
|
- goto out2;
|
|
- if(args->argc == 1 &&
|
|
- args->argv[0][0] == '-') {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
|
|
- "will be ignored\n");
|
|
- } else if (args->argc != 1) {
|
|
- int i;
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
|
|
- for(i = 1; i < args->argc-1; i++)
|
|
- fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
|
|
- fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
|
|
- goto out4;
|
|
- }
|
|
-
|
|
- se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() +
|
|
- FUSE_BUFFER_HEADER_SIZE;
|
|
-
|
|
- list_init_req(&se->list);
|
|
- list_init_req(&se->interrupts);
|
|
- list_init_nreq(&se->notify_list);
|
|
- se->notify_ctr = 1;
|
|
- fuse_mutex_init(&se->lock);
|
|
-
|
|
- memcpy(&se->op, op, op_size);
|
|
- se->owner = getuid();
|
|
- se->userdata = userdata;
|
|
-
|
|
- return se;
|
|
+ const struct fuse_lowlevel_ops *op,
|
|
+ size_t op_size, void *userdata)
|
|
+{
|
|
+ struct fuse_session *se;
|
|
+
|
|
+ if (sizeof(struct fuse_lowlevel_ops) < op_size) {
|
|
+ fuse_log(
|
|
+ FUSE_LOG_ERR,
|
|
+ "fuse: warning: library too old, some operations may not work\n");
|
|
+ op_size = sizeof(struct fuse_lowlevel_ops);
|
|
+ }
|
|
+
|
|
+ if (args->argc == 0) {
|
|
+ fuse_log(FUSE_LOG_ERR,
|
|
+ "fuse: empty argv passed to fuse_session_new().\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ se = (struct fuse_session *)calloc(1, sizeof(struct fuse_session));
|
|
+ if (se == NULL) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
|
|
+ goto out1;
|
|
+ }
|
|
+ se->fd = -1;
|
|
+ se->conn.max_write = UINT_MAX;
|
|
+ se->conn.max_readahead = UINT_MAX;
|
|
+
|
|
+ /* Parse options */
|
|
+ if (fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) {
|
|
+ goto out2;
|
|
+ }
|
|
+ if (args->argc == 1 && args->argv[0][0] == '-') {
|
|
+ fuse_log(FUSE_LOG_ERR,
|
|
+ "fuse: warning: argv[0] looks like an option, but "
|
|
+ "will be ignored\n");
|
|
+ } else if (args->argc != 1) {
|
|
+ int i;
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
|
|
+ for (i = 1; i < args->argc - 1; i++) {
|
|
+ fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
|
|
+ }
|
|
+ fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
|
|
+ goto out4;
|
|
+ }
|
|
+
|
|
+ se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE;
|
|
+
|
|
+ list_init_req(&se->list);
|
|
+ list_init_req(&se->interrupts);
|
|
+ list_init_nreq(&se->notify_list);
|
|
+ se->notify_ctr = 1;
|
|
+ fuse_mutex_init(&se->lock);
|
|
+
|
|
+ memcpy(&se->op, op, op_size);
|
|
+ se->owner = getuid();
|
|
+ se->userdata = userdata;
|
|
+
|
|
+ return se;
|
|
|
|
out4:
|
|
- fuse_opt_free_args(args);
|
|
+ fuse_opt_free_args(args);
|
|
out2:
|
|
- free(se);
|
|
+ free(se);
|
|
out1:
|
|
- return NULL;
|
|
+ return NULL;
|
|
}
|
|
|
|
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
|
|
{
|
|
- int fd;
|
|
-
|
|
- /*
|
|
- * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
|
|
- * would ensue.
|
|
- */
|
|
- do {
|
|
- fd = open("/dev/null", O_RDWR);
|
|
- if (fd > 2)
|
|
- close(fd);
|
|
- } while (fd >= 0 && fd <= 2);
|
|
-
|
|
- /*
|
|
- * To allow FUSE daemons to run without privileges, the caller may open
|
|
- * /dev/fuse before launching the file system and pass on the file
|
|
- * descriptor by specifying /dev/fd/N as the mount point. Note that the
|
|
- * parent process takes care of performing the mount in this case.
|
|
- */
|
|
- fd = fuse_mnt_parse_fuse_fd(mountpoint);
|
|
- if (fd != -1) {
|
|
- if (fcntl(fd, F_GETFD) == -1) {
|
|
- fuse_log(FUSE_LOG_ERR,
|
|
- "fuse: Invalid file descriptor /dev/fd/%u\n",
|
|
- fd);
|
|
- return -1;
|
|
- }
|
|
- se->fd = fd;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* Open channel */
|
|
- fd = fuse_kern_mount(mountpoint, se->mo);
|
|
- if (fd == -1)
|
|
- return -1;
|
|
- se->fd = fd;
|
|
-
|
|
- /* Save mountpoint */
|
|
- se->mountpoint = strdup(mountpoint);
|
|
- if (se->mountpoint == NULL)
|
|
- goto error_out;
|
|
-
|
|
- return 0;
|
|
+ int fd;
|
|
+
|
|
+ /*
|
|
+ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
|
|
+ * would ensue.
|
|
+ */
|
|
+ do {
|
|
+ fd = open("/dev/null", O_RDWR);
|
|
+ if (fd > 2) {
|
|
+ close(fd);
|
|
+ }
|
|
+ } while (fd >= 0 && fd <= 2);
|
|
+
|
|
+ /*
|
|
+ * To allow FUSE daemons to run without privileges, the caller may open
|
|
+ * /dev/fuse before launching the file system and pass on the file
|
|
+ * descriptor by specifying /dev/fd/N as the mount point. Note that the
|
|
+ * parent process takes care of performing the mount in this case.
|
|
+ */
|
|
+ fd = fuse_mnt_parse_fuse_fd(mountpoint);
|
|
+ if (fd != -1) {
|
|
+ if (fcntl(fd, F_GETFD) == -1) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n",
|
|
+ fd);
|
|
+ return -1;
|
|
+ }
|
|
+ se->fd = fd;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Open channel */
|
|
+ fd = fuse_kern_mount(mountpoint, se->mo);
|
|
+ if (fd == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ se->fd = fd;
|
|
+
|
|
+ /* Save mountpoint */
|
|
+ se->mountpoint = strdup(mountpoint);
|
|
+ if (se->mountpoint == NULL) {
|
|
+ goto error_out;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
|
|
error_out:
|
|
- fuse_kern_unmount(mountpoint, fd);
|
|
- return -1;
|
|
+ fuse_kern_unmount(mountpoint, fd);
|
|
+ return -1;
|
|
}
|
|
|
|
int fuse_session_fd(struct fuse_session *se)
|
|
{
|
|
- return se->fd;
|
|
+ return se->fd;
|
|
}
|
|
|
|
void fuse_session_unmount(struct fuse_session *se)
|
|
@@ -2384,61 +2519,66 @@ void fuse_session_unmount(struct fuse_session *se)
|
|
#ifdef linux
|
|
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
|
|
{
|
|
- char *buf;
|
|
- size_t bufsize = 1024;
|
|
- char path[128];
|
|
- int ret;
|
|
- int fd;
|
|
- unsigned long pid = req->ctx.pid;
|
|
- char *s;
|
|
+ char *buf;
|
|
+ size_t bufsize = 1024;
|
|
+ char path[128];
|
|
+ int ret;
|
|
+ int fd;
|
|
+ unsigned long pid = req->ctx.pid;
|
|
+ char *s;
|
|
|
|
- sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
|
|
+ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
|
|
|
|
retry:
|
|
- buf = malloc(bufsize);
|
|
- if (buf == NULL)
|
|
- return -ENOMEM;
|
|
-
|
|
- ret = -EIO;
|
|
- fd = open(path, O_RDONLY);
|
|
- if (fd == -1)
|
|
- goto out_free;
|
|
-
|
|
- ret = read(fd, buf, bufsize);
|
|
- close(fd);
|
|
- if (ret < 0) {
|
|
- ret = -EIO;
|
|
- goto out_free;
|
|
- }
|
|
-
|
|
- if ((size_t)ret == bufsize) {
|
|
- free(buf);
|
|
- bufsize *= 4;
|
|
- goto retry;
|
|
- }
|
|
-
|
|
- ret = -EIO;
|
|
- s = strstr(buf, "\nGroups:");
|
|
- if (s == NULL)
|
|
- goto out_free;
|
|
-
|
|
- s += 8;
|
|
- ret = 0;
|
|
- while (1) {
|
|
- char *end;
|
|
- unsigned long val = strtoul(s, &end, 0);
|
|
- if (end == s)
|
|
- break;
|
|
-
|
|
- s = end;
|
|
- if (ret < size)
|
|
- list[ret] = val;
|
|
- ret++;
|
|
- }
|
|
+ buf = malloc(bufsize);
|
|
+ if (buf == NULL) {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = -EIO;
|
|
+ fd = open(path, O_RDONLY);
|
|
+ if (fd == -1) {
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ ret = read(fd, buf, bufsize);
|
|
+ close(fd);
|
|
+ if (ret < 0) {
|
|
+ ret = -EIO;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ if ((size_t)ret == bufsize) {
|
|
+ free(buf);
|
|
+ bufsize *= 4;
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ ret = -EIO;
|
|
+ s = strstr(buf, "\nGroups:");
|
|
+ if (s == NULL) {
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ s += 8;
|
|
+ ret = 0;
|
|
+ while (1) {
|
|
+ char *end;
|
|
+ unsigned long val = strtoul(s, &end, 0);
|
|
+ if (end == s) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ s = end;
|
|
+ if (ret < size) {
|
|
+ list[ret] = val;
|
|
+ }
|
|
+ ret++;
|
|
+ }
|
|
|
|
out_free:
|
|
- free(buf);
|
|
- return ret;
|
|
+ free(buf);
|
|
+ return ret;
|
|
}
|
|
#else /* linux */
|
|
/*
|
|
@@ -2446,23 +2586,25 @@ out_free:
|
|
*/
|
|
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
|
|
{
|
|
- (void) req; (void) size; (void) list;
|
|
- return -ENOSYS;
|
|
+ (void)req;
|
|
+ (void)size;
|
|
+ (void)list;
|
|
+ return -ENOSYS;
|
|
}
|
|
#endif
|
|
|
|
void fuse_session_exit(struct fuse_session *se)
|
|
{
|
|
- se->exited = 1;
|
|
+ se->exited = 1;
|
|
}
|
|
|
|
void fuse_session_reset(struct fuse_session *se)
|
|
{
|
|
- se->exited = 0;
|
|
- se->error = 0;
|
|
+ se->exited = 0;
|
|
+ se->error = 0;
|
|
}
|
|
|
|
int fuse_session_exited(struct fuse_session *se)
|
|
{
|
|
- return se->exited;
|
|
+ return se->exited;
|
|
}
|
|
diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h
|
|
index 6b1adfcfd1..adb9054bb1 100644
|
|
--- a/tools/virtiofsd/fuse_lowlevel.h
|
|
+++ b/tools/virtiofsd/fuse_lowlevel.h
|
|
@@ -1,15 +1,16 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
#ifndef FUSE_LOWLEVEL_H_
|
|
#define FUSE_LOWLEVEL_H_
|
|
|
|
-/** @file
|
|
+/**
|
|
+ * @file
|
|
*
|
|
* Low level API
|
|
*
|
|
@@ -24,16 +25,16 @@
|
|
|
|
#include "fuse_common.h"
|
|
|
|
-#include <utime.h>
|
|
#include <fcntl.h>
|
|
-#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.h>
|
|
+#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
+#include <utime.h>
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Miscellaneous definitions *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Miscellaneous definitions
|
|
+ */
|
|
|
|
/** The node ID of the root inode */
|
|
#define FUSE_ROOT_ID 1
|
|
@@ -53,47 +54,54 @@ struct fuse_session;
|
|
|
|
/** Directory entry parameters supplied to fuse_reply_entry() */
|
|
struct fuse_entry_param {
|
|
- /** Unique inode number
|
|
- *
|
|
- * In lookup, zero means negative entry (from version 2.5)
|
|
- * Returning ENOENT also means negative entry, but by setting zero
|
|
- * ino the kernel may cache negative entries for entry_timeout
|
|
- * seconds.
|
|
- */
|
|
- fuse_ino_t ino;
|
|
-
|
|
- /** Generation number for this entry.
|
|
- *
|
|
- * If the file system will be exported over NFS, the
|
|
- * ino/generation pairs need to be unique over the file
|
|
- * system's lifetime (rather than just the mount time). So if
|
|
- * the file system reuses an inode after it has been deleted,
|
|
- * it must assign a new, previously unused generation number
|
|
- * to the inode at the same time.
|
|
- *
|
|
- */
|
|
- uint64_t generation;
|
|
-
|
|
- /** Inode attributes.
|
|
- *
|
|
- * Even if attr_timeout == 0, attr must be correct. For example,
|
|
- * for open(), FUSE uses attr.st_size from lookup() to determine
|
|
- * how many bytes to request. If this value is not correct,
|
|
- * incorrect data will be returned.
|
|
- */
|
|
- struct stat attr;
|
|
-
|
|
- /** Validity timeout (in seconds) for inode attributes. If
|
|
- attributes only change as a result of requests that come
|
|
- through the kernel, this should be set to a very large
|
|
- value. */
|
|
- double attr_timeout;
|
|
-
|
|
- /** Validity timeout (in seconds) for the name. If directory
|
|
- entries are changed/deleted only as a result of requests
|
|
- that come through the kernel, this should be set to a very
|
|
- large value. */
|
|
- double entry_timeout;
|
|
+ /**
|
|
+ * Unique inode number
|
|
+ *
|
|
+ * In lookup, zero means negative entry (from version 2.5)
|
|
+ * Returning ENOENT also means negative entry, but by setting zero
|
|
+ * ino the kernel may cache negative entries for entry_timeout
|
|
+ * seconds.
|
|
+ */
|
|
+ fuse_ino_t ino;
|
|
+
|
|
+ /**
|
|
+ * Generation number for this entry.
|
|
+ *
|
|
+ * If the file system will be exported over NFS, the
|
|
+ * ino/generation pairs need to be unique over the file
|
|
+ * system's lifetime (rather than just the mount time). So if
|
|
+ * the file system reuses an inode after it has been deleted,
|
|
+ * it must assign a new, previously unused generation number
|
|
+ * to the inode at the same time.
|
|
+ *
|
|
+ */
|
|
+ uint64_t generation;
|
|
+
|
|
+ /**
|
|
+ * Inode attributes.
|
|
+ *
|
|
+ * Even if attr_timeout == 0, attr must be correct. For example,
|
|
+ * for open(), FUSE uses attr.st_size from lookup() to determine
|
|
+ * how many bytes to request. If this value is not correct,
|
|
+ * incorrect data will be returned.
|
|
+ */
|
|
+ struct stat attr;
|
|
+
|
|
+ /**
|
|
+ * Validity timeout (in seconds) for inode attributes. If
|
|
+ * attributes only change as a result of requests that come
|
|
+ * through the kernel, this should be set to a very large
|
|
+ * value.
|
|
+ */
|
|
+ double attr_timeout;
|
|
+
|
|
+ /**
|
|
+ * Validity timeout (in seconds) for the name. If directory
|
|
+ * entries are changed/deleted only as a result of requests
|
|
+ * that come through the kernel, this should be set to a very
|
|
+ * large value.
|
|
+ */
|
|
+ double entry_timeout;
|
|
};
|
|
|
|
/**
|
|
@@ -105,38 +113,38 @@ struct fuse_entry_param {
|
|
* there is no valid uid/pid/gid that could be reported.
|
|
*/
|
|
struct fuse_ctx {
|
|
- /** User ID of the calling process */
|
|
- uid_t uid;
|
|
+ /** User ID of the calling process */
|
|
+ uid_t uid;
|
|
|
|
- /** Group ID of the calling process */
|
|
- gid_t gid;
|
|
+ /** Group ID of the calling process */
|
|
+ gid_t gid;
|
|
|
|
- /** Thread ID of the calling process */
|
|
- pid_t pid;
|
|
+ /** Thread ID of the calling process */
|
|
+ pid_t pid;
|
|
|
|
- /** Umask of the calling process */
|
|
- mode_t umask;
|
|
+ /** Umask of the calling process */
|
|
+ mode_t umask;
|
|
};
|
|
|
|
struct fuse_forget_data {
|
|
- fuse_ino_t ino;
|
|
- uint64_t nlookup;
|
|
+ fuse_ino_t ino;
|
|
+ uint64_t nlookup;
|
|
};
|
|
|
|
/* 'to_set' flags in setattr */
|
|
-#define FUSE_SET_ATTR_MODE (1 << 0)
|
|
-#define FUSE_SET_ATTR_UID (1 << 1)
|
|
-#define FUSE_SET_ATTR_GID (1 << 2)
|
|
-#define FUSE_SET_ATTR_SIZE (1 << 3)
|
|
-#define FUSE_SET_ATTR_ATIME (1 << 4)
|
|
-#define FUSE_SET_ATTR_MTIME (1 << 5)
|
|
-#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
|
|
-#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
|
|
-#define FUSE_SET_ATTR_CTIME (1 << 10)
|
|
-
|
|
-/* ----------------------------------------------------------- *
|
|
- * Request methods and replies *
|
|
- * ----------------------------------------------------------- */
|
|
+#define FUSE_SET_ATTR_MODE (1 << 0)
|
|
+#define FUSE_SET_ATTR_UID (1 << 1)
|
|
+#define FUSE_SET_ATTR_GID (1 << 2)
|
|
+#define FUSE_SET_ATTR_SIZE (1 << 3)
|
|
+#define FUSE_SET_ATTR_ATIME (1 << 4)
|
|
+#define FUSE_SET_ATTR_MTIME (1 << 5)
|
|
+#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
|
|
+#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
|
|
+#define FUSE_SET_ATTR_CTIME (1 << 10)
|
|
+
|
|
+/*
|
|
+ * Request methods and replies
|
|
+ */
|
|
|
|
/**
|
|
* Low level filesystem operations
|
|
@@ -166,1075 +174,1069 @@ struct fuse_forget_data {
|
|
* this file will not be called.
|
|
*/
|
|
struct fuse_lowlevel_ops {
|
|
- /**
|
|
- * Initialize filesystem
|
|
- *
|
|
- * This function is called when libfuse establishes
|
|
- * communication with the FUSE kernel module. The file system
|
|
- * should use this module to inspect and/or modify the
|
|
- * connection parameters provided in the `conn` structure.
|
|
- *
|
|
- * Note that some parameters may be overwritten by options
|
|
- * passed to fuse_session_new() which take precedence over the
|
|
- * values set in this handler.
|
|
- *
|
|
- * There's no reply to this function
|
|
- *
|
|
- * @param userdata the user data passed to fuse_session_new()
|
|
- */
|
|
- void (*init) (void *userdata, struct fuse_conn_info *conn);
|
|
-
|
|
- /**
|
|
- * Clean up filesystem.
|
|
- *
|
|
- * Called on filesystem exit. When this method is called, the
|
|
- * connection to the kernel may be gone already, so that eg. calls
|
|
- * to fuse_lowlevel_notify_* will fail.
|
|
- *
|
|
- * There's no reply to this function
|
|
- *
|
|
- * @param userdata the user data passed to fuse_session_new()
|
|
- */
|
|
- void (*destroy) (void *userdata);
|
|
-
|
|
- /**
|
|
- * Look up a directory entry by name and get its attributes.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_entry
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name the name to look up
|
|
- */
|
|
- void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
-
|
|
- /**
|
|
- * Forget about an inode
|
|
- *
|
|
- * This function is called when the kernel removes an inode
|
|
- * from its internal caches.
|
|
- *
|
|
- * The inode's lookup count increases by one for every call to
|
|
- * fuse_reply_entry and fuse_reply_create. The nlookup parameter
|
|
- * indicates by how much the lookup count should be decreased.
|
|
- *
|
|
- * Inodes with a non-zero lookup count may receive request from
|
|
- * the kernel even after calls to unlink, rmdir or (when
|
|
- * overwriting an existing file) rename. Filesystems must handle
|
|
- * such requests properly and it is recommended to defer removal
|
|
- * of the inode until the lookup count reaches zero. Calls to
|
|
- * unlink, rmdir or rename will be followed closely by forget
|
|
- * unless the file or directory is open, in which case the
|
|
- * kernel issues forget only after the release or releasedir
|
|
- * calls.
|
|
- *
|
|
- * Note that if a file system will be exported over NFS the
|
|
- * inodes lifetime must extend even beyond forget. See the
|
|
- * generation field in struct fuse_entry_param above.
|
|
- *
|
|
- * On unmount the lookup count for all inodes implicitly drops
|
|
- * to zero. It is not guaranteed that the file system will
|
|
- * receive corresponding forget messages for the affected
|
|
- * inodes.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_none
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param nlookup the number of lookups to forget
|
|
- */
|
|
- void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
|
|
-
|
|
- /**
|
|
- * Get file attributes.
|
|
- *
|
|
- * If writeback caching is enabled, the kernel may have a
|
|
- * better idea of a file's length than the FUSE file system
|
|
- * (eg if there has been a write that extended the file size,
|
|
- * but that has not yet been passed to the filesystem.n
|
|
- *
|
|
- * In this case, the st_size value provided by the file system
|
|
- * will be ignored.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_attr
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi for future use, currently always NULL
|
|
- */
|
|
- void (*getattr) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Set file attributes
|
|
- *
|
|
- * In the 'attr' argument only members indicated by the 'to_set'
|
|
- * bitmask contain valid values. Other members contain undefined
|
|
- * values.
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits if the file
|
|
- * size or owner is being changed.
|
|
- *
|
|
- * If the setattr was invoked from the ftruncate() system call
|
|
- * under Linux kernel versions 2.6.15 or later, the fi->fh will
|
|
- * contain the value set by the open method or will be undefined
|
|
- * if the open method didn't set any value. Otherwise (not
|
|
- * ftruncate call, or kernel version earlier than 2.6.15) the fi
|
|
- * parameter will be NULL.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_attr
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param attr the attributes
|
|
- * @param to_set bit mask of attributes which should be set
|
|
- * @param fi file information, or NULL
|
|
- */
|
|
- void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
|
- int to_set, struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Read symbolic link
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_readlink
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- */
|
|
- void (*readlink) (fuse_req_t req, fuse_ino_t ino);
|
|
-
|
|
- /**
|
|
- * Create file node
|
|
- *
|
|
- * Create a regular file, character device, block device, fifo or
|
|
- * socket node.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_entry
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to create
|
|
- * @param mode file type and mode with which to create the new file
|
|
- * @param rdev the device number (only valid if created file is a device)
|
|
- */
|
|
- void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- mode_t mode, dev_t rdev);
|
|
-
|
|
- /**
|
|
- * Create a directory
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_entry
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to create
|
|
- * @param mode with which to create the new file
|
|
- */
|
|
- void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- mode_t mode);
|
|
-
|
|
- /**
|
|
- * Remove a file
|
|
- *
|
|
- * If the file's inode's lookup count is non-zero, the file
|
|
- * system is expected to postpone any removal of the inode
|
|
- * until the lookup count reaches zero (see description of the
|
|
- * forget function).
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to remove
|
|
- */
|
|
- void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
-
|
|
- /**
|
|
- * Remove a directory
|
|
- *
|
|
- * If the directory's inode's lookup count is non-zero, the
|
|
- * file system is expected to postpone any removal of the
|
|
- * inode until the lookup count reaches zero (see description
|
|
- * of the forget function).
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to remove
|
|
- */
|
|
- void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
-
|
|
- /**
|
|
- * Create a symbolic link
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_entry
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param link the contents of the symbolic link
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to create
|
|
- */
|
|
- void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
|
|
- const char *name);
|
|
-
|
|
- /** Rename a file
|
|
- *
|
|
- * If the target exists it should be atomically replaced. If
|
|
- * the target's inode's lookup count is non-zero, the file
|
|
- * system is expected to postpone any removal of the inode
|
|
- * until the lookup count reaches zero (see description of the
|
|
- * forget function).
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EINVAL, i.e. all
|
|
- * future bmap requests will fail with EINVAL without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
|
|
- * RENAME_NOREPLACE is specified, the filesystem must not
|
|
- * overwrite *newname* if it exists and return an error
|
|
- * instead. If `RENAME_EXCHANGE` is specified, the filesystem
|
|
- * must atomically exchange the two files, i.e. both must
|
|
- * exist and neither may be deleted.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the old parent directory
|
|
- * @param name old name
|
|
- * @param newparent inode number of the new parent directory
|
|
- * @param newname new name
|
|
- */
|
|
- void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- fuse_ino_t newparent, const char *newname,
|
|
- unsigned int flags);
|
|
-
|
|
- /**
|
|
- * Create a hard link
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_entry
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the old inode number
|
|
- * @param newparent inode number of the new parent directory
|
|
- * @param newname new name to create
|
|
- */
|
|
- void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
|
- const char *newname);
|
|
-
|
|
- /**
|
|
- * Open a file
|
|
- *
|
|
- * Open flags are available in fi->flags. The following rules
|
|
- * apply.
|
|
- *
|
|
- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be
|
|
- * filtered out / handled by the kernel.
|
|
- *
|
|
- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used
|
|
- * by the filesystem to check if the operation is
|
|
- * permitted. If the ``-o default_permissions`` mount
|
|
- * option is given, this check is already done by the
|
|
- * kernel before calling open() and may thus be omitted by
|
|
- * the filesystem.
|
|
- *
|
|
- * - When writeback caching is enabled, the kernel may send
|
|
- * read requests even for files opened with O_WRONLY. The
|
|
- * filesystem should be prepared to handle this.
|
|
- *
|
|
- * - When writeback caching is disabled, the filesystem is
|
|
- * expected to properly handle the O_APPEND flag and ensure
|
|
- * that each write is appending to the end of the file.
|
|
- *
|
|
- * - When writeback caching is enabled, the kernel will
|
|
- * handle O_APPEND. However, unless all changes to the file
|
|
- * come through the kernel this will not work reliably. The
|
|
- * filesystem should thus either ignore the O_APPEND flag
|
|
- * (and let the kernel handle it), or return an error
|
|
- * (indicating that reliably O_APPEND is not available).
|
|
- *
|
|
- * Filesystem may store an arbitrary file handle (pointer,
|
|
- * index, etc) in fi->fh, and use this in other all other file
|
|
- * operations (read, write, flush, release, fsync).
|
|
- *
|
|
- * Filesystem may also implement stateless file I/O and not store
|
|
- * anything in fi->fh.
|
|
- *
|
|
- * There are also some flags (direct_io, keep_cache) which the
|
|
- * filesystem may set in fi, to change the way the file is opened.
|
|
- * See fuse_file_info structure in <fuse_common.h> for more details.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS
|
|
- * and FUSE_CAP_NO_OPEN_SUPPORT is set in
|
|
- * `fuse_conn_info.capable`, this is treated as success and
|
|
- * future calls to open and release will also succeed without being
|
|
- * sent to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_open
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- */
|
|
- void (*open) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Read data
|
|
- *
|
|
- * Read should send exactly the number of bytes requested except
|
|
- * on EOF or error, otherwise the rest of the data will be
|
|
- * substituted with zeroes. An exception to this is when the file
|
|
- * has been opened in 'direct_io' mode, in which case the return
|
|
- * value of the read system call will reflect the return value of
|
|
- * this operation.
|
|
- *
|
|
- * fi->fh will contain the value set by the open method, or will
|
|
- * be undefined if the open method didn't set any value.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_buf
|
|
- * fuse_reply_iov
|
|
- * fuse_reply_data
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param size number of bytes to read
|
|
- * @param off offset to read from
|
|
- * @param fi file information
|
|
- */
|
|
- void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Write data
|
|
- *
|
|
- * Write should return exactly the number of bytes requested
|
|
- * except on error. An exception to this is when the file has
|
|
- * been opened in 'direct_io' mode, in which case the return value
|
|
- * of the write system call will reflect the return value of this
|
|
- * operation.
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- *
|
|
- * fi->fh will contain the value set by the open method, or will
|
|
- * be undefined if the open method didn't set any value.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_write
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param buf data to write
|
|
- * @param size number of bytes to write
|
|
- * @param off offset to write to
|
|
- * @param fi file information
|
|
- */
|
|
- void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
|
|
- size_t size, off_t off, struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Flush method
|
|
- *
|
|
- * This is called on each close() of the opened file.
|
|
- *
|
|
- * Since file descriptors can be duplicated (dup, dup2, fork), for
|
|
- * one open call there may be many flush calls.
|
|
- *
|
|
- * Filesystems shouldn't assume that flush will always be called
|
|
- * after some writes, or that if will be called at all.
|
|
- *
|
|
- * fi->fh will contain the value set by the open method, or will
|
|
- * be undefined if the open method didn't set any value.
|
|
- *
|
|
- * NOTE: the name of the method is misleading, since (unlike
|
|
- * fsync) the filesystem is not forced to flush pending writes.
|
|
- * One reason to flush data is if the filesystem wants to return
|
|
- * write errors during close. However, such use is non-portable
|
|
- * because POSIX does not require [close] to wait for delayed I/O to
|
|
- * complete.
|
|
- *
|
|
- * If the filesystem supports file locking operations (setlk,
|
|
- * getlk) it should remove all locks belonging to 'fi->owner'.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS,
|
|
- * this is treated as success and future calls to flush() will
|
|
- * succeed automatically without being send to the filesystem
|
|
- * process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- *
|
|
- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
|
|
- */
|
|
- void (*flush) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Release an open file
|
|
- *
|
|
- * Release is called when there are no more references to an open
|
|
- * file: all file descriptors are closed and all memory mappings
|
|
- * are unmapped.
|
|
- *
|
|
- * For every open call there will be exactly one release call (unless
|
|
- * the filesystem is force-unmounted).
|
|
- *
|
|
- * The filesystem may reply with an error, but error values are
|
|
- * not returned to close() or munmap() which triggered the
|
|
- * release.
|
|
- *
|
|
- * fi->fh will contain the value set by the open method, or will
|
|
- * be undefined if the open method didn't set any value.
|
|
- * fi->flags will contain the same flags as for open.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- */
|
|
- void (*release) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Synchronize file contents
|
|
- *
|
|
- * If the datasync parameter is non-zero, then only the user data
|
|
- * should be flushed, not the meta data.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS,
|
|
- * this is treated as success and future calls to fsync() will
|
|
- * succeed automatically without being send to the filesystem
|
|
- * process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param datasync flag indicating if only data should be flushed
|
|
- * @param fi file information
|
|
- */
|
|
- void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Open a directory
|
|
- *
|
|
- * Filesystem may store an arbitrary file handle (pointer, index,
|
|
- * etc) in fi->fh, and use this in other all other directory
|
|
- * stream operations (readdir, releasedir, fsyncdir).
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS and
|
|
- * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`,
|
|
- * this is treated as success and future calls to opendir and
|
|
- * releasedir will also succeed without being sent to the filesystem
|
|
- * process. In addition, the kernel will cache readdir results
|
|
- * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_open
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- */
|
|
- void (*opendir) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Read directory
|
|
- *
|
|
- * Send a buffer filled using fuse_add_direntry(), with size not
|
|
- * exceeding the requested size. Send an empty buffer on end of
|
|
- * stream.
|
|
- *
|
|
- * fi->fh will contain the value set by the opendir method, or
|
|
- * will be undefined if the opendir method didn't set any value.
|
|
- *
|
|
- * Returning a directory entry from readdir() does not affect
|
|
- * its lookup count.
|
|
- *
|
|
- * If off_t is non-zero, then it will correspond to one of the off_t
|
|
- * values that was previously returned by readdir() for the same
|
|
- * directory handle. In this case, readdir() should skip over entries
|
|
- * coming before the position defined by the off_t value. If entries
|
|
- * are added or removed while the directory handle is open, they filesystem
|
|
- * may still include the entries that have been removed, and may not
|
|
- * report the entries that have been created. However, addition or
|
|
- * removal of entries must never cause readdir() to skip over unrelated
|
|
- * entries or to report them more than once. This means
|
|
- * that off_t can not be a simple index that enumerates the entries
|
|
- * that have been returned but must contain sufficient information to
|
|
- * uniquely determine the next directory entry to return even when the
|
|
- * set of entries is changing.
|
|
- *
|
|
- * The function does not have to report the '.' and '..'
|
|
- * entries, but is allowed to do so. Note that, if readdir does
|
|
- * not return '.' or '..', they will not be implicitly returned,
|
|
- * and this behavior is observable by the caller.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_buf
|
|
- * fuse_reply_data
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param size maximum number of bytes to send
|
|
- * @param off offset to continue reading the directory stream
|
|
- * @param fi file information
|
|
- */
|
|
- void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Release an open directory
|
|
- *
|
|
- * For every opendir call there will be exactly one releasedir
|
|
- * call (unless the filesystem is force-unmounted).
|
|
- *
|
|
- * fi->fh will contain the value set by the opendir method, or
|
|
- * will be undefined if the opendir method didn't set any value.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- */
|
|
- void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Synchronize directory contents
|
|
- *
|
|
- * If the datasync parameter is non-zero, then only the directory
|
|
- * contents should be flushed, not the meta data.
|
|
- *
|
|
- * fi->fh will contain the value set by the opendir method, or
|
|
- * will be undefined if the opendir method didn't set any value.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS,
|
|
- * this is treated as success and future calls to fsyncdir() will
|
|
- * succeed automatically without being send to the filesystem
|
|
- * process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param datasync flag indicating if only data should be flushed
|
|
- * @param fi file information
|
|
- */
|
|
- void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Get file system statistics
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_statfs
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number, zero means "undefined"
|
|
- */
|
|
- void (*statfs) (fuse_req_t req, fuse_ino_t ino);
|
|
-
|
|
- /**
|
|
- * Set an extended attribute
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future setxattr() requests will fail with EOPNOTSUPP without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- */
|
|
- void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
- const char *value, size_t size, int flags);
|
|
-
|
|
- /**
|
|
- * Get an extended attribute
|
|
- *
|
|
- * If size is zero, the size of the value should be sent with
|
|
- * fuse_reply_xattr.
|
|
- *
|
|
- * If the size is non-zero, and the value fits in the buffer, the
|
|
- * value should be sent with fuse_reply_buf.
|
|
- *
|
|
- * If the size is too small for the value, the ERANGE error should
|
|
- * be sent.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future getxattr() requests will fail with EOPNOTSUPP without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_buf
|
|
- * fuse_reply_data
|
|
- * fuse_reply_xattr
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param name of the extended attribute
|
|
- * @param size maximum size of the value to send
|
|
- */
|
|
- void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
- size_t size);
|
|
-
|
|
- /**
|
|
- * List extended attribute names
|
|
- *
|
|
- * If size is zero, the total size of the attribute list should be
|
|
- * sent with fuse_reply_xattr.
|
|
- *
|
|
- * If the size is non-zero, and the null character separated
|
|
- * attribute list fits in the buffer, the list should be sent with
|
|
- * fuse_reply_buf.
|
|
- *
|
|
- * If the size is too small for the list, the ERANGE error should
|
|
- * be sent.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future listxattr() requests will fail with EOPNOTSUPP without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_buf
|
|
- * fuse_reply_data
|
|
- * fuse_reply_xattr
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param size maximum size of the list to send
|
|
- */
|
|
- void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
|
|
-
|
|
- /**
|
|
- * Remove an extended attribute
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future removexattr() requests will fail with EOPNOTSUPP without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param name of the extended attribute
|
|
- */
|
|
- void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
|
|
-
|
|
- /**
|
|
- * Check file access permissions
|
|
- *
|
|
- * This will be called for the access() and chdir() system
|
|
- * calls. If the 'default_permissions' mount option is given,
|
|
- * this method is not called.
|
|
- *
|
|
- * This method is not called under Linux kernel versions 2.4.x
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent success, i.e. this and all future access()
|
|
- * requests will succeed without being send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param mask requested access mode
|
|
- */
|
|
- void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
|
|
-
|
|
- /**
|
|
- * Create and open a file
|
|
- *
|
|
- * If the file does not exist, first create it with the specified
|
|
- * mode, and then open it.
|
|
- *
|
|
- * See the description of the open handler for more
|
|
- * information.
|
|
- *
|
|
- * If this method is not implemented or under Linux kernel
|
|
- * versions earlier than 2.6.15, the mknod() and open() methods
|
|
- * will be called instead.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, the handler
|
|
- * is treated as not implemented (i.e., for this and future requests the
|
|
- * mknod() and open() handlers will be called instead).
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_create
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param parent inode number of the parent directory
|
|
- * @param name to create
|
|
- * @param mode file type and mode with which to create the new file
|
|
- * @param fi file information
|
|
- */
|
|
- void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- mode_t mode, struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Test for a POSIX file lock
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_lock
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- * @param lock the region/type to test
|
|
- */
|
|
- void (*getlk) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi, struct flock *lock);
|
|
-
|
|
- /**
|
|
- * Acquire, modify or release a POSIX file lock
|
|
- *
|
|
- * For POSIX threads (NPTL) there's a 1-1 relation between pid and
|
|
- * owner, but otherwise this is not always the case. For checking
|
|
- * lock ownership, 'fi->owner' must be used. The l_pid field in
|
|
- * 'struct flock' should only be used to fill in this field in
|
|
- * getlk().
|
|
- *
|
|
- * Note: if the locking methods are not implemented, the kernel
|
|
- * will still allow file locking to work locally. Hence these are
|
|
- * only interesting for network filesystems and similar.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- * @param lock the region/type to set
|
|
- * @param sleep locking operation may sleep
|
|
- */
|
|
- void (*setlk) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi,
|
|
- struct flock *lock, int sleep);
|
|
-
|
|
- /**
|
|
- * Map block index within file to block index within device
|
|
- *
|
|
- * Note: This makes sense only for block device backed filesystems
|
|
- * mounted with the 'blkdev' option
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure, i.e. all future bmap() requests will
|
|
- * fail with the same error code without being send to the filesystem
|
|
- * process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_bmap
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param blocksize unit of block index
|
|
- * @param idx block index within file
|
|
- */
|
|
- void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
|
|
- uint64_t idx);
|
|
-
|
|
- /**
|
|
- * Ioctl
|
|
- *
|
|
- * Note: For unrestricted ioctls (not allowed for FUSE
|
|
- * servers), data in and out areas can be discovered by giving
|
|
- * iovs and setting FUSE_IOCTL_RETRY in *flags*. For
|
|
- * restricted ioctls, kernel prepares in/out data area
|
|
- * according to the information encoded in cmd.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_ioctl_retry
|
|
- * fuse_reply_ioctl
|
|
- * fuse_reply_ioctl_iov
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param cmd ioctl command
|
|
- * @param arg ioctl argument
|
|
- * @param fi file information
|
|
- * @param flags for FUSE_IOCTL_* flags
|
|
- * @param in_buf data fetched from the caller
|
|
- * @param in_bufsz number of fetched bytes
|
|
- * @param out_bufsz maximum size of output data
|
|
- *
|
|
- * Note : the unsigned long request submitted by the application
|
|
- * is truncated to 32 bits.
|
|
- */
|
|
- void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
|
|
- void *arg, struct fuse_file_info *fi, unsigned flags,
|
|
- const void *in_buf, size_t in_bufsz, size_t out_bufsz);
|
|
-
|
|
- /**
|
|
- * Poll for IO readiness
|
|
- *
|
|
- * Note: If ph is non-NULL, the client should notify
|
|
- * when IO readiness events occur by calling
|
|
- * fuse_lowlevel_notify_poll() with the specified ph.
|
|
- *
|
|
- * Regardless of the number of times poll with a non-NULL ph
|
|
- * is received, single notification is enough to clear all.
|
|
- * Notifying more times incurs overhead but doesn't harm
|
|
- * correctness.
|
|
- *
|
|
- * The callee is responsible for destroying ph with
|
|
- * fuse_pollhandle_destroy() when no longer in use.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as success (with a kernel-defined default poll-mask) and
|
|
- * future calls to pull() will succeed the same way without being send
|
|
- * to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_poll
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- * @param ph poll handle to be used for notification
|
|
- */
|
|
- void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
- struct fuse_pollhandle *ph);
|
|
-
|
|
- /**
|
|
- * Write data made available in a buffer
|
|
- *
|
|
- * This is a more generic version of the ->write() method. If
|
|
- * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the
|
|
- * kernel supports splicing from the fuse device, then the
|
|
- * data will be made available in pipe for supporting zero
|
|
- * copy data transfer.
|
|
- *
|
|
- * buf->count is guaranteed to be one (and thus buf->idx is
|
|
- * always zero). The write_buf handler must ensure that
|
|
- * bufv->off is correctly updated (reflecting the number of
|
|
- * bytes read from bufv->buf[0]).
|
|
- *
|
|
- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
- * expected to reset the setuid and setgid bits.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_write
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param bufv buffer containing the data
|
|
- * @param off offset to write to
|
|
- * @param fi file information
|
|
- */
|
|
- void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_bufvec *bufv, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Callback function for the retrieve request
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_none
|
|
- *
|
|
- * @param req request handle
|
|
- * @param cookie user data supplied to fuse_lowlevel_notify_retrieve()
|
|
- * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve()
|
|
- * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
|
|
- * @param bufv the buffer containing the returned data
|
|
- */
|
|
- void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
|
|
- off_t offset, struct fuse_bufvec *bufv);
|
|
-
|
|
- /**
|
|
- * Forget about multiple inodes
|
|
- *
|
|
- * See description of the forget function for more
|
|
- * information.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_none
|
|
- *
|
|
- * @param req request handle
|
|
- */
|
|
- void (*forget_multi) (fuse_req_t req, size_t count,
|
|
- struct fuse_forget_data *forgets);
|
|
-
|
|
- /**
|
|
- * Acquire, modify or release a BSD file lock
|
|
- *
|
|
- * Note: if the locking methods are not implemented, the kernel
|
|
- * will still allow file locking to work locally. Hence these are
|
|
- * only interesting for network filesystems and similar.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param fi file information
|
|
- * @param op the locking operation, see flock(2)
|
|
- */
|
|
- void (*flock) (fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi, int op);
|
|
-
|
|
- /**
|
|
- * Allocate requested space. If this function returns success then
|
|
- * subsequent writes to the specified range shall not fail due to the lack
|
|
- * of free space on the file system storage media.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future fallocate() requests will fail with EOPNOTSUPP without being
|
|
- * send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param offset starting point for allocated region
|
|
- * @param length size of allocated region
|
|
- * @param mode determines the operation to be performed on the given range,
|
|
- * see fallocate(2)
|
|
- */
|
|
- void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
|
|
- off_t offset, off_t length, struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Read directory with attributes
|
|
- *
|
|
- * Send a buffer filled using fuse_add_direntry_plus(), with size not
|
|
- * exceeding the requested size. Send an empty buffer on end of
|
|
- * stream.
|
|
- *
|
|
- * fi->fh will contain the value set by the opendir method, or
|
|
- * will be undefined if the opendir method didn't set any value.
|
|
- *
|
|
- * In contrast to readdir() (which does not affect the lookup counts),
|
|
- * the lookup count of every entry returned by readdirplus(), except "."
|
|
- * and "..", is incremented by one.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_buf
|
|
- * fuse_reply_data
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param size maximum number of bytes to send
|
|
- * @param off offset to continue reading the directory stream
|
|
- * @param fi file information
|
|
- */
|
|
- void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
- struct fuse_file_info *fi);
|
|
-
|
|
- /**
|
|
- * Copy a range of data from one file to another
|
|
- *
|
|
- * Performs an optimized copy between two file descriptors without the
|
|
- * additional cost of transferring data through the FUSE kernel module
|
|
- * to user space (glibc) and then back into the FUSE filesystem again.
|
|
- *
|
|
- * In case this method is not implemented, glibc falls back to reading
|
|
- * data from the source and writing to the destination. Effectively
|
|
- * doing an inefficient copy of the data.
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
- * future copy_file_range() requests will fail with EOPNOTSUPP without
|
|
- * being send to the filesystem process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_write
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino_in the inode number or the source file
|
|
- * @param off_in starting point from were the data should be read
|
|
- * @param fi_in file information of the source file
|
|
- * @param ino_out the inode number or the destination file
|
|
- * @param off_out starting point where the data should be written
|
|
- * @param fi_out file information of the destination file
|
|
- * @param len maximum size of the data to copy
|
|
- * @param flags passed along with the copy_file_range() syscall
|
|
- */
|
|
- void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
|
|
- off_t off_in, struct fuse_file_info *fi_in,
|
|
- fuse_ino_t ino_out, off_t off_out,
|
|
- struct fuse_file_info *fi_out, size_t len,
|
|
- int flags);
|
|
-
|
|
- /**
|
|
- * Find next data or hole after the specified offset
|
|
- *
|
|
- * If this request is answered with an error code of ENOSYS, this is
|
|
- * treated as a permanent failure, i.e. all future lseek() requests will
|
|
- * fail with the same error code without being send to the filesystem
|
|
- * process.
|
|
- *
|
|
- * Valid replies:
|
|
- * fuse_reply_lseek
|
|
- * fuse_reply_err
|
|
- *
|
|
- * @param req request handle
|
|
- * @param ino the inode number
|
|
- * @param off offset to start search from
|
|
- * @param whence either SEEK_DATA or SEEK_HOLE
|
|
- * @param fi file information
|
|
- */
|
|
- void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
|
|
- struct fuse_file_info *fi);
|
|
+ /**
|
|
+ * Initialize filesystem
|
|
+ *
|
|
+ * This function is called when libfuse establishes
|
|
+ * communication with the FUSE kernel module. The file system
|
|
+ * should use this module to inspect and/or modify the
|
|
+ * connection parameters provided in the `conn` structure.
|
|
+ *
|
|
+ * Note that some parameters may be overwritten by options
|
|
+ * passed to fuse_session_new() which take precedence over the
|
|
+ * values set in this handler.
|
|
+ *
|
|
+ * There's no reply to this function
|
|
+ *
|
|
+ * @param userdata the user data passed to fuse_session_new()
|
|
+ */
|
|
+ void (*init)(void *userdata, struct fuse_conn_info *conn);
|
|
+
|
|
+ /**
|
|
+ * Clean up filesystem.
|
|
+ *
|
|
+ * Called on filesystem exit. When this method is called, the
|
|
+ * connection to the kernel may be gone already, so that eg. calls
|
|
+ * to fuse_lowlevel_notify_* will fail.
|
|
+ *
|
|
+ * There's no reply to this function
|
|
+ *
|
|
+ * @param userdata the user data passed to fuse_session_new()
|
|
+ */
|
|
+ void (*destroy)(void *userdata);
|
|
+
|
|
+ /**
|
|
+ * Look up a directory entry by name and get its attributes.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_entry
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name the name to look up
|
|
+ */
|
|
+ void (*lookup)(fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
+
|
|
+ /**
|
|
+ * Forget about an inode
|
|
+ *
|
|
+ * This function is called when the kernel removes an inode
|
|
+ * from its internal caches.
|
|
+ *
|
|
+ * The inode's lookup count increases by one for every call to
|
|
+ * fuse_reply_entry and fuse_reply_create. The nlookup parameter
|
|
+ * indicates by how much the lookup count should be decreased.
|
|
+ *
|
|
+ * Inodes with a non-zero lookup count may receive request from
|
|
+ * the kernel even after calls to unlink, rmdir or (when
|
|
+ * overwriting an existing file) rename. Filesystems must handle
|
|
+ * such requests properly and it is recommended to defer removal
|
|
+ * of the inode until the lookup count reaches zero. Calls to
|
|
+ * unlink, rmdir or rename will be followed closely by forget
|
|
+ * unless the file or directory is open, in which case the
|
|
+ * kernel issues forget only after the release or releasedir
|
|
+ * calls.
|
|
+ *
|
|
+ * Note that if a file system will be exported over NFS the
|
|
+ * inodes lifetime must extend even beyond forget. See the
|
|
+ * generation field in struct fuse_entry_param above.
|
|
+ *
|
|
+ * On unmount the lookup count for all inodes implicitly drops
|
|
+ * to zero. It is not guaranteed that the file system will
|
|
+ * receive corresponding forget messages for the affected
|
|
+ * inodes.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_none
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param nlookup the number of lookups to forget
|
|
+ */
|
|
+ void (*forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
|
|
+
|
|
+ /**
|
|
+ * Get file attributes.
|
|
+ *
|
|
+ * If writeback caching is enabled, the kernel may have a
|
|
+ * better idea of a file's length than the FUSE file system
|
|
+ * (eg if there has been a write that extended the file size,
|
|
+ * but that has not yet been passed to the filesystem.n
|
|
+ *
|
|
+ * In this case, the st_size value provided by the file system
|
|
+ * will be ignored.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_attr
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi for future use, currently always NULL
|
|
+ */
|
|
+ void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Set file attributes
|
|
+ *
|
|
+ * In the 'attr' argument only members indicated by the 'to_set'
|
|
+ * bitmask contain valid values. Other members contain undefined
|
|
+ * values.
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits if the file
|
|
+ * size or owner is being changed.
|
|
+ *
|
|
+ * If the setattr was invoked from the ftruncate() system call
|
|
+ * under Linux kernel versions 2.6.15 or later, the fi->fh will
|
|
+ * contain the value set by the open method or will be undefined
|
|
+ * if the open method didn't set any value. Otherwise (not
|
|
+ * ftruncate call, or kernel version earlier than 2.6.15) the fi
|
|
+ * parameter will be NULL.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_attr
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param attr the attributes
|
|
+ * @param to_set bit mask of attributes which should be set
|
|
+ * @param fi file information, or NULL
|
|
+ */
|
|
+ void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
|
+ int to_set, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Read symbolic link
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_readlink
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ */
|
|
+ void (*readlink)(fuse_req_t req, fuse_ino_t ino);
|
|
+
|
|
+ /**
|
|
+ * Create file node
|
|
+ *
|
|
+ * Create a regular file, character device, block device, fifo or
|
|
+ * socket node.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_entry
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to create
|
|
+ * @param mode file type and mode with which to create the new file
|
|
+ * @param rdev the device number (only valid if created file is a device)
|
|
+ */
|
|
+ void (*mknod)(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
+ mode_t mode, dev_t rdev);
|
|
+
|
|
+ /**
|
|
+ * Create a directory
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_entry
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to create
|
|
+ * @param mode with which to create the new file
|
|
+ */
|
|
+ void (*mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
+ mode_t mode);
|
|
+
|
|
+ /**
|
|
+ * Remove a file
|
|
+ *
|
|
+ * If the file's inode's lookup count is non-zero, the file
|
|
+ * system is expected to postpone any removal of the inode
|
|
+ * until the lookup count reaches zero (see description of the
|
|
+ * forget function).
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to remove
|
|
+ */
|
|
+ void (*unlink)(fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
+
|
|
+ /**
|
|
+ * Remove a directory
|
|
+ *
|
|
+ * If the directory's inode's lookup count is non-zero, the
|
|
+ * file system is expected to postpone any removal of the
|
|
+ * inode until the lookup count reaches zero (see description
|
|
+ * of the forget function).
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to remove
|
|
+ */
|
|
+ void (*rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name);
|
|
+
|
|
+ /**
|
|
+ * Create a symbolic link
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_entry
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param link the contents of the symbolic link
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to create
|
|
+ */
|
|
+ void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent,
|
|
+ const char *name);
|
|
+
|
|
+ /**
|
|
+ * Rename a file
|
|
+ *
|
|
+ * If the target exists it should be atomically replaced. If
|
|
+ * the target's inode's lookup count is non-zero, the file
|
|
+ * system is expected to postpone any removal of the inode
|
|
+ * until the lookup count reaches zero (see description of the
|
|
+ * forget function).
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EINVAL, i.e. all
|
|
+ * future bmap requests will fail with EINVAL without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
|
|
+ * RENAME_NOREPLACE is specified, the filesystem must not
|
|
+ * overwrite *newname* if it exists and return an error
|
|
+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem
|
|
+ * must atomically exchange the two files, i.e. both must
|
|
+ * exist and neither may be deleted.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the old parent directory
|
|
+ * @param name old name
|
|
+ * @param newparent inode number of the new parent directory
|
|
+ * @param newname new name
|
|
+ */
|
|
+ void (*rename)(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
+ fuse_ino_t newparent, const char *newname,
|
|
+ unsigned int flags);
|
|
+
|
|
+ /**
|
|
+ * Create a hard link
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_entry
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the old inode number
|
|
+ * @param newparent inode number of the new parent directory
|
|
+ * @param newname new name to create
|
|
+ */
|
|
+ void (*link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
|
+ const char *newname);
|
|
+
|
|
+ /**
|
|
+ * Open a file
|
|
+ *
|
|
+ * Open flags are available in fi->flags. The following rules
|
|
+ * apply.
|
|
+ *
|
|
+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be
|
|
+ * filtered out / handled by the kernel.
|
|
+ *
|
|
+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used
|
|
+ * by the filesystem to check if the operation is
|
|
+ * permitted. If the ``-o default_permissions`` mount
|
|
+ * option is given, this check is already done by the
|
|
+ * kernel before calling open() and may thus be omitted by
|
|
+ * the filesystem.
|
|
+ *
|
|
+ * - When writeback caching is enabled, the kernel may send
|
|
+ * read requests even for files opened with O_WRONLY. The
|
|
+ * filesystem should be prepared to handle this.
|
|
+ *
|
|
+ * - When writeback caching is disabled, the filesystem is
|
|
+ * expected to properly handle the O_APPEND flag and ensure
|
|
+ * that each write is appending to the end of the file.
|
|
+ *
|
|
+ * - When writeback caching is enabled, the kernel will
|
|
+ * handle O_APPEND. However, unless all changes to the file
|
|
+ * come through the kernel this will not work reliably. The
|
|
+ * filesystem should thus either ignore the O_APPEND flag
|
|
+ * (and let the kernel handle it), or return an error
|
|
+ * (indicating that reliably O_APPEND is not available).
|
|
+ *
|
|
+ * Filesystem may store an arbitrary file handle (pointer,
|
|
+ * index, etc) in fi->fh, and use this in other all other file
|
|
+ * operations (read, write, flush, release, fsync).
|
|
+ *
|
|
+ * Filesystem may also implement stateless file I/O and not store
|
|
+ * anything in fi->fh.
|
|
+ *
|
|
+ * There are also some flags (direct_io, keep_cache) which the
|
|
+ * filesystem may set in fi, to change the way the file is opened.
|
|
+ * See fuse_file_info structure in <fuse_common.h> for more details.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS
|
|
+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in
|
|
+ * `fuse_conn_info.capable`, this is treated as success and
|
|
+ * future calls to open and release will also succeed without being
|
|
+ * sent to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_open
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Read data
|
|
+ *
|
|
+ * Read should send exactly the number of bytes requested except
|
|
+ * on EOF or error, otherwise the rest of the data will be
|
|
+ * substituted with zeroes. An exception to this is when the file
|
|
+ * has been opened in 'direct_io' mode, in which case the return
|
|
+ * value of the read system call will reflect the return value of
|
|
+ * this operation.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the open method, or will
|
|
+ * be undefined if the open method didn't set any value.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_buf
|
|
+ * fuse_reply_iov
|
|
+ * fuse_reply_data
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param size number of bytes to read
|
|
+ * @param off offset to read from
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Write data
|
|
+ *
|
|
+ * Write should return exactly the number of bytes requested
|
|
+ * except on error. An exception to this is when the file has
|
|
+ * been opened in 'direct_io' mode, in which case the return value
|
|
+ * of the write system call will reflect the return value of this
|
|
+ * operation.
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the open method, or will
|
|
+ * be undefined if the open method didn't set any value.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_write
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param buf data to write
|
|
+ * @param size number of bytes to write
|
|
+ * @param off offset to write to
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size,
|
|
+ off_t off, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Flush method
|
|
+ *
|
|
+ * This is called on each close() of the opened file.
|
|
+ *
|
|
+ * Since file descriptors can be duplicated (dup, dup2, fork), for
|
|
+ * one open call there may be many flush calls.
|
|
+ *
|
|
+ * Filesystems shouldn't assume that flush will always be called
|
|
+ * after some writes, or that if will be called at all.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the open method, or will
|
|
+ * be undefined if the open method didn't set any value.
|
|
+ *
|
|
+ * NOTE: the name of the method is misleading, since (unlike
|
|
+ * fsync) the filesystem is not forced to flush pending writes.
|
|
+ * One reason to flush data is if the filesystem wants to return
|
|
+ * write errors during close. However, such use is non-portable
|
|
+ * because POSIX does not require [close] to wait for delayed I/O to
|
|
+ * complete.
|
|
+ *
|
|
+ * If the filesystem supports file locking operations (setlk,
|
|
+ * getlk) it should remove all locks belonging to 'fi->owner'.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS,
|
|
+ * this is treated as success and future calls to flush() will
|
|
+ * succeed automatically without being send to the filesystem
|
|
+ * process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ *
|
|
+ * [close]:
|
|
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
|
|
+ */
|
|
+ void (*flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Release an open file
|
|
+ *
|
|
+ * Release is called when there are no more references to an open
|
|
+ * file: all file descriptors are closed and all memory mappings
|
|
+ * are unmapped.
|
|
+ *
|
|
+ * For every open call there will be exactly one release call (unless
|
|
+ * the filesystem is force-unmounted).
|
|
+ *
|
|
+ * The filesystem may reply with an error, but error values are
|
|
+ * not returned to close() or munmap() which triggered the
|
|
+ * release.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the open method, or will
|
|
+ * be undefined if the open method didn't set any value.
|
|
+ * fi->flags will contain the same flags as for open.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Synchronize file contents
|
|
+ *
|
|
+ * If the datasync parameter is non-zero, then only the user data
|
|
+ * should be flushed, not the meta data.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS,
|
|
+ * this is treated as success and future calls to fsync() will
|
|
+ * succeed automatically without being send to the filesystem
|
|
+ * process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param datasync flag indicating if only data should be flushed
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*fsync)(fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Open a directory
|
|
+ *
|
|
+ * Filesystem may store an arbitrary file handle (pointer, index,
|
|
+ * etc) in fi->fh, and use this in other all other directory
|
|
+ * stream operations (readdir, releasedir, fsyncdir).
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS and
|
|
+ * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`,
|
|
+ * this is treated as success and future calls to opendir and
|
|
+ * releasedir will also succeed without being sent to the filesystem
|
|
+ * process. In addition, the kernel will cache readdir results
|
|
+ * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_open
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Read directory
|
|
+ *
|
|
+ * Send a buffer filled using fuse_add_direntry(), with size not
|
|
+ * exceeding the requested size. Send an empty buffer on end of
|
|
+ * stream.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the opendir method, or
|
|
+ * will be undefined if the opendir method didn't set any value.
|
|
+ *
|
|
+ * Returning a directory entry from readdir() does not affect
|
|
+ * its lookup count.
|
|
+ *
|
|
+ * If off_t is non-zero, then it will correspond to one of the off_t
|
|
+ * values that was previously returned by readdir() for the same
|
|
+ * directory handle. In this case, readdir() should skip over entries
|
|
+ * coming before the position defined by the off_t value. If entries
|
|
+ * are added or removed while the directory handle is open, they filesystem
|
|
+ * may still include the entries that have been removed, and may not
|
|
+ * report the entries that have been created. However, addition or
|
|
+ * removal of entries must never cause readdir() to skip over unrelated
|
|
+ * entries or to report them more than once. This means
|
|
+ * that off_t can not be a simple index that enumerates the entries
|
|
+ * that have been returned but must contain sufficient information to
|
|
+ * uniquely determine the next directory entry to return even when the
|
|
+ * set of entries is changing.
|
|
+ *
|
|
+ * The function does not have to report the '.' and '..'
|
|
+ * entries, but is allowed to do so. Note that, if readdir does
|
|
+ * not return '.' or '..', they will not be implicitly returned,
|
|
+ * and this behavior is observable by the caller.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_buf
|
|
+ * fuse_reply_data
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param size maximum number of bytes to send
|
|
+ * @param off offset to continue reading the directory stream
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Release an open directory
|
|
+ *
|
|
+ * For every opendir call there will be exactly one releasedir
|
|
+ * call (unless the filesystem is force-unmounted).
|
|
+ *
|
|
+ * fi->fh will contain the value set by the opendir method, or
|
|
+ * will be undefined if the opendir method didn't set any value.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*releasedir)(fuse_req_t req, fuse_ino_t ino,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Synchronize directory contents
|
|
+ *
|
|
+ * If the datasync parameter is non-zero, then only the directory
|
|
+ * contents should be flushed, not the meta data.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the opendir method, or
|
|
+ * will be undefined if the opendir method didn't set any value.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS,
|
|
+ * this is treated as success and future calls to fsyncdir() will
|
|
+ * succeed automatically without being send to the filesystem
|
|
+ * process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param datasync flag indicating if only data should be flushed
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Get file system statistics
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_statfs
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number, zero means "undefined"
|
|
+ */
|
|
+ void (*statfs)(fuse_req_t req, fuse_ino_t ino);
|
|
+
|
|
+ /**
|
|
+ * Set an extended attribute
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future setxattr() requests will fail with EOPNOTSUPP without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ */
|
|
+ void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
+ const char *value, size_t size, int flags);
|
|
+
|
|
+ /**
|
|
+ * Get an extended attribute
|
|
+ *
|
|
+ * If size is zero, the size of the value should be sent with
|
|
+ * fuse_reply_xattr.
|
|
+ *
|
|
+ * If the size is non-zero, and the value fits in the buffer, the
|
|
+ * value should be sent with fuse_reply_buf.
|
|
+ *
|
|
+ * If the size is too small for the value, the ERANGE error should
|
|
+ * be sent.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future getxattr() requests will fail with EOPNOTSUPP without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_buf
|
|
+ * fuse_reply_data
|
|
+ * fuse_reply_xattr
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param name of the extended attribute
|
|
+ * @param size maximum size of the value to send
|
|
+ */
|
|
+ void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
+ size_t size);
|
|
+
|
|
+ /**
|
|
+ * List extended attribute names
|
|
+ *
|
|
+ * If size is zero, the total size of the attribute list should be
|
|
+ * sent with fuse_reply_xattr.
|
|
+ *
|
|
+ * If the size is non-zero, and the null character separated
|
|
+ * attribute list fits in the buffer, the list should be sent with
|
|
+ * fuse_reply_buf.
|
|
+ *
|
|
+ * If the size is too small for the list, the ERANGE error should
|
|
+ * be sent.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future listxattr() requests will fail with EOPNOTSUPP without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_buf
|
|
+ * fuse_reply_data
|
|
+ * fuse_reply_xattr
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param size maximum size of the list to send
|
|
+ */
|
|
+ void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size);
|
|
+
|
|
+ /**
|
|
+ * Remove an extended attribute
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future removexattr() requests will fail with EOPNOTSUPP without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param name of the extended attribute
|
|
+ */
|
|
+ void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name);
|
|
+
|
|
+ /**
|
|
+ * Check file access permissions
|
|
+ *
|
|
+ * This will be called for the access() and chdir() system
|
|
+ * calls. If the 'default_permissions' mount option is given,
|
|
+ * this method is not called.
|
|
+ *
|
|
+ * This method is not called under Linux kernel versions 2.4.x
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent success, i.e. this and all future access()
|
|
+ * requests will succeed without being send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param mask requested access mode
|
|
+ */
|
|
+ void (*access)(fuse_req_t req, fuse_ino_t ino, int mask);
|
|
+
|
|
+ /**
|
|
+ * Create and open a file
|
|
+ *
|
|
+ * If the file does not exist, first create it with the specified
|
|
+ * mode, and then open it.
|
|
+ *
|
|
+ * See the description of the open handler for more
|
|
+ * information.
|
|
+ *
|
|
+ * If this method is not implemented or under Linux kernel
|
|
+ * versions earlier than 2.6.15, the mknod() and open() methods
|
|
+ * will be called instead.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, the handler
|
|
+ * is treated as not implemented (i.e., for this and future requests the
|
|
+ * mknod() and open() handlers will be called instead).
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_create
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param parent inode number of the parent directory
|
|
+ * @param name to create
|
|
+ * @param mode file type and mode with which to create the new file
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*create)(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
+ mode_t mode, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Test for a POSIX file lock
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_lock
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ * @param lock the region/type to test
|
|
+ */
|
|
+ void (*getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
+ struct flock *lock);
|
|
+
|
|
+ /**
|
|
+ * Acquire, modify or release a POSIX file lock
|
|
+ *
|
|
+ * For POSIX threads (NPTL) there's a 1-1 relation between pid and
|
|
+ * owner, but otherwise this is not always the case. For checking
|
|
+ * lock ownership, 'fi->owner' must be used. The l_pid field in
|
|
+ * 'struct flock' should only be used to fill in this field in
|
|
+ * getlk().
|
|
+ *
|
|
+ * Note: if the locking methods are not implemented, the kernel
|
|
+ * will still allow file locking to work locally. Hence these are
|
|
+ * only interesting for network filesystems and similar.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ * @param lock the region/type to set
|
|
+ * @param sleep locking operation may sleep
|
|
+ */
|
|
+ void (*setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
+ struct flock *lock, int sleep);
|
|
+
|
|
+ /**
|
|
+ * Map block index within file to block index within device
|
|
+ *
|
|
+ * Note: This makes sense only for block device backed filesystems
|
|
+ * mounted with the 'blkdev' option
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure, i.e. all future bmap() requests will
|
|
+ * fail with the same error code without being send to the filesystem
|
|
+ * process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_bmap
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param blocksize unit of block index
|
|
+ * @param idx block index within file
|
|
+ */
|
|
+ void (*bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
|
|
+ uint64_t idx);
|
|
+
|
|
+ /**
|
|
+ * Ioctl
|
|
+ *
|
|
+ * Note: For unrestricted ioctls (not allowed for FUSE
|
|
+ * servers), data in and out areas can be discovered by giving
|
|
+ * iovs and setting FUSE_IOCTL_RETRY in *flags*. For
|
|
+ * restricted ioctls, kernel prepares in/out data area
|
|
+ * according to the information encoded in cmd.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_ioctl_retry
|
|
+ * fuse_reply_ioctl
|
|
+ * fuse_reply_ioctl_iov
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param cmd ioctl command
|
|
+ * @param arg ioctl argument
|
|
+ * @param fi file information
|
|
+ * @param flags for FUSE_IOCTL_* flags
|
|
+ * @param in_buf data fetched from the caller
|
|
+ * @param in_bufsz number of fetched bytes
|
|
+ * @param out_bufsz maximum size of output data
|
|
+ *
|
|
+ * Note : the unsigned long request submitted by the application
|
|
+ * is truncated to 32 bits.
|
|
+ */
|
|
+ void (*ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg,
|
|
+ struct fuse_file_info *fi, unsigned flags, const void *in_buf,
|
|
+ size_t in_bufsz, size_t out_bufsz);
|
|
+
|
|
+ /**
|
|
+ * Poll for IO readiness
|
|
+ *
|
|
+ * Note: If ph is non-NULL, the client should notify
|
|
+ * when IO readiness events occur by calling
|
|
+ * fuse_lowlevel_notify_poll() with the specified ph.
|
|
+ *
|
|
+ * Regardless of the number of times poll with a non-NULL ph
|
|
+ * is received, single notification is enough to clear all.
|
|
+ * Notifying more times incurs overhead but doesn't harm
|
|
+ * correctness.
|
|
+ *
|
|
+ * The callee is responsible for destroying ph with
|
|
+ * fuse_pollhandle_destroy() when no longer in use.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as success (with a kernel-defined default poll-mask) and
|
|
+ * future calls to pull() will succeed the same way without being send
|
|
+ * to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_poll
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ * @param ph poll handle to be used for notification
|
|
+ */
|
|
+ void (*poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
+ struct fuse_pollhandle *ph);
|
|
+
|
|
+ /**
|
|
+ * Write data made available in a buffer
|
|
+ *
|
|
+ * This is a more generic version of the ->write() method. If
|
|
+ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the
|
|
+ * kernel supports splicing from the fuse device, then the
|
|
+ * data will be made available in pipe for supporting zero
|
|
+ * copy data transfer.
|
|
+ *
|
|
+ * buf->count is guaranteed to be one (and thus buf->idx is
|
|
+ * always zero). The write_buf handler must ensure that
|
|
+ * bufv->off is correctly updated (reflecting the number of
|
|
+ * bytes read from bufv->buf[0]).
|
|
+ *
|
|
+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
|
+ * expected to reset the setuid and setgid bits.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_write
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param bufv buffer containing the data
|
|
+ * @param off offset to write to
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv,
|
|
+ off_t off, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Callback function for the retrieve request
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_none
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve()
|
|
+ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve()
|
|
+ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
|
|
+ * @param bufv the buffer containing the returned data
|
|
+ */
|
|
+ void (*retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino,
|
|
+ off_t offset, struct fuse_bufvec *bufv);
|
|
+
|
|
+ /**
|
|
+ * Forget about multiple inodes
|
|
+ *
|
|
+ * See description of the forget function for more
|
|
+ * information.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_none
|
|
+ *
|
|
+ * @param req request handle
|
|
+ */
|
|
+ void (*forget_multi)(fuse_req_t req, size_t count,
|
|
+ struct fuse_forget_data *forgets);
|
|
+
|
|
+ /**
|
|
+ * Acquire, modify or release a BSD file lock
|
|
+ *
|
|
+ * Note: if the locking methods are not implemented, the kernel
|
|
+ * will still allow file locking to work locally. Hence these are
|
|
+ * only interesting for network filesystems and similar.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param fi file information
|
|
+ * @param op the locking operation, see flock(2)
|
|
+ */
|
|
+ void (*flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
+ int op);
|
|
+
|
|
+ /**
|
|
+ * Allocate requested space. If this function returns success then
|
|
+ * subsequent writes to the specified range shall not fail due to the lack
|
|
+ * of free space on the file system storage media.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future fallocate() requests will fail with EOPNOTSUPP without being
|
|
+ * send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param offset starting point for allocated region
|
|
+ * @param length size of allocated region
|
|
+ * @param mode determines the operation to be performed on the given range,
|
|
+ * see fallocate(2)
|
|
+ */
|
|
+ void (*fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset,
|
|
+ off_t length, struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Read directory with attributes
|
|
+ *
|
|
+ * Send a buffer filled using fuse_add_direntry_plus(), with size not
|
|
+ * exceeding the requested size. Send an empty buffer on end of
|
|
+ * stream.
|
|
+ *
|
|
+ * fi->fh will contain the value set by the opendir method, or
|
|
+ * will be undefined if the opendir method didn't set any value.
|
|
+ *
|
|
+ * In contrast to readdir() (which does not affect the lookup counts),
|
|
+ * the lookup count of every entry returned by readdirplus(), except "."
|
|
+ * and "..", is incremented by one.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_buf
|
|
+ * fuse_reply_data
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param size maximum number of bytes to send
|
|
+ * @param off offset to continue reading the directory stream
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
|
+ struct fuse_file_info *fi);
|
|
+
|
|
+ /**
|
|
+ * Copy a range of data from one file to another
|
|
+ *
|
|
+ * Performs an optimized copy between two file descriptors without the
|
|
+ * additional cost of transferring data through the FUSE kernel module
|
|
+ * to user space (glibc) and then back into the FUSE filesystem again.
|
|
+ *
|
|
+ * In case this method is not implemented, glibc falls back to reading
|
|
+ * data from the source and writing to the destination. Effectively
|
|
+ * doing an inefficient copy of the data.
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all
|
|
+ * future copy_file_range() requests will fail with EOPNOTSUPP without
|
|
+ * being send to the filesystem process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_write
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino_in the inode number or the source file
|
|
+ * @param off_in starting point from were the data should be read
|
|
+ * @param fi_in file information of the source file
|
|
+ * @param ino_out the inode number or the destination file
|
|
+ * @param off_out starting point where the data should be written
|
|
+ * @param fi_out file information of the destination file
|
|
+ * @param len maximum size of the data to copy
|
|
+ * @param flags passed along with the copy_file_range() syscall
|
|
+ */
|
|
+ void (*copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
|
|
+ struct fuse_file_info *fi_in, fuse_ino_t ino_out,
|
|
+ off_t off_out, struct fuse_file_info *fi_out,
|
|
+ size_t len, int flags);
|
|
+
|
|
+ /**
|
|
+ * Find next data or hole after the specified offset
|
|
+ *
|
|
+ * If this request is answered with an error code of ENOSYS, this is
|
|
+ * treated as a permanent failure, i.e. all future lseek() requests will
|
|
+ * fail with the same error code without being send to the filesystem
|
|
+ * process.
|
|
+ *
|
|
+ * Valid replies:
|
|
+ * fuse_reply_lseek
|
|
+ * fuse_reply_err
|
|
+ *
|
|
+ * @param req request handle
|
|
+ * @param ino the inode number
|
|
+ * @param off offset to start search from
|
|
+ * @param whence either SEEK_DATA or SEEK_HOLE
|
|
+ * @param fi file information
|
|
+ */
|
|
+ void (*lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
|
|
+ struct fuse_file_info *fi);
|
|
};
|
|
|
|
/**
|
|
@@ -1305,7 +1307,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
|
|
* @return zero for success, -errno for failure to send reply
|
|
*/
|
|
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
|
- const struct fuse_file_info *fi);
|
|
+ const struct fuse_file_info *fi);
|
|
|
|
/**
|
|
* Reply with attributes
|
|
@@ -1315,11 +1317,11 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
|
*
|
|
* @param req request handle
|
|
* @param attr the attributes
|
|
- * @param attr_timeout validity timeout (in seconds) for the attributes
|
|
+ * @param attr_timeout validity timeout (in seconds) for the attributes
|
|
* @return zero for success, -errno for failure to send reply
|
|
*/
|
|
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
|
- double attr_timeout);
|
|
+ double attr_timeout);
|
|
|
|
/**
|
|
* Reply with the contents of a symbolic link
|
|
@@ -1417,7 +1419,7 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
|
|
* @return zero for success, -errno for failure to send reply
|
|
*/
|
|
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
|
|
- enum fuse_buf_copy_flags flags);
|
|
+ enum fuse_buf_copy_flags flags);
|
|
|
|
/**
|
|
* Reply with data vector
|
|
@@ -1480,9 +1482,9 @@ int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
|
|
*/
|
|
int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Filling a buffer in readdir *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Filling a buffer in readdir
|
|
+ */
|
|
|
|
/**
|
|
* Add a directory entry to the buffer
|
|
@@ -1512,8 +1514,7 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
|
|
* @return the space needed for the entry
|
|
*/
|
|
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
|
|
- const char *name, const struct stat *stbuf,
|
|
- off_t off);
|
|
+ const char *name, const struct stat *stbuf, off_t off);
|
|
|
|
/**
|
|
* Add a directory entry to the buffer with the attributes
|
|
@@ -1529,8 +1530,8 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
|
|
* @return the space needed for the entry
|
|
*/
|
|
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
|
- const char *name,
|
|
- const struct fuse_entry_param *e, off_t off);
|
|
+ const char *name,
|
|
+ const struct fuse_entry_param *e, off_t off);
|
|
|
|
/**
|
|
* Reply to ask for data fetch and output buffer preparation. ioctl
|
|
@@ -1547,9 +1548,9 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
|
* @param out_count number of entries in out_iov
|
|
* @return zero for success, -errno for failure to send reply
|
|
*/
|
|
-int fuse_reply_ioctl_retry(fuse_req_t req,
|
|
- const struct iovec *in_iov, size_t in_count,
|
|
- const struct iovec *out_iov, size_t out_count);
|
|
+int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov,
|
|
+ size_t in_count, const struct iovec *out_iov,
|
|
+ size_t out_count);
|
|
|
|
/**
|
|
* Reply to finish ioctl
|
|
@@ -1576,7 +1577,7 @@ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
|
|
* @param count the size of vector
|
|
*/
|
|
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
|
|
- int count);
|
|
+ int count);
|
|
|
|
/**
|
|
* Reply with poll result event mask
|
|
@@ -1598,9 +1599,9 @@ int fuse_reply_poll(fuse_req_t req, unsigned revents);
|
|
*/
|
|
int fuse_reply_lseek(fuse_req_t req, off_t off);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Notification *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Notification
|
|
+ */
|
|
|
|
/**
|
|
* Notify IO readiness event
|
|
@@ -1635,7 +1636,7 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
|
|
* @return zero for success, -errno for failure
|
|
*/
|
|
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
|
|
- off_t off, off_t len);
|
|
+ off_t off, off_t len);
|
|
|
|
/**
|
|
* Notify to invalidate parent attributes and the dentry matching
|
|
@@ -1663,7 +1664,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
|
|
* @return zero for success, -errno for failure
|
|
*/
|
|
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
|
|
- const char *name, size_t namelen);
|
|
+ const char *name, size_t namelen);
|
|
|
|
/**
|
|
* This function behaves like fuse_lowlevel_notify_inval_entry() with
|
|
@@ -1693,9 +1694,9 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
|
|
* @param namelen strlen() of file name
|
|
* @return zero for success, -errno for failure
|
|
*/
|
|
-int fuse_lowlevel_notify_delete(struct fuse_session *se,
|
|
- fuse_ino_t parent, fuse_ino_t child,
|
|
- const char *name, size_t namelen);
|
|
+int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent,
|
|
+ fuse_ino_t child, const char *name,
|
|
+ size_t namelen);
|
|
|
|
/**
|
|
* Store data to the kernel buffers
|
|
@@ -1723,8 +1724,8 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se,
|
|
* @return zero for success, -errno for failure
|
|
*/
|
|
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
|
|
- off_t offset, struct fuse_bufvec *bufv,
|
|
- enum fuse_buf_copy_flags flags);
|
|
+ off_t offset, struct fuse_bufvec *bufv,
|
|
+ enum fuse_buf_copy_flags flags);
|
|
/**
|
|
* Retrieve data from the kernel buffers
|
|
*
|
|
@@ -1755,12 +1756,12 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
|
|
* @return zero for success, -errno for failure
|
|
*/
|
|
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
|
|
- size_t size, off_t offset, void *cookie);
|
|
+ size_t size, off_t offset, void *cookie);
|
|
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Utility functions *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Utility functions
|
|
+ */
|
|
|
|
/**
|
|
* Get the userdata from the request
|
|
@@ -1822,7 +1823,7 @@ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
|
|
* @param data user data passed to the callback function
|
|
*/
|
|
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
|
|
- void *data);
|
|
+ void *data);
|
|
|
|
/**
|
|
* Check if a request has already been interrupted
|
|
@@ -1833,9 +1834,9 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
|
|
int fuse_req_interrupted(fuse_req_t req);
|
|
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Inquiry functions *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Inquiry functions
|
|
+ */
|
|
|
|
/**
|
|
* Print low-level version information to stdout.
|
|
@@ -1854,18 +1855,18 @@ void fuse_lowlevel_help(void);
|
|
*/
|
|
void fuse_cmdline_help(void);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Filesystem setup & teardown *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Filesystem setup & teardown
|
|
+ */
|
|
|
|
struct fuse_cmdline_opts {
|
|
- int foreground;
|
|
- int debug;
|
|
- int nodefault_subtype;
|
|
- char *mountpoint;
|
|
- int show_version;
|
|
- int show_help;
|
|
- unsigned int max_idle_threads;
|
|
+ int foreground;
|
|
+ int debug;
|
|
+ int nodefault_subtype;
|
|
+ char *mountpoint;
|
|
+ int show_version;
|
|
+ int show_help;
|
|
+ unsigned int max_idle_threads;
|
|
};
|
|
|
|
/**
|
|
@@ -1886,8 +1887,7 @@ struct fuse_cmdline_opts {
|
|
* @param opts output argument for parsed options
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
-int fuse_parse_cmdline(struct fuse_args *args,
|
|
- struct fuse_cmdline_opts *opts);
|
|
+int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts);
|
|
|
|
/**
|
|
* Create a low level session.
|
|
@@ -1918,8 +1918,8 @@ int fuse_parse_cmdline(struct fuse_args *args,
|
|
* @return the fuse session on success, NULL on failure
|
|
**/
|
|
struct fuse_session *fuse_session_new(struct fuse_args *args,
|
|
- const struct fuse_lowlevel_ops *op,
|
|
- size_t op_size, void *userdata);
|
|
+ const struct fuse_lowlevel_ops *op,
|
|
+ size_t op_size, void *userdata);
|
|
|
|
/**
|
|
* Mount a FUSE file system.
|
|
@@ -2014,9 +2014,9 @@ void fuse_session_unmount(struct fuse_session *se);
|
|
*/
|
|
void fuse_session_destroy(struct fuse_session *se);
|
|
|
|
-/* ----------------------------------------------------------- *
|
|
- * Custom event loop support *
|
|
- * ----------------------------------------------------------- */
|
|
+/*
|
|
+ * Custom event loop support
|
|
+ */
|
|
|
|
/**
|
|
* Return file descriptor for communication with kernel.
|
|
@@ -2043,7 +2043,7 @@ int fuse_session_fd(struct fuse_session *se);
|
|
* @param buf the fuse_buf containing the request
|
|
*/
|
|
void fuse_session_process_buf(struct fuse_session *se,
|
|
- const struct fuse_buf *buf);
|
|
+ const struct fuse_buf *buf);
|
|
|
|
/**
|
|
* Read a raw request from the kernel into the supplied buffer.
|
|
diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h
|
|
index 2f6663ed7d..f252baa752 100644
|
|
--- a/tools/virtiofsd/fuse_misc.h
|
|
+++ b/tools/virtiofsd/fuse_misc.h
|
|
@@ -1,18 +1,18 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#include <pthread.h>
|
|
|
|
/*
|
|
- Versioned symbols cannot be used in some cases because it
|
|
- - confuse the dynamic linker in uClibc
|
|
- - not supported on MacOSX (in MachO binary format)
|
|
-*/
|
|
+ * Versioned symbols cannot be used in some cases because it
|
|
+ * - confuse the dynamic linker in uClibc
|
|
+ * - not supported on MacOSX (in MachO binary format)
|
|
+ */
|
|
#if (!defined(__UCLIBC__) && !defined(__APPLE__))
|
|
#define FUSE_SYMVER(x) __asm__(x)
|
|
#else
|
|
@@ -25,11 +25,11 @@
|
|
/* Is this hack still needed? */
|
|
static inline void fuse_mutex_init(pthread_mutex_t *mut)
|
|
{
|
|
- pthread_mutexattr_t attr;
|
|
- pthread_mutexattr_init(&attr);
|
|
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
|
- pthread_mutex_init(mut, &attr);
|
|
- pthread_mutexattr_destroy(&attr);
|
|
+ pthread_mutexattr_t attr;
|
|
+ pthread_mutexattr_init(&attr);
|
|
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
|
+ pthread_mutex_init(mut, &attr);
|
|
+ pthread_mutexattr_destroy(&attr);
|
|
}
|
|
#endif
|
|
|
|
diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c
|
|
index 93066b926e..edd36f4a3b 100644
|
|
--- a/tools/virtiofsd/fuse_opt.c
|
|
+++ b/tools/virtiofsd/fuse_opt.c
|
|
@@ -1,423 +1,450 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- Implementation of option parsing routines (dealing with `struct
|
|
- fuse_args`).
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * Implementation of option parsing routines (dealing with `struct
|
|
+ * fuse_args`).
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
+#include "fuse_opt.h"
|
|
#include "config.h"
|
|
#include "fuse_i.h"
|
|
-#include "fuse_opt.h"
|
|
#include "fuse_misc.h"
|
|
|
|
+#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
-#include <assert.h>
|
|
|
|
struct fuse_opt_context {
|
|
- void *data;
|
|
- const struct fuse_opt *opt;
|
|
- fuse_opt_proc_t proc;
|
|
- int argctr;
|
|
- int argc;
|
|
- char **argv;
|
|
- struct fuse_args outargs;
|
|
- char *opts;
|
|
- int nonopt;
|
|
+ void *data;
|
|
+ const struct fuse_opt *opt;
|
|
+ fuse_opt_proc_t proc;
|
|
+ int argctr;
|
|
+ int argc;
|
|
+ char **argv;
|
|
+ struct fuse_args outargs;
|
|
+ char *opts;
|
|
+ int nonopt;
|
|
};
|
|
|
|
void fuse_opt_free_args(struct fuse_args *args)
|
|
{
|
|
- if (args) {
|
|
- if (args->argv && args->allocated) {
|
|
- int i;
|
|
- for (i = 0; i < args->argc; i++)
|
|
- free(args->argv[i]);
|
|
- free(args->argv);
|
|
- }
|
|
- args->argc = 0;
|
|
- args->argv = NULL;
|
|
- args->allocated = 0;
|
|
- }
|
|
+ if (args) {
|
|
+ if (args->argv && args->allocated) {
|
|
+ int i;
|
|
+ for (i = 0; i < args->argc; i++) {
|
|
+ free(args->argv[i]);
|
|
+ }
|
|
+ free(args->argv);
|
|
+ }
|
|
+ args->argc = 0;
|
|
+ args->argv = NULL;
|
|
+ args->allocated = 0;
|
|
+ }
|
|
}
|
|
|
|
static int alloc_failed(void)
|
|
{
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
|
|
- return -1;
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
|
|
+ return -1;
|
|
}
|
|
|
|
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
|
{
|
|
- char **newargv;
|
|
- char *newarg;
|
|
-
|
|
- assert(!args->argv || args->allocated);
|
|
-
|
|
- newarg = strdup(arg);
|
|
- if (!newarg)
|
|
- return alloc_failed();
|
|
-
|
|
- newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
|
- if (!newargv) {
|
|
- free(newarg);
|
|
- return alloc_failed();
|
|
- }
|
|
-
|
|
- args->argv = newargv;
|
|
- args->allocated = 1;
|
|
- args->argv[args->argc++] = newarg;
|
|
- args->argv[args->argc] = NULL;
|
|
- return 0;
|
|
+ char **newargv;
|
|
+ char *newarg;
|
|
+
|
|
+ assert(!args->argv || args->allocated);
|
|
+
|
|
+ newarg = strdup(arg);
|
|
+ if (!newarg) {
|
|
+ return alloc_failed();
|
|
+ }
|
|
+
|
|
+ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
|
+ if (!newargv) {
|
|
+ free(newarg);
|
|
+ return alloc_failed();
|
|
+ }
|
|
+
|
|
+ args->argv = newargv;
|
|
+ args->allocated = 1;
|
|
+ args->argv[args->argc++] = newarg;
|
|
+ args->argv[args->argc] = NULL;
|
|
+ return 0;
|
|
}
|
|
|
|
static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
|
|
- const char *arg)
|
|
+ const char *arg)
|
|
{
|
|
- assert(pos <= args->argc);
|
|
- if (fuse_opt_add_arg(args, arg) == -1)
|
|
- return -1;
|
|
-
|
|
- if (pos != args->argc - 1) {
|
|
- char *newarg = args->argv[args->argc - 1];
|
|
- memmove(&args->argv[pos + 1], &args->argv[pos],
|
|
- sizeof(char *) * (args->argc - pos - 1));
|
|
- args->argv[pos] = newarg;
|
|
- }
|
|
- return 0;
|
|
+ assert(pos <= args->argc);
|
|
+ if (fuse_opt_add_arg(args, arg) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (pos != args->argc - 1) {
|
|
+ char *newarg = args->argv[args->argc - 1];
|
|
+ memmove(&args->argv[pos + 1], &args->argv[pos],
|
|
+ sizeof(char *) * (args->argc - pos - 1));
|
|
+ args->argv[pos] = newarg;
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
|
|
{
|
|
- return fuse_opt_insert_arg_common(args, pos, arg);
|
|
+ return fuse_opt_insert_arg_common(args, pos, arg);
|
|
}
|
|
|
|
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
|
|
{
|
|
- if (ctx->argctr + 1 >= ctx->argc) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
|
|
- return -1;
|
|
- }
|
|
- ctx->argctr++;
|
|
- return 0;
|
|
+ if (ctx->argctr + 1 >= ctx->argc) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
|
|
+ return -1;
|
|
+ }
|
|
+ ctx->argctr++;
|
|
+ return 0;
|
|
}
|
|
|
|
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
|
|
{
|
|
- return fuse_opt_add_arg(&ctx->outargs, arg);
|
|
+ return fuse_opt_add_arg(&ctx->outargs, arg);
|
|
}
|
|
|
|
static int add_opt_common(char **opts, const char *opt, int esc)
|
|
{
|
|
- unsigned oldlen = *opts ? strlen(*opts) : 0;
|
|
- char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
|
|
-
|
|
- if (!d)
|
|
- return alloc_failed();
|
|
-
|
|
- *opts = d;
|
|
- if (oldlen) {
|
|
- d += oldlen;
|
|
- *d++ = ',';
|
|
- }
|
|
-
|
|
- for (; *opt; opt++) {
|
|
- if (esc && (*opt == ',' || *opt == '\\'))
|
|
- *d++ = '\\';
|
|
- *d++ = *opt;
|
|
- }
|
|
- *d = '\0';
|
|
-
|
|
- return 0;
|
|
+ unsigned oldlen = *opts ? strlen(*opts) : 0;
|
|
+ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
|
|
+
|
|
+ if (!d) {
|
|
+ return alloc_failed();
|
|
+ }
|
|
+
|
|
+ *opts = d;
|
|
+ if (oldlen) {
|
|
+ d += oldlen;
|
|
+ *d++ = ',';
|
|
+ }
|
|
+
|
|
+ for (; *opt; opt++) {
|
|
+ if (esc && (*opt == ',' || *opt == '\\')) {
|
|
+ *d++ = '\\';
|
|
+ }
|
|
+ *d++ = *opt;
|
|
+ }
|
|
+ *d = '\0';
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
int fuse_opt_add_opt(char **opts, const char *opt)
|
|
{
|
|
- return add_opt_common(opts, opt, 0);
|
|
+ return add_opt_common(opts, opt, 0);
|
|
}
|
|
|
|
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
|
|
{
|
|
- return add_opt_common(opts, opt, 1);
|
|
+ return add_opt_common(opts, opt, 1);
|
|
}
|
|
|
|
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
|
|
{
|
|
- return add_opt_common(&ctx->opts, opt, 1);
|
|
+ return add_opt_common(&ctx->opts, opt, 1);
|
|
}
|
|
|
|
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
|
|
- int iso)
|
|
+ int iso)
|
|
{
|
|
- if (key == FUSE_OPT_KEY_DISCARD)
|
|
- return 0;
|
|
-
|
|
- if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
|
- int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
|
- if (res == -1 || !res)
|
|
- return res;
|
|
- }
|
|
- if (iso)
|
|
- return add_opt(ctx, arg);
|
|
- else
|
|
- return add_arg(ctx, arg);
|
|
+ if (key == FUSE_OPT_KEY_DISCARD) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
|
+ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
|
+ if (res == -1 || !res) {
|
|
+ return res;
|
|
+ }
|
|
+ }
|
|
+ if (iso) {
|
|
+ return add_opt(ctx, arg);
|
|
+ } else {
|
|
+ return add_arg(ctx, arg);
|
|
+ }
|
|
}
|
|
|
|
static int match_template(const char *t, const char *arg, unsigned *sepp)
|
|
{
|
|
- int arglen = strlen(arg);
|
|
- const char *sep = strchr(t, '=');
|
|
- sep = sep ? sep : strchr(t, ' ');
|
|
- if (sep && (!sep[1] || sep[1] == '%')) {
|
|
- int tlen = sep - t;
|
|
- if (sep[0] == '=')
|
|
- tlen ++;
|
|
- if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
|
- *sepp = sep - t;
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- if (strcmp(t, arg) == 0) {
|
|
- *sepp = 0;
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
+ int arglen = strlen(arg);
|
|
+ const char *sep = strchr(t, '=');
|
|
+ sep = sep ? sep : strchr(t, ' ');
|
|
+ if (sep && (!sep[1] || sep[1] == '%')) {
|
|
+ int tlen = sep - t;
|
|
+ if (sep[0] == '=') {
|
|
+ tlen++;
|
|
+ }
|
|
+ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
|
+ *sepp = sep - t;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ if (strcmp(t, arg) == 0) {
|
|
+ *sepp = 0;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
|
|
- const char *arg, unsigned *sepp)
|
|
+ const char *arg, unsigned *sepp)
|
|
{
|
|
- for (; opt && opt->templ; opt++)
|
|
- if (match_template(opt->templ, arg, sepp))
|
|
- return opt;
|
|
- return NULL;
|
|
+ for (; opt && opt->templ; opt++) {
|
|
+ if (match_template(opt->templ, arg, sepp)) {
|
|
+ return opt;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
}
|
|
|
|
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
|
|
{
|
|
- unsigned dummy;
|
|
- return find_opt(opts, opt, &dummy) ? 1 : 0;
|
|
+ unsigned dummy;
|
|
+ return find_opt(opts, opt, &dummy) ? 1 : 0;
|
|
}
|
|
|
|
static int process_opt_param(void *var, const char *format, const char *param,
|
|
- const char *arg)
|
|
+ const char *arg)
|
|
{
|
|
- assert(format[0] == '%');
|
|
- if (format[1] == 's') {
|
|
- char **s = var;
|
|
- char *copy = strdup(param);
|
|
- if (!copy)
|
|
- return alloc_failed();
|
|
-
|
|
- free(*s);
|
|
- *s = copy;
|
|
- } else {
|
|
- if (sscanf(param, format, var) != 1) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
|
|
- return -1;
|
|
- }
|
|
- }
|
|
- return 0;
|
|
+ assert(format[0] == '%');
|
|
+ if (format[1] == 's') {
|
|
+ char **s = var;
|
|
+ char *copy = strdup(param);
|
|
+ if (!copy) {
|
|
+ return alloc_failed();
|
|
+ }
|
|
+
|
|
+ free(*s);
|
|
+ *s = copy;
|
|
+ } else {
|
|
+ if (sscanf(param, format, var) != 1) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n",
|
|
+ arg);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
-static int process_opt(struct fuse_opt_context *ctx,
|
|
- const struct fuse_opt *opt, unsigned sep,
|
|
- const char *arg, int iso)
|
|
+static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt,
|
|
+ unsigned sep, const char *arg, int iso)
|
|
{
|
|
- if (opt->offset == -1U) {
|
|
- if (call_proc(ctx, arg, opt->value, iso) == -1)
|
|
- return -1;
|
|
- } else {
|
|
- void *var = (char *)ctx->data + opt->offset;
|
|
- if (sep && opt->templ[sep + 1]) {
|
|
- const char *param = arg + sep;
|
|
- if (opt->templ[sep] == '=')
|
|
- param ++;
|
|
- if (process_opt_param(var, opt->templ + sep + 1,
|
|
- param, arg) == -1)
|
|
- return -1;
|
|
- } else
|
|
- *(int *)var = opt->value;
|
|
- }
|
|
- return 0;
|
|
+ if (opt->offset == -1U) {
|
|
+ if (call_proc(ctx, arg, opt->value, iso) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ } else {
|
|
+ void *var = (char *)ctx->data + opt->offset;
|
|
+ if (sep && opt->templ[sep + 1]) {
|
|
+ const char *param = arg + sep;
|
|
+ if (opt->templ[sep] == '=') {
|
|
+ param++;
|
|
+ }
|
|
+ if (process_opt_param(var, opt->templ + sep + 1, param, arg) ==
|
|
+ -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ } else {
|
|
+ *(int *)var = opt->value;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
static int process_opt_sep_arg(struct fuse_opt_context *ctx,
|
|
- const struct fuse_opt *opt, unsigned sep,
|
|
- const char *arg, int iso)
|
|
+ const struct fuse_opt *opt, unsigned sep,
|
|
+ const char *arg, int iso)
|
|
{
|
|
- int res;
|
|
- char *newarg;
|
|
- char *param;
|
|
-
|
|
- if (next_arg(ctx, arg) == -1)
|
|
- return -1;
|
|
-
|
|
- param = ctx->argv[ctx->argctr];
|
|
- newarg = malloc(sep + strlen(param) + 1);
|
|
- if (!newarg)
|
|
- return alloc_failed();
|
|
-
|
|
- memcpy(newarg, arg, sep);
|
|
- strcpy(newarg + sep, param);
|
|
- res = process_opt(ctx, opt, sep, newarg, iso);
|
|
- free(newarg);
|
|
-
|
|
- return res;
|
|
+ int res;
|
|
+ char *newarg;
|
|
+ char *param;
|
|
+
|
|
+ if (next_arg(ctx, arg) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ param = ctx->argv[ctx->argctr];
|
|
+ newarg = malloc(sep + strlen(param) + 1);
|
|
+ if (!newarg) {
|
|
+ return alloc_failed();
|
|
+ }
|
|
+
|
|
+ memcpy(newarg, arg, sep);
|
|
+ strcpy(newarg + sep, param);
|
|
+ res = process_opt(ctx, opt, sep, newarg, iso);
|
|
+ free(newarg);
|
|
+
|
|
+ return res;
|
|
}
|
|
|
|
static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
|
|
{
|
|
- unsigned sep;
|
|
- const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
|
- if (opt) {
|
|
- for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
|
- int res;
|
|
- if (sep && opt->templ[sep] == ' ' && !arg[sep])
|
|
- res = process_opt_sep_arg(ctx, opt, sep, arg,
|
|
- iso);
|
|
- else
|
|
- res = process_opt(ctx, opt, sep, arg, iso);
|
|
- if (res == -1)
|
|
- return -1;
|
|
- }
|
|
- return 0;
|
|
- } else
|
|
- return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
|
+ unsigned sep;
|
|
+ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
|
+ if (opt) {
|
|
+ for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
|
+ int res;
|
|
+ if (sep && opt->templ[sep] == ' ' && !arg[sep]) {
|
|
+ res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
|
|
+ } else {
|
|
+ res = process_opt(ctx, opt, sep, arg, iso);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+ } else {
|
|
+ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
|
+ }
|
|
}
|
|
|
|
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
|
|
{
|
|
- char *s = opts;
|
|
- char *d = s;
|
|
- int end = 0;
|
|
-
|
|
- while (!end) {
|
|
- if (*s == '\0')
|
|
- end = 1;
|
|
- if (*s == ',' || end) {
|
|
- int res;
|
|
-
|
|
- *d = '\0';
|
|
- res = process_gopt(ctx, opts, 1);
|
|
- if (res == -1)
|
|
- return -1;
|
|
- d = opts;
|
|
- } else {
|
|
- if (s[0] == '\\' && s[1] != '\0') {
|
|
- s++;
|
|
- if (s[0] >= '0' && s[0] <= '3' &&
|
|
- s[1] >= '0' && s[1] <= '7' &&
|
|
- s[2] >= '0' && s[2] <= '7') {
|
|
- *d++ = (s[0] - '0') * 0100 +
|
|
- (s[1] - '0') * 0010 +
|
|
- (s[2] - '0');
|
|
- s += 2;
|
|
- } else {
|
|
- *d++ = *s;
|
|
- }
|
|
- } else {
|
|
- *d++ = *s;
|
|
- }
|
|
- }
|
|
- s++;
|
|
- }
|
|
-
|
|
- return 0;
|
|
+ char *s = opts;
|
|
+ char *d = s;
|
|
+ int end = 0;
|
|
+
|
|
+ while (!end) {
|
|
+ if (*s == '\0') {
|
|
+ end = 1;
|
|
+ }
|
|
+ if (*s == ',' || end) {
|
|
+ int res;
|
|
+
|
|
+ *d = '\0';
|
|
+ res = process_gopt(ctx, opts, 1);
|
|
+ if (res == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ d = opts;
|
|
+ } else {
|
|
+ if (s[0] == '\\' && s[1] != '\0') {
|
|
+ s++;
|
|
+ if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' &&
|
|
+ s[2] >= '0' && s[2] <= '7') {
|
|
+ *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 +
|
|
+ (s[2] - '0');
|
|
+ s += 2;
|
|
+ } else {
|
|
+ *d++ = *s;
|
|
+ }
|
|
+ } else {
|
|
+ *d++ = *s;
|
|
+ }
|
|
+ }
|
|
+ s++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
|
|
{
|
|
- int res;
|
|
- char *copy = strdup(opts);
|
|
-
|
|
- if (!copy) {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
|
|
- return -1;
|
|
- }
|
|
- res = process_real_option_group(ctx, copy);
|
|
- free(copy);
|
|
- return res;
|
|
+ int res;
|
|
+ char *copy = strdup(opts);
|
|
+
|
|
+ if (!copy) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
|
|
+ return -1;
|
|
+ }
|
|
+ res = process_real_option_group(ctx, copy);
|
|
+ free(copy);
|
|
+ return res;
|
|
}
|
|
|
|
static int process_one(struct fuse_opt_context *ctx, const char *arg)
|
|
{
|
|
- if (ctx->nonopt || arg[0] != '-')
|
|
- return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
|
- else if (arg[1] == 'o') {
|
|
- if (arg[2])
|
|
- return process_option_group(ctx, arg + 2);
|
|
- else {
|
|
- if (next_arg(ctx, arg) == -1)
|
|
- return -1;
|
|
-
|
|
- return process_option_group(ctx,
|
|
- ctx->argv[ctx->argctr]);
|
|
- }
|
|
- } else if (arg[1] == '-' && !arg[2]) {
|
|
- if (add_arg(ctx, arg) == -1)
|
|
- return -1;
|
|
- ctx->nonopt = ctx->outargs.argc;
|
|
- return 0;
|
|
- } else
|
|
- return process_gopt(ctx, arg, 0);
|
|
+ if (ctx->nonopt || arg[0] != '-') {
|
|
+ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
|
+ } else if (arg[1] == 'o') {
|
|
+ if (arg[2]) {
|
|
+ return process_option_group(ctx, arg + 2);
|
|
+ } else {
|
|
+ if (next_arg(ctx, arg) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return process_option_group(ctx, ctx->argv[ctx->argctr]);
|
|
+ }
|
|
+ } else if (arg[1] == '-' && !arg[2]) {
|
|
+ if (add_arg(ctx, arg) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ ctx->nonopt = ctx->outargs.argc;
|
|
+ return 0;
|
|
+ } else {
|
|
+ return process_gopt(ctx, arg, 0);
|
|
+ }
|
|
}
|
|
|
|
static int opt_parse(struct fuse_opt_context *ctx)
|
|
{
|
|
- if (ctx->argc) {
|
|
- if (add_arg(ctx, ctx->argv[0]) == -1)
|
|
- return -1;
|
|
- }
|
|
-
|
|
- for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
|
|
- if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
|
|
- return -1;
|
|
-
|
|
- if (ctx->opts) {
|
|
- if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
|
- fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
|
|
- return -1;
|
|
- }
|
|
-
|
|
- /* If option separator ("--") is the last argument, remove it */
|
|
- if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
|
|
- strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
|
|
- free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
|
- ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
|
- }
|
|
-
|
|
- return 0;
|
|
+ if (ctx->argc) {
|
|
+ if (add_arg(ctx, ctx->argv[0]) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) {
|
|
+ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ctx->opts) {
|
|
+ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
|
+ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If option separator ("--") is the last argument, remove it */
|
|
+ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
|
|
+ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
|
|
+ free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
|
+ ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
int fuse_opt_parse(struct fuse_args *args, void *data,
|
|
- const struct fuse_opt opts[], fuse_opt_proc_t proc)
|
|
+ const struct fuse_opt opts[], fuse_opt_proc_t proc)
|
|
{
|
|
- int res;
|
|
- struct fuse_opt_context ctx = {
|
|
- .data = data,
|
|
- .opt = opts,
|
|
- .proc = proc,
|
|
- };
|
|
-
|
|
- if (!args || !args->argv || !args->argc)
|
|
- return 0;
|
|
-
|
|
- ctx.argc = args->argc;
|
|
- ctx.argv = args->argv;
|
|
-
|
|
- res = opt_parse(&ctx);
|
|
- if (res != -1) {
|
|
- struct fuse_args tmp = *args;
|
|
- *args = ctx.outargs;
|
|
- ctx.outargs = tmp;
|
|
- }
|
|
- free(ctx.opts);
|
|
- fuse_opt_free_args(&ctx.outargs);
|
|
- return res;
|
|
+ int res;
|
|
+ struct fuse_opt_context ctx = {
|
|
+ .data = data,
|
|
+ .opt = opts,
|
|
+ .proc = proc,
|
|
+ };
|
|
+
|
|
+ if (!args || !args->argv || !args->argc) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ctx.argc = args->argc;
|
|
+ ctx.argv = args->argv;
|
|
+
|
|
+ res = opt_parse(&ctx);
|
|
+ if (res != -1) {
|
|
+ struct fuse_args tmp = *args;
|
|
+ *args = ctx.outargs;
|
|
+ ctx.outargs = tmp;
|
|
+ }
|
|
+ free(ctx.opts);
|
|
+ fuse_opt_free_args(&ctx.outargs);
|
|
+ return res;
|
|
}
|
|
diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h
|
|
index 69102555be..8f59b4d301 100644
|
|
--- a/tools/virtiofsd/fuse_opt.h
|
|
+++ b/tools/virtiofsd/fuse_opt.h
|
|
@@ -1,10 +1,10 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
#ifndef FUSE_OPT_H_
|
|
#define FUSE_OPT_H_
|
|
@@ -37,7 +37,7 @@
|
|
*
|
|
* - 'offsetof(struct foo, member)' actions i) and iii)
|
|
*
|
|
- * - -1 action ii)
|
|
+ * - -1 action ii)
|
|
*
|
|
* The 'offsetof()' macro is defined in the <stddef.h> header.
|
|
*
|
|
@@ -48,7 +48,7 @@
|
|
*
|
|
* The types of templates are:
|
|
*
|
|
- * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
|
|
+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
|
|
* themselves. Invalid values are "--" and anything beginning
|
|
* with "-o"
|
|
*
|
|
@@ -71,58 +71,67 @@
|
|
* freed.
|
|
*/
|
|
struct fuse_opt {
|
|
- /** Matching template and optional parameter formatting */
|
|
- const char *templ;
|
|
+ /** Matching template and optional parameter formatting */
|
|
+ const char *templ;
|
|
|
|
- /**
|
|
- * Offset of variable within 'data' parameter of fuse_opt_parse()
|
|
- * or -1
|
|
- */
|
|
- unsigned long offset;
|
|
+ /**
|
|
+ * Offset of variable within 'data' parameter of fuse_opt_parse()
|
|
+ * or -1
|
|
+ */
|
|
+ unsigned long offset;
|
|
|
|
- /**
|
|
- * Value to set the variable to, or to be passed as 'key' to the
|
|
- * processing function. Ignored if template has a format
|
|
- */
|
|
- int value;
|
|
+ /**
|
|
+ * Value to set the variable to, or to be passed as 'key' to the
|
|
+ * processing function. Ignored if template has a format
|
|
+ */
|
|
+ int value;
|
|
};
|
|
|
|
/**
|
|
- * Key option. In case of a match, the processing function will be
|
|
+ * Key option. In case of a match, the processing function will be
|
|
* called with the specified key.
|
|
*/
|
|
-#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
|
|
+#define FUSE_OPT_KEY(templ, key) \
|
|
+ { \
|
|
+ templ, -1U, key \
|
|
+ }
|
|
|
|
/**
|
|
- * Last option. An array of 'struct fuse_opt' must end with a NULL
|
|
+ * Last option. An array of 'struct fuse_opt' must end with a NULL
|
|
* template value
|
|
*/
|
|
-#define FUSE_OPT_END { NULL, 0, 0 }
|
|
+#define FUSE_OPT_END \
|
|
+ { \
|
|
+ NULL, 0, 0 \
|
|
+ }
|
|
|
|
/**
|
|
* Argument list
|
|
*/
|
|
struct fuse_args {
|
|
- /** Argument count */
|
|
- int argc;
|
|
+ /** Argument count */
|
|
+ int argc;
|
|
|
|
- /** Argument vector. NULL terminated */
|
|
- char **argv;
|
|
+ /** Argument vector. NULL terminated */
|
|
+ char **argv;
|
|
|
|
- /** Is 'argv' allocated? */
|
|
- int allocated;
|
|
+ /** Is 'argv' allocated? */
|
|
+ int allocated;
|
|
};
|
|
|
|
/**
|
|
* Initializer for 'struct fuse_args'
|
|
*/
|
|
-#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
|
|
+#define FUSE_ARGS_INIT(argc, argv) \
|
|
+ { \
|
|
+ argc, argv, 0 \
|
|
+ }
|
|
|
|
/**
|
|
* Key value passed to the processing function if an option did not
|
|
* match any template
|
|
*/
|
|
-#define FUSE_OPT_KEY_OPT -1
|
|
+#define FUSE_OPT_KEY_OPT -1
|
|
|
|
/**
|
|
* Key value passed to the processing function for all non-options
|
|
@@ -130,7 +139,7 @@ struct fuse_args {
|
|
* Non-options are the arguments beginning with a character other than
|
|
* '-' or all arguments after the special '--' option
|
|
*/
|
|
-#define FUSE_OPT_KEY_NONOPT -2
|
|
+#define FUSE_OPT_KEY_NONOPT -2
|
|
|
|
/**
|
|
* Special key value for options to keep
|
|
@@ -174,7 +183,7 @@ struct fuse_args {
|
|
* @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
|
|
*/
|
|
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
|
- struct fuse_args *outargs);
|
|
+ struct fuse_args *outargs);
|
|
|
|
/**
|
|
* Option parsing function
|
|
@@ -197,7 +206,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
|
* @return -1 on error, 0 on success
|
|
*/
|
|
int fuse_opt_parse(struct fuse_args *args, void *data,
|
|
- const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
|
+ const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
|
|
|
/**
|
|
* Add an option to a comma separated option list
|
|
diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c
|
|
index 4271947bd4..19d6791cb9 100644
|
|
--- a/tools/virtiofsd/fuse_signals.c
|
|
+++ b/tools/virtiofsd/fuse_signals.c
|
|
@@ -1,91 +1,95 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- Utility functions for setting signal handlers.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * Utility functions for setting signal handlers.
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB
|
|
+ */
|
|
|
|
#include "config.h"
|
|
-#include "fuse_lowlevel.h"
|
|
#include "fuse_i.h"
|
|
+#include "fuse_lowlevel.h"
|
|
|
|
-#include <stdio.h>
|
|
-#include <string.h>
|
|
#include <signal.h>
|
|
+#include <stdio.h>
|
|
#include <stdlib.h>
|
|
+#include <string.h>
|
|
|
|
static struct fuse_session *fuse_instance;
|
|
|
|
static void exit_handler(int sig)
|
|
{
|
|
- if (fuse_instance) {
|
|
- fuse_session_exit(fuse_instance);
|
|
- if(sig <= 0) {
|
|
- fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n");
|
|
- abort();
|
|
- }
|
|
- fuse_instance->error = sig;
|
|
- }
|
|
+ if (fuse_instance) {
|
|
+ fuse_session_exit(fuse_instance);
|
|
+ if (sig <= 0) {
|
|
+ fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n");
|
|
+ abort();
|
|
+ }
|
|
+ fuse_instance->error = sig;
|
|
+ }
|
|
}
|
|
|
|
static void do_nothing(int sig)
|
|
{
|
|
- (void) sig;
|
|
+ (void)sig;
|
|
}
|
|
|
|
static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
|
|
{
|
|
- struct sigaction sa;
|
|
- struct sigaction old_sa;
|
|
+ struct sigaction sa;
|
|
+ struct sigaction old_sa;
|
|
|
|
- memset(&sa, 0, sizeof(struct sigaction));
|
|
- sa.sa_handler = remove ? SIG_DFL : handler;
|
|
- sigemptyset(&(sa.sa_mask));
|
|
- sa.sa_flags = 0;
|
|
+ memset(&sa, 0, sizeof(struct sigaction));
|
|
+ sa.sa_handler = remove ? SIG_DFL : handler;
|
|
+ sigemptyset(&(sa.sa_mask));
|
|
+ sa.sa_flags = 0;
|
|
|
|
- if (sigaction(sig, NULL, &old_sa) == -1) {
|
|
- perror("fuse: cannot get old signal handler");
|
|
- return -1;
|
|
- }
|
|
+ if (sigaction(sig, NULL, &old_sa) == -1) {
|
|
+ perror("fuse: cannot get old signal handler");
|
|
+ return -1;
|
|
+ }
|
|
|
|
- if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
|
|
- sigaction(sig, &sa, NULL) == -1) {
|
|
- perror("fuse: cannot set signal handler");
|
|
- return -1;
|
|
- }
|
|
- return 0;
|
|
+ if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
|
|
+ sigaction(sig, &sa, NULL) == -1) {
|
|
+ perror("fuse: cannot set signal handler");
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
int fuse_set_signal_handlers(struct fuse_session *se)
|
|
{
|
|
- /* If we used SIG_IGN instead of the do_nothing function,
|
|
- then we would be unable to tell if we set SIG_IGN (and
|
|
- thus should reset to SIG_DFL in fuse_remove_signal_handlers)
|
|
- or if it was already set to SIG_IGN (and should be left
|
|
- untouched. */
|
|
- if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
|
|
- set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
|
|
- set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
|
|
- set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1)
|
|
- return -1;
|
|
+ /*
|
|
+ * If we used SIG_IGN instead of the do_nothing function,
|
|
+ * then we would be unable to tell if we set SIG_IGN (and
|
|
+ * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
|
|
+ * or if it was already set to SIG_IGN (and should be left
|
|
+ * untouched.
|
|
+ */
|
|
+ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
|
|
+ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
|
|
+ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
|
|
+ set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) {
|
|
+ return -1;
|
|
+ }
|
|
|
|
- fuse_instance = se;
|
|
- return 0;
|
|
+ fuse_instance = se;
|
|
+ return 0;
|
|
}
|
|
|
|
void fuse_remove_signal_handlers(struct fuse_session *se)
|
|
{
|
|
- if (fuse_instance != se)
|
|
- fuse_log(FUSE_LOG_ERR,
|
|
- "fuse: fuse_remove_signal_handlers: unknown session\n");
|
|
- else
|
|
- fuse_instance = NULL;
|
|
+ if (fuse_instance != se) {
|
|
+ fuse_log(FUSE_LOG_ERR,
|
|
+ "fuse: fuse_remove_signal_handlers: unknown session\n");
|
|
+ } else {
|
|
+ fuse_instance = NULL;
|
|
+ }
|
|
|
|
- set_one_signal_handler(SIGHUP, exit_handler, 1);
|
|
- set_one_signal_handler(SIGINT, exit_handler, 1);
|
|
- set_one_signal_handler(SIGTERM, exit_handler, 1);
|
|
- set_one_signal_handler(SIGPIPE, do_nothing, 1);
|
|
+ set_one_signal_handler(SIGHUP, exit_handler, 1);
|
|
+ set_one_signal_handler(SIGINT, exit_handler, 1);
|
|
+ set_one_signal_handler(SIGTERM, exit_handler, 1);
|
|
+ set_one_signal_handler(SIGPIPE, do_nothing, 1);
|
|
}
|
|
diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c
|
|
index 5a2e64c6d0..5711dd2660 100644
|
|
--- a/tools/virtiofsd/helper.c
|
|
+++ b/tools/virtiofsd/helper.c
|
|
@@ -1,297 +1,309 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * Helper functions to create (simple) standalone programs. With the
|
|
+ * aid of these functions it should be possible to create full FUSE
|
|
+ * file system by implementing nothing but the request handlers.
|
|
|
|
- Helper functions to create (simple) standalone programs. With the
|
|
- aid of these functions it should be possible to create full FUSE
|
|
- file system by implementing nothing but the request handlers.
|
|
-
|
|
- This program can be distributed under the terms of the GNU LGPLv2.
|
|
- See the file COPYING.LIB.
|
|
-*/
|
|
+ * This program can be distributed under the terms of the GNU LGPLv2.
|
|
+ * See the file COPYING.LIB.
|
|
+ */
|
|
|
|
#include "config.h"
|
|
#include "fuse_i.h"
|
|
+#include "fuse_lowlevel.h"
|
|
#include "fuse_misc.h"
|
|
#include "fuse_opt.h"
|
|
-#include "fuse_lowlevel.h"
|
|
#include "mount_util.h"
|
|
|
|
+#include <errno.h>
|
|
+#include <limits.h>
|
|
+#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
-#include <stddef.h>
|
|
-#include <unistd.h>
|
|
#include <string.h>
|
|
-#include <limits.h>
|
|
-#include <errno.h>
|
|
#include <sys/param.h>
|
|
+#include <unistd.h>
|
|
|
|
-#define FUSE_HELPER_OPT(t, p) \
|
|
- { t, offsetof(struct fuse_cmdline_opts, p), 1 }
|
|
+#define FUSE_HELPER_OPT(t, p) \
|
|
+ { \
|
|
+ t, offsetof(struct fuse_cmdline_opts, p), 1 \
|
|
+ }
|
|
|
|
static const struct fuse_opt fuse_helper_opts[] = {
|
|
- FUSE_HELPER_OPT("-h", show_help),
|
|
- FUSE_HELPER_OPT("--help", show_help),
|
|
- FUSE_HELPER_OPT("-V", show_version),
|
|
- FUSE_HELPER_OPT("--version", show_version),
|
|
- FUSE_HELPER_OPT("-d", debug),
|
|
- FUSE_HELPER_OPT("debug", debug),
|
|
- FUSE_HELPER_OPT("-d", foreground),
|
|
- FUSE_HELPER_OPT("debug", foreground),
|
|
- FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
|
- FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
|
- FUSE_HELPER_OPT("-f", foreground),
|
|
- FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
|
- FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
|
- FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
|
- FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
|
- FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
|
|
- FUSE_OPT_END
|
|
+ FUSE_HELPER_OPT("-h", show_help),
|
|
+ FUSE_HELPER_OPT("--help", show_help),
|
|
+ FUSE_HELPER_OPT("-V", show_version),
|
|
+ FUSE_HELPER_OPT("--version", show_version),
|
|
+ FUSE_HELPER_OPT("-d", debug),
|
|
+ FUSE_HELPER_OPT("debug", debug),
|
|
+ FUSE_HELPER_OPT("-d", foreground),
|
|
+ FUSE_HELPER_OPT("debug", foreground),
|
|
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
|
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
|
+ FUSE_HELPER_OPT("-f", foreground),
|
|
+ FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
|
+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
|
+ FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
|
+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
|
+ FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
|
|
+ FUSE_OPT_END
|
|
};
|
|
|
|
struct fuse_conn_info_opts {
|
|
- int atomic_o_trunc;
|
|
- int no_remote_posix_lock;
|
|
- int no_remote_flock;
|
|
- int splice_write;
|
|
- int splice_move;
|
|
- int splice_read;
|
|
- int no_splice_write;
|
|
- int no_splice_move;
|
|
- int no_splice_read;
|
|
- int auto_inval_data;
|
|
- int no_auto_inval_data;
|
|
- int no_readdirplus;
|
|
- int no_readdirplus_auto;
|
|
- int async_dio;
|
|
- int no_async_dio;
|
|
- int writeback_cache;
|
|
- int no_writeback_cache;
|
|
- int async_read;
|
|
- int sync_read;
|
|
- unsigned max_write;
|
|
- unsigned max_readahead;
|
|
- unsigned max_background;
|
|
- unsigned congestion_threshold;
|
|
- unsigned time_gran;
|
|
- int set_max_write;
|
|
- int set_max_readahead;
|
|
- int set_max_background;
|
|
- int set_congestion_threshold;
|
|
- int set_time_gran;
|
|
+ int atomic_o_trunc;
|
|
+ int no_remote_posix_lock;
|
|
+ int no_remote_flock;
|
|
+ int splice_write;
|
|
+ int splice_move;
|
|
+ int splice_read;
|
|
+ int no_splice_write;
|
|
+ int no_splice_move;
|
|
+ int no_splice_read;
|
|
+ int auto_inval_data;
|
|
+ int no_auto_inval_data;
|
|
+ int no_readdirplus;
|
|
+ int no_readdirplus_auto;
|
|
+ int async_dio;
|
|
+ int no_async_dio;
|
|
+ int writeback_cache;
|
|
+ int no_writeback_cache;
|
|
+ int async_read;
|
|
+ int sync_read;
|
|
+ unsigned max_write;
|
|
+ unsigned max_readahead;
|
|
+ unsigned max_background;
|
|
+ unsigned congestion_threshold;
|
|
+ unsigned time_gran;
|
|
+ int set_max_write;
|
|
+ int set_max_readahead;
|
|
+ int set_max_background;
|
|
+ int set_congestion_threshold;
|
|
+ int set_time_gran;
|
|
};
|
|
|
|
-#define CONN_OPTION(t, p, v) \
|
|
- { t, offsetof(struct fuse_conn_info_opts, p), v }
|
|
+#define CONN_OPTION(t, p, v) \
|
|
+ { \
|
|
+ t, offsetof(struct fuse_conn_info_opts, p), v \
|
|
+ }
|
|
static const struct fuse_opt conn_info_opt_spec[] = {
|
|
- CONN_OPTION("max_write=%u", max_write, 0),
|
|
- CONN_OPTION("max_write=", set_max_write, 1),
|
|
- CONN_OPTION("max_readahead=%u", max_readahead, 0),
|
|
- CONN_OPTION("max_readahead=", set_max_readahead, 1),
|
|
- CONN_OPTION("max_background=%u", max_background, 0),
|
|
- CONN_OPTION("max_background=", set_max_background, 1),
|
|
- CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
|
|
- CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
|
|
- CONN_OPTION("sync_read", sync_read, 1),
|
|
- CONN_OPTION("async_read", async_read, 1),
|
|
- CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
|
|
- CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
|
|
- CONN_OPTION("no_remote_lock", no_remote_flock, 1),
|
|
- CONN_OPTION("no_remote_flock", no_remote_flock, 1),
|
|
- CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
|
|
- CONN_OPTION("splice_write", splice_write, 1),
|
|
- CONN_OPTION("no_splice_write", no_splice_write, 1),
|
|
- CONN_OPTION("splice_move", splice_move, 1),
|
|
- CONN_OPTION("no_splice_move", no_splice_move, 1),
|
|
- CONN_OPTION("splice_read", splice_read, 1),
|
|
- CONN_OPTION("no_splice_read", no_splice_read, 1),
|
|
- CONN_OPTION("auto_inval_data", auto_inval_data, 1),
|
|
- CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
|
|
- CONN_OPTION("readdirplus=no", no_readdirplus, 1),
|
|
- CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
|
|
- CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
|
|
- CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
|
|
- CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
|
|
- CONN_OPTION("async_dio", async_dio, 1),
|
|
- CONN_OPTION("no_async_dio", no_async_dio, 1),
|
|
- CONN_OPTION("writeback_cache", writeback_cache, 1),
|
|
- CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
|
|
- CONN_OPTION("time_gran=%u", time_gran, 0),
|
|
- CONN_OPTION("time_gran=", set_time_gran, 1),
|
|
- FUSE_OPT_END
|
|
+ CONN_OPTION("max_write=%u", max_write, 0),
|
|
+ CONN_OPTION("max_write=", set_max_write, 1),
|
|
+ CONN_OPTION("max_readahead=%u", max_readahead, 0),
|
|
+ CONN_OPTION("max_readahead=", set_max_readahead, 1),
|
|
+ CONN_OPTION("max_background=%u", max_background, 0),
|
|
+ CONN_OPTION("max_background=", set_max_background, 1),
|
|
+ CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
|
|
+ CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
|
|
+ CONN_OPTION("sync_read", sync_read, 1),
|
|
+ CONN_OPTION("async_read", async_read, 1),
|
|
+ CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
|
|
+ CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
|
|
+ CONN_OPTION("no_remote_lock", no_remote_flock, 1),
|
|
+ CONN_OPTION("no_remote_flock", no_remote_flock, 1),
|
|
+ CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
|
|
+ CONN_OPTION("splice_write", splice_write, 1),
|
|
+ CONN_OPTION("no_splice_write", no_splice_write, 1),
|
|
+ CONN_OPTION("splice_move", splice_move, 1),
|
|
+ CONN_OPTION("no_splice_move", no_splice_move, 1),
|
|
+ CONN_OPTION("splice_read", splice_read, 1),
|
|
+ CONN_OPTION("no_splice_read", no_splice_read, 1),
|
|
+ CONN_OPTION("auto_inval_data", auto_inval_data, 1),
|
|
+ CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
|
|
+ CONN_OPTION("readdirplus=no", no_readdirplus, 1),
|
|
+ CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
|
|
+ CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
|
|
+ CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
|
|
+ CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
|
|
+ CONN_OPTION("async_dio", async_dio, 1),
|
|
+ CONN_OPTION("no_async_dio", no_async_dio, 1),
|
|
+ CONN_OPTION("writeback_cache", writeback_cache, 1),
|
|
+ CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
|
|
+ CONN_OPTION("time_gran=%u", time_gran, 0),
|
|
+ CONN_OPTION("time_gran=", set_time_gran, 1),
|
|
+ FUSE_OPT_END
|
|
};
|
|
|
|
|
|
void fuse_cmdline_help(void)
|
|
{
|
|
- printf(" -h --help print help\n"
|
|
- " -V --version print version\n"
|
|
- " -d -o debug enable debug output (implies -f)\n"
|
|
- " -f foreground operation\n"
|
|
- " -o max_idle_threads the maximum number of idle worker threads\n"
|
|
- " allowed (default: 10)\n");
|
|
+ printf(
|
|
+ " -h --help print help\n"
|
|
+ " -V --version print version\n"
|
|
+ " -d -o debug enable debug output (implies -f)\n"
|
|
+ " -f foreground operation\n"
|
|
+ " -o max_idle_threads the maximum number of idle worker threads\n"
|
|
+ " allowed (default: 10)\n");
|
|
}
|
|
|
|
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
|
|
- struct fuse_args *outargs)
|
|
+ struct fuse_args *outargs)
|
|
{
|
|
- (void) outargs;
|
|
- struct fuse_cmdline_opts *opts = data;
|
|
-
|
|
- switch (key) {
|
|
- case FUSE_OPT_KEY_NONOPT:
|
|
- if (!opts->mountpoint) {
|
|
- if (fuse_mnt_parse_fuse_fd(arg) != -1) {
|
|
- return fuse_opt_add_opt(&opts->mountpoint, arg);
|
|
- }
|
|
-
|
|
- char mountpoint[PATH_MAX] = "";
|
|
- if (realpath(arg, mountpoint) == NULL) {
|
|
- fuse_log(FUSE_LOG_ERR,
|
|
- "fuse: bad mount point `%s': %s\n",
|
|
- arg, strerror(errno));
|
|
- return -1;
|
|
- }
|
|
- return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
|
|
- } else {
|
|
- fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
|
|
- return -1;
|
|
- }
|
|
-
|
|
- default:
|
|
- /* Pass through unknown options */
|
|
- return 1;
|
|
- }
|
|
+ (void)outargs;
|
|
+ struct fuse_cmdline_opts *opts = data;
|
|
+
|
|
+ switch (key) {
|
|
+ case FUSE_OPT_KEY_NONOPT:
|
|
+ if (!opts->mountpoint) {
|
|
+ if (fuse_mnt_parse_fuse_fd(arg) != -1) {
|
|
+ return fuse_opt_add_opt(&opts->mountpoint, arg);
|
|
+ }
|
|
+
|
|
+ char mountpoint[PATH_MAX] = "";
|
|
+ if (realpath(arg, mountpoint) == NULL) {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg,
|
|
+ strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+ return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
|
|
+ } else {
|
|
+ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ /* Pass through unknown options */
|
|
+ return 1;
|
|
+ }
|
|
}
|
|
|
|
-int fuse_parse_cmdline(struct fuse_args *args,
|
|
- struct fuse_cmdline_opts *opts)
|
|
+int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
|
|
{
|
|
- memset(opts, 0, sizeof(struct fuse_cmdline_opts));
|
|
+ memset(opts, 0, sizeof(struct fuse_cmdline_opts));
|
|
|
|
- opts->max_idle_threads = 10;
|
|
+ opts->max_idle_threads = 10;
|
|
|
|
- if (fuse_opt_parse(args, opts, fuse_helper_opts,
|
|
- fuse_helper_opt_proc) == -1)
|
|
- return -1;
|
|
+ if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) ==
|
|
+ -1) {
|
|
+ return -1;
|
|
+ }
|
|
|
|
- return 0;
|
|
+ return 0;
|
|
}
|
|
|
|
|
|
int fuse_daemonize(int foreground)
|
|
{
|
|
- if (!foreground) {
|
|
- int nullfd;
|
|
- int waiter[2];
|
|
- char completed;
|
|
-
|
|
- if (pipe(waiter)) {
|
|
- perror("fuse_daemonize: pipe");
|
|
- return -1;
|
|
- }
|
|
-
|
|
- /*
|
|
- * demonize current process by forking it and killing the
|
|
- * parent. This makes current process as a child of 'init'.
|
|
- */
|
|
- switch(fork()) {
|
|
- case -1:
|
|
- perror("fuse_daemonize: fork");
|
|
- return -1;
|
|
- case 0:
|
|
- break;
|
|
- default:
|
|
- (void) read(waiter[0], &completed, sizeof(completed));
|
|
- _exit(0);
|
|
- }
|
|
-
|
|
- if (setsid() == -1) {
|
|
- perror("fuse_daemonize: setsid");
|
|
- return -1;
|
|
- }
|
|
-
|
|
- (void) chdir("/");
|
|
-
|
|
- nullfd = open("/dev/null", O_RDWR, 0);
|
|
- if (nullfd != -1) {
|
|
- (void) dup2(nullfd, 0);
|
|
- (void) dup2(nullfd, 1);
|
|
- (void) dup2(nullfd, 2);
|
|
- if (nullfd > 2)
|
|
- close(nullfd);
|
|
- }
|
|
-
|
|
- /* Propagate completion of daemon initialization */
|
|
- completed = 1;
|
|
- (void) write(waiter[1], &completed, sizeof(completed));
|
|
- close(waiter[0]);
|
|
- close(waiter[1]);
|
|
- } else {
|
|
- (void) chdir("/");
|
|
- }
|
|
- return 0;
|
|
+ if (!foreground) {
|
|
+ int nullfd;
|
|
+ int waiter[2];
|
|
+ char completed;
|
|
+
|
|
+ if (pipe(waiter)) {
|
|
+ perror("fuse_daemonize: pipe");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * demonize current process by forking it and killing the
|
|
+ * parent. This makes current process as a child of 'init'.
|
|
+ */
|
|
+ switch (fork()) {
|
|
+ case -1:
|
|
+ perror("fuse_daemonize: fork");
|
|
+ return -1;
|
|
+ case 0:
|
|
+ break;
|
|
+ default:
|
|
+ (void)read(waiter[0], &completed, sizeof(completed));
|
|
+ _exit(0);
|
|
+ }
|
|
+
|
|
+ if (setsid() == -1) {
|
|
+ perror("fuse_daemonize: setsid");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ (void)chdir("/");
|
|
+
|
|
+ nullfd = open("/dev/null", O_RDWR, 0);
|
|
+ if (nullfd != -1) {
|
|
+ (void)dup2(nullfd, 0);
|
|
+ (void)dup2(nullfd, 1);
|
|
+ (void)dup2(nullfd, 2);
|
|
+ if (nullfd > 2) {
|
|
+ close(nullfd);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Propagate completion of daemon initialization */
|
|
+ completed = 1;
|
|
+ (void)write(waiter[1], &completed, sizeof(completed));
|
|
+ close(waiter[0]);
|
|
+ close(waiter[1]);
|
|
+ } else {
|
|
+ (void)chdir("/");
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
|
|
- struct fuse_conn_info *conn)
|
|
+ struct fuse_conn_info *conn)
|
|
{
|
|
- if(opts->set_max_write)
|
|
- conn->max_write = opts->max_write;
|
|
- if(opts->set_max_background)
|
|
- conn->max_background = opts->max_background;
|
|
- if(opts->set_congestion_threshold)
|
|
- conn->congestion_threshold = opts->congestion_threshold;
|
|
- if(opts->set_time_gran)
|
|
- conn->time_gran = opts->time_gran;
|
|
- if(opts->set_max_readahead)
|
|
- conn->max_readahead = opts->max_readahead;
|
|
-
|
|
-#define LL_ENABLE(cond,cap) \
|
|
- if (cond) conn->want |= (cap)
|
|
-#define LL_DISABLE(cond,cap) \
|
|
- if (cond) conn->want &= ~(cap)
|
|
-
|
|
- LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
|
|
- LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
|
|
-
|
|
- LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
|
|
- LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
|
|
-
|
|
- LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
|
|
- LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
|
|
-
|
|
- LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
|
|
- LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
|
|
-
|
|
- LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
|
|
- LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
|
|
-
|
|
- LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
|
|
- LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
|
|
-
|
|
- LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
|
|
- LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
|
|
-
|
|
- LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
|
|
- LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
|
|
-
|
|
- LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
|
|
- LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
|
|
+ if (opts->set_max_write) {
|
|
+ conn->max_write = opts->max_write;
|
|
+ }
|
|
+ if (opts->set_max_background) {
|
|
+ conn->max_background = opts->max_background;
|
|
+ }
|
|
+ if (opts->set_congestion_threshold) {
|
|
+ conn->congestion_threshold = opts->congestion_threshold;
|
|
+ }
|
|
+ if (opts->set_time_gran) {
|
|
+ conn->time_gran = opts->time_gran;
|
|
+ }
|
|
+ if (opts->set_max_readahead) {
|
|
+ conn->max_readahead = opts->max_readahead;
|
|
+ }
|
|
+
|
|
+#define LL_ENABLE(cond, cap) \
|
|
+ if (cond) \
|
|
+ conn->want |= (cap)
|
|
+#define LL_DISABLE(cond, cap) \
|
|
+ if (cond) \
|
|
+ conn->want &= ~(cap)
|
|
+
|
|
+ LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
|
|
+ LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
|
|
+
|
|
+ LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
|
|
+ LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
|
|
+
|
|
+ LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
|
|
+ LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
|
|
+
|
|
+ LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
|
|
+ LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
|
|
+
|
|
+ LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
|
|
+ LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
|
|
+
|
|
+ LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
|
|
+ LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
|
|
+
|
|
+ LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
|
|
+ LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
|
|
+
|
|
+ LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
|
|
+ LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
|
|
+
|
|
+ LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
|
|
+ LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
|
|
}
|
|
|
|
-struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
|
|
+struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args)
|
|
{
|
|
- struct fuse_conn_info_opts *opts;
|
|
-
|
|
- opts = calloc(1, sizeof(struct fuse_conn_info_opts));
|
|
- if(opts == NULL) {
|
|
- fuse_log(FUSE_LOG_ERR, "calloc failed\n");
|
|
- return NULL;
|
|
- }
|
|
- if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
|
|
- free(opts);
|
|
- return NULL;
|
|
- }
|
|
- return opts;
|
|
+ struct fuse_conn_info_opts *opts;
|
|
+
|
|
+ opts = calloc(1, sizeof(struct fuse_conn_info_opts));
|
|
+ if (opts == NULL) {
|
|
+ fuse_log(FUSE_LOG_ERR, "calloc failed\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ if (fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
|
|
+ free(opts);
|
|
+ return NULL;
|
|
+ }
|
|
+ return opts;
|
|
}
|
|
diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h
|
|
index 7c5f561fbc..0b98275ed5 100644
|
|
--- a/tools/virtiofsd/passthrough_helpers.h
|
|
+++ b/tools/virtiofsd/passthrough_helpers.h
|
|
@@ -28,23 +28,24 @@
|
|
* operation
|
|
*/
|
|
static int mknod_wrapper(int dirfd, const char *path, const char *link,
|
|
- int mode, dev_t rdev)
|
|
+ int mode, dev_t rdev)
|
|
{
|
|
- int res;
|
|
+ int res;
|
|
|
|
- if (S_ISREG(mode)) {
|
|
- res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
|
|
- if (res >= 0)
|
|
- res = close(res);
|
|
- } else if (S_ISDIR(mode)) {
|
|
- res = mkdirat(dirfd, path, mode);
|
|
- } else if (S_ISLNK(mode) && link != NULL) {
|
|
- res = symlinkat(link, dirfd, path);
|
|
- } else if (S_ISFIFO(mode)) {
|
|
- res = mkfifoat(dirfd, path, mode);
|
|
- } else {
|
|
- res = mknodat(dirfd, path, mode, rdev);
|
|
- }
|
|
+ if (S_ISREG(mode)) {
|
|
+ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
|
|
+ if (res >= 0) {
|
|
+ res = close(res);
|
|
+ }
|
|
+ } else if (S_ISDIR(mode)) {
|
|
+ res = mkdirat(dirfd, path, mode);
|
|
+ } else if (S_ISLNK(mode) && link != NULL) {
|
|
+ res = symlinkat(link, dirfd, path);
|
|
+ } else if (S_ISFIFO(mode)) {
|
|
+ res = mkfifoat(dirfd, path, mode);
|
|
+ } else {
|
|
+ res = mknodat(dirfd, path, mode, rdev);
|
|
+ }
|
|
|
|
- return res;
|
|
+ return res;
|
|
}
|
|
diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
|
|
index e5f7115bc1..c5850ef803 100644
|
|
--- a/tools/virtiofsd/passthrough_ll.c
|
|
+++ b/tools/virtiofsd/passthrough_ll.c
|
|
@@ -1,12 +1,12 @@
|
|
/*
|
|
- FUSE: Filesystem in Userspace
|
|
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
-
|
|
- This program can be distributed under the terms of the GNU GPLv2.
|
|
- See the file COPYING.
|
|
-*/
|
|
+ * FUSE: Filesystem in Userspace
|
|
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
+ *
|
|
+ * This program can be distributed under the terms of the GNU GPLv2.
|
|
+ * See the file COPYING.
|
|
+ */
|
|
|
|
-/** @file
|
|
+/*
|
|
*
|
|
* This file system mirrors the existing file system hierarchy of the
|
|
* system, starting at the root file system. This is implemented by
|
|
@@ -28,7 +28,8 @@
|
|
*
|
|
* Compile with:
|
|
*
|
|
- * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll
|
|
+ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o
|
|
+ * passthrough_ll
|
|
*
|
|
* ## Source code ##
|
|
* \include passthrough_ll.c
|
|
@@ -39,1299 +40,1365 @@
|
|
|
|
#include "config.h"
|
|
|
|
-#include <fuse_lowlevel.h>
|
|
-#include <unistd.h>
|
|
-#include <stdlib.h>
|
|
-#include <stdio.h>
|
|
-#include <stddef.h>
|
|
-#include <stdbool.h>
|
|
-#include <string.h>
|
|
-#include <limits.h>
|
|
-#include <dirent.h>
|
|
#include <assert.h>
|
|
+#include <dirent.h>
|
|
#include <errno.h>
|
|
+#include <fuse_lowlevel.h>
|
|
#include <inttypes.h>
|
|
+#include <limits.h>
|
|
#include <pthread.h>
|
|
+#include <stdbool.h>
|
|
+#include <stddef.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <sys/xattr.h>
|
|
+#include <unistd.h>
|
|
|
|
#include "passthrough_helpers.h"
|
|
|
|
-/* We are re-using pointers to our `struct lo_inode` and `struct
|
|
- lo_dirp` elements as inodes. This means that we must be able to
|
|
- store uintptr_t values in a fuse_ino_t variable. The following
|
|
- incantation checks this condition at compile time. */
|
|
-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
|
|
+/*
|
|
+ * We are re-using pointers to our `struct lo_inode` and `struct
|
|
+ * lo_dirp` elements as inodes. This means that we must be able to
|
|
+ * store uintptr_t values in a fuse_ino_t variable. The following
|
|
+ * incantation checks this condition at compile time.
|
|
+ */
|
|
+#if defined(__GNUC__) && \
|
|
+ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \
|
|
+ !defined __cplusplus
|
|
_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
|
|
- "fuse_ino_t too small to hold uintptr_t values!");
|
|
+ "fuse_ino_t too small to hold uintptr_t values!");
|
|
#else
|
|
-struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
|
|
- { unsigned _uintptr_to_must_hold_fuse_ino_t:
|
|
- ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
|
|
+struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct {
|
|
+ unsigned _uintptr_to_must_hold_fuse_ino_t
|
|
+ : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1);
|
|
+};
|
|
#endif
|
|
|
|
struct lo_inode {
|
|
- struct lo_inode *next; /* protected by lo->mutex */
|
|
- struct lo_inode *prev; /* protected by lo->mutex */
|
|
- int fd;
|
|
- bool is_symlink;
|
|
- ino_t ino;
|
|
- dev_t dev;
|
|
- uint64_t refcount; /* protected by lo->mutex */
|
|
+ struct lo_inode *next; /* protected by lo->mutex */
|
|
+ struct lo_inode *prev; /* protected by lo->mutex */
|
|
+ int fd;
|
|
+ bool is_symlink;
|
|
+ ino_t ino;
|
|
+ dev_t dev;
|
|
+ uint64_t refcount; /* protected by lo->mutex */
|
|
};
|
|
|
|
enum {
|
|
- CACHE_NEVER,
|
|
- CACHE_NORMAL,
|
|
- CACHE_ALWAYS,
|
|
+ CACHE_NEVER,
|
|
+ CACHE_NORMAL,
|
|
+ CACHE_ALWAYS,
|
|
};
|
|
|
|
struct lo_data {
|
|
- pthread_mutex_t mutex;
|
|
- int debug;
|
|
- int writeback;
|
|
- int flock;
|
|
- int xattr;
|
|
- const char *source;
|
|
- double timeout;
|
|
- int cache;
|
|
- int timeout_set;
|
|
- struct lo_inode root; /* protected by lo->mutex */
|
|
+ pthread_mutex_t mutex;
|
|
+ int debug;
|
|
+ int writeback;
|
|
+ int flock;
|
|
+ int xattr;
|
|
+ const char *source;
|
|
+ double timeout;
|
|
+ int cache;
|
|
+ int timeout_set;
|
|
+ struct lo_inode root; /* protected by lo->mutex */
|
|
};
|
|
|
|
static const struct fuse_opt lo_opts[] = {
|
|
- { "writeback",
|
|
- offsetof(struct lo_data, writeback), 1 },
|
|
- { "no_writeback",
|
|
- offsetof(struct lo_data, writeback), 0 },
|
|
- { "source=%s",
|
|
- offsetof(struct lo_data, source), 0 },
|
|
- { "flock",
|
|
- offsetof(struct lo_data, flock), 1 },
|
|
- { "no_flock",
|
|
- offsetof(struct lo_data, flock), 0 },
|
|
- { "xattr",
|
|
- offsetof(struct lo_data, xattr), 1 },
|
|
- { "no_xattr",
|
|
- offsetof(struct lo_data, xattr), 0 },
|
|
- { "timeout=%lf",
|
|
- offsetof(struct lo_data, timeout), 0 },
|
|
- { "timeout=",
|
|
- offsetof(struct lo_data, timeout_set), 1 },
|
|
- { "cache=never",
|
|
- offsetof(struct lo_data, cache), CACHE_NEVER },
|
|
- { "cache=auto",
|
|
- offsetof(struct lo_data, cache), CACHE_NORMAL },
|
|
- { "cache=always",
|
|
- offsetof(struct lo_data, cache), CACHE_ALWAYS },
|
|
-
|
|
- FUSE_OPT_END
|
|
+ { "writeback", offsetof(struct lo_data, writeback), 1 },
|
|
+ { "no_writeback", offsetof(struct lo_data, writeback), 0 },
|
|
+ { "source=%s", offsetof(struct lo_data, source), 0 },
|
|
+ { "flock", offsetof(struct lo_data, flock), 1 },
|
|
+ { "no_flock", offsetof(struct lo_data, flock), 0 },
|
|
+ { "xattr", offsetof(struct lo_data, xattr), 1 },
|
|
+ { "no_xattr", offsetof(struct lo_data, xattr), 0 },
|
|
+ { "timeout=%lf", offsetof(struct lo_data, timeout), 0 },
|
|
+ { "timeout=", offsetof(struct lo_data, timeout_set), 1 },
|
|
+ { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER },
|
|
+ { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL },
|
|
+ { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
|
|
+
|
|
+ FUSE_OPT_END
|
|
};
|
|
|
|
static struct lo_data *lo_data(fuse_req_t req)
|
|
{
|
|
- return (struct lo_data *) fuse_req_userdata(req);
|
|
+ return (struct lo_data *)fuse_req_userdata(req);
|
|
}
|
|
|
|
static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
|
|
{
|
|
- if (ino == FUSE_ROOT_ID)
|
|
- return &lo_data(req)->root;
|
|
- else
|
|
- return (struct lo_inode *) (uintptr_t) ino;
|
|
+ if (ino == FUSE_ROOT_ID) {
|
|
+ return &lo_data(req)->root;
|
|
+ } else {
|
|
+ return (struct lo_inode *)(uintptr_t)ino;
|
|
+ }
|
|
}
|
|
|
|
static int lo_fd(fuse_req_t req, fuse_ino_t ino)
|
|
{
|
|
- return lo_inode(req, ino)->fd;
|
|
+ return lo_inode(req, ino)->fd;
|
|
}
|
|
|
|
static bool lo_debug(fuse_req_t req)
|
|
{
|
|
- return lo_data(req)->debug != 0;
|
|
+ return lo_data(req)->debug != 0;
|
|
}
|
|
|
|
-static void lo_init(void *userdata,
|
|
- struct fuse_conn_info *conn)
|
|
+static void lo_init(void *userdata, struct fuse_conn_info *conn)
|
|
{
|
|
- struct lo_data *lo = (struct lo_data*) userdata;
|
|
-
|
|
- if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
|
|
- conn->want |= FUSE_CAP_EXPORT_SUPPORT;
|
|
-
|
|
- if (lo->writeback &&
|
|
- conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
|
|
- if (lo->debug)
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n");
|
|
- conn->want |= FUSE_CAP_WRITEBACK_CACHE;
|
|
- }
|
|
- if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
|
|
- if (lo->debug)
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n");
|
|
- conn->want |= FUSE_CAP_FLOCK_LOCKS;
|
|
- }
|
|
+ struct lo_data *lo = (struct lo_data *)userdata;
|
|
+
|
|
+ if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) {
|
|
+ conn->want |= FUSE_CAP_EXPORT_SUPPORT;
|
|
+ }
|
|
+
|
|
+ if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
|
|
+ if (lo->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n");
|
|
+ }
|
|
+ conn->want |= FUSE_CAP_WRITEBACK_CACHE;
|
|
+ }
|
|
+ if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
|
|
+ if (lo->debug) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n");
|
|
+ }
|
|
+ conn->want |= FUSE_CAP_FLOCK_LOCKS;
|
|
+ }
|
|
}
|
|
|
|
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_file_info *fi)
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- int res;
|
|
- struct stat buf;
|
|
- struct lo_data *lo = lo_data(req);
|
|
+ int res;
|
|
+ struct stat buf;
|
|
+ struct lo_data *lo = lo_data(req);
|
|
|
|
- (void) fi;
|
|
+ (void)fi;
|
|
|
|
- res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
- if (res == -1)
|
|
- return (void) fuse_reply_err(req, errno);
|
|
+ res =
|
|
+ fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
+ if (res == -1) {
|
|
+ return (void)fuse_reply_err(req, errno);
|
|
+ }
|
|
|
|
- fuse_reply_attr(req, &buf, lo->timeout);
|
|
+ fuse_reply_attr(req, &buf, lo->timeout);
|
|
}
|
|
|
|
static int utimensat_empty_nofollow(struct lo_inode *inode,
|
|
- const struct timespec *tv)
|
|
+ const struct timespec *tv)
|
|
{
|
|
- int res;
|
|
- char procname[64];
|
|
-
|
|
- if (inode->is_symlink) {
|
|
- res = utimensat(inode->fd, "", tv,
|
|
- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
- if (res == -1 && errno == EINVAL) {
|
|
- /* Sorry, no race free way to set times on symlink. */
|
|
- errno = EPERM;
|
|
- }
|
|
- return res;
|
|
- }
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
-
|
|
- return utimensat(AT_FDCWD, procname, tv, 0);
|
|
+ int res;
|
|
+ char procname[64];
|
|
+
|
|
+ if (inode->is_symlink) {
|
|
+ res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
+ if (res == -1 && errno == EINVAL) {
|
|
+ /* Sorry, no race free way to set times on symlink. */
|
|
+ errno = EPERM;
|
|
+ }
|
|
+ return res;
|
|
+ }
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+
|
|
+ return utimensat(AT_FDCWD, procname, tv, 0);
|
|
}
|
|
|
|
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
|
- int valid, struct fuse_file_info *fi)
|
|
+ int valid, struct fuse_file_info *fi)
|
|
{
|
|
- int saverr;
|
|
- char procname[64];
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- int ifd = inode->fd;
|
|
- int res;
|
|
-
|
|
- if (valid & FUSE_SET_ATTR_MODE) {
|
|
- if (fi) {
|
|
- res = fchmod(fi->fh, attr->st_mode);
|
|
- } else {
|
|
- sprintf(procname, "/proc/self/fd/%i", ifd);
|
|
- res = chmod(procname, attr->st_mode);
|
|
- }
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
- }
|
|
- if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
|
|
- uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
|
|
- attr->st_uid : (uid_t) -1;
|
|
- gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
|
|
- attr->st_gid : (gid_t) -1;
|
|
-
|
|
- res = fchownat(ifd, "", uid, gid,
|
|
- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
- }
|
|
- if (valid & FUSE_SET_ATTR_SIZE) {
|
|
- if (fi) {
|
|
- res = ftruncate(fi->fh, attr->st_size);
|
|
- } else {
|
|
- sprintf(procname, "/proc/self/fd/%i", ifd);
|
|
- res = truncate(procname, attr->st_size);
|
|
- }
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
- }
|
|
- if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
|
|
- struct timespec tv[2];
|
|
-
|
|
- tv[0].tv_sec = 0;
|
|
- tv[1].tv_sec = 0;
|
|
- tv[0].tv_nsec = UTIME_OMIT;
|
|
- tv[1].tv_nsec = UTIME_OMIT;
|
|
-
|
|
- if (valid & FUSE_SET_ATTR_ATIME_NOW)
|
|
- tv[0].tv_nsec = UTIME_NOW;
|
|
- else if (valid & FUSE_SET_ATTR_ATIME)
|
|
- tv[0] = attr->st_atim;
|
|
-
|
|
- if (valid & FUSE_SET_ATTR_MTIME_NOW)
|
|
- tv[1].tv_nsec = UTIME_NOW;
|
|
- else if (valid & FUSE_SET_ATTR_MTIME)
|
|
- tv[1] = attr->st_mtim;
|
|
-
|
|
- if (fi)
|
|
- res = futimens(fi->fh, tv);
|
|
- else
|
|
- res = utimensat_empty_nofollow(inode, tv);
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
- }
|
|
-
|
|
- return lo_getattr(req, ino, fi);
|
|
+ int saverr;
|
|
+ char procname[64];
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ int ifd = inode->fd;
|
|
+ int res;
|
|
+
|
|
+ if (valid & FUSE_SET_ATTR_MODE) {
|
|
+ if (fi) {
|
|
+ res = fchmod(fi->fh, attr->st_mode);
|
|
+ } else {
|
|
+ sprintf(procname, "/proc/self/fd/%i", ifd);
|
|
+ res = chmod(procname, attr->st_mode);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+ if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
|
|
+ uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1;
|
|
+ gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1;
|
|
+
|
|
+ res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+ if (valid & FUSE_SET_ATTR_SIZE) {
|
|
+ if (fi) {
|
|
+ res = ftruncate(fi->fh, attr->st_size);
|
|
+ } else {
|
|
+ sprintf(procname, "/proc/self/fd/%i", ifd);
|
|
+ res = truncate(procname, attr->st_size);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+ if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
|
|
+ struct timespec tv[2];
|
|
+
|
|
+ tv[0].tv_sec = 0;
|
|
+ tv[1].tv_sec = 0;
|
|
+ tv[0].tv_nsec = UTIME_OMIT;
|
|
+ tv[1].tv_nsec = UTIME_OMIT;
|
|
+
|
|
+ if (valid & FUSE_SET_ATTR_ATIME_NOW) {
|
|
+ tv[0].tv_nsec = UTIME_NOW;
|
|
+ } else if (valid & FUSE_SET_ATTR_ATIME) {
|
|
+ tv[0] = attr->st_atim;
|
|
+ }
|
|
+
|
|
+ if (valid & FUSE_SET_ATTR_MTIME_NOW) {
|
|
+ tv[1].tv_nsec = UTIME_NOW;
|
|
+ } else if (valid & FUSE_SET_ATTR_MTIME) {
|
|
+ tv[1] = attr->st_mtim;
|
|
+ }
|
|
+
|
|
+ if (fi) {
|
|
+ res = futimens(fi->fh, tv);
|
|
+ } else {
|
|
+ res = utimensat_empty_nofollow(inode, tv);
|
|
+ }
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return lo_getattr(req, ino, fi);
|
|
|
|
out_err:
|
|
- saverr = errno;
|
|
- fuse_reply_err(req, saverr);
|
|
+ saverr = errno;
|
|
+ fuse_reply_err(req, saverr);
|
|
}
|
|
|
|
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
|
|
{
|
|
- struct lo_inode *p;
|
|
- struct lo_inode *ret = NULL;
|
|
-
|
|
- pthread_mutex_lock(&lo->mutex);
|
|
- for (p = lo->root.next; p != &lo->root; p = p->next) {
|
|
- if (p->ino == st->st_ino && p->dev == st->st_dev) {
|
|
- assert(p->refcount > 0);
|
|
- ret = p;
|
|
- ret->refcount++;
|
|
- break;
|
|
- }
|
|
- }
|
|
- pthread_mutex_unlock(&lo->mutex);
|
|
- return ret;
|
|
+ struct lo_inode *p;
|
|
+ struct lo_inode *ret = NULL;
|
|
+
|
|
+ pthread_mutex_lock(&lo->mutex);
|
|
+ for (p = lo->root.next; p != &lo->root; p = p->next) {
|
|
+ if (p->ino == st->st_ino && p->dev == st->st_dev) {
|
|
+ assert(p->refcount > 0);
|
|
+ ret = p;
|
|
+ ret->refcount++;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ pthread_mutex_unlock(&lo->mutex);
|
|
+ return ret;
|
|
}
|
|
|
|
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- struct fuse_entry_param *e)
|
|
+ struct fuse_entry_param *e)
|
|
{
|
|
- int newfd;
|
|
- int res;
|
|
- int saverr;
|
|
- struct lo_data *lo = lo_data(req);
|
|
- struct lo_inode *inode;
|
|
-
|
|
- memset(e, 0, sizeof(*e));
|
|
- e->attr_timeout = lo->timeout;
|
|
- e->entry_timeout = lo->timeout;
|
|
-
|
|
- newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
|
|
- if (newfd == -1)
|
|
- goto out_err;
|
|
-
|
|
- res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
-
|
|
- inode = lo_find(lo_data(req), &e->attr);
|
|
- if (inode) {
|
|
- close(newfd);
|
|
- newfd = -1;
|
|
- } else {
|
|
- struct lo_inode *prev, *next;
|
|
-
|
|
- saverr = ENOMEM;
|
|
- inode = calloc(1, sizeof(struct lo_inode));
|
|
- if (!inode)
|
|
- goto out_err;
|
|
-
|
|
- inode->is_symlink = S_ISLNK(e->attr.st_mode);
|
|
- inode->refcount = 1;
|
|
- inode->fd = newfd;
|
|
- inode->ino = e->attr.st_ino;
|
|
- inode->dev = e->attr.st_dev;
|
|
-
|
|
- pthread_mutex_lock(&lo->mutex);
|
|
- prev = &lo->root;
|
|
- next = prev->next;
|
|
- next->prev = inode;
|
|
- inode->next = next;
|
|
- inode->prev = prev;
|
|
- prev->next = inode;
|
|
- pthread_mutex_unlock(&lo->mutex);
|
|
- }
|
|
- e->ino = (uintptr_t) inode;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
- (unsigned long long) parent, name, (unsigned long long) e->ino);
|
|
-
|
|
- return 0;
|
|
+ int newfd;
|
|
+ int res;
|
|
+ int saverr;
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+ struct lo_inode *inode;
|
|
+
|
|
+ memset(e, 0, sizeof(*e));
|
|
+ e->attr_timeout = lo->timeout;
|
|
+ e->entry_timeout = lo->timeout;
|
|
+
|
|
+ newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
|
|
+ if (newfd == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ inode = lo_find(lo_data(req), &e->attr);
|
|
+ if (inode) {
|
|
+ close(newfd);
|
|
+ newfd = -1;
|
|
+ } else {
|
|
+ struct lo_inode *prev, *next;
|
|
+
|
|
+ saverr = ENOMEM;
|
|
+ inode = calloc(1, sizeof(struct lo_inode));
|
|
+ if (!inode) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ inode->is_symlink = S_ISLNK(e->attr.st_mode);
|
|
+ inode->refcount = 1;
|
|
+ inode->fd = newfd;
|
|
+ inode->ino = e->attr.st_ino;
|
|
+ inode->dev = e->attr.st_dev;
|
|
+
|
|
+ pthread_mutex_lock(&lo->mutex);
|
|
+ prev = &lo->root;
|
|
+ next = prev->next;
|
|
+ next->prev = inode;
|
|
+ inode->next = next;
|
|
+ inode->prev = prev;
|
|
+ prev->next = inode;
|
|
+ pthread_mutex_unlock(&lo->mutex);
|
|
+ }
|
|
+ e->ino = (uintptr_t)inode;
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
+ (unsigned long long)parent, name, (unsigned long long)e->ino);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
|
|
out_err:
|
|
- saverr = errno;
|
|
- if (newfd != -1)
|
|
- close(newfd);
|
|
- return saverr;
|
|
+ saverr = errno;
|
|
+ if (newfd != -1) {
|
|
+ close(newfd);
|
|
+ }
|
|
+ return saverr;
|
|
}
|
|
|
|
static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
|
{
|
|
- struct fuse_entry_param e;
|
|
- int err;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
|
|
- parent, name);
|
|
-
|
|
- err = lo_do_lookup(req, parent, name, &e);
|
|
- if (err)
|
|
- fuse_reply_err(req, err);
|
|
- else
|
|
- fuse_reply_entry(req, &e);
|
|
+ struct fuse_entry_param e;
|
|
+ int err;
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
|
|
+ parent, name);
|
|
+ }
|
|
+
|
|
+ err = lo_do_lookup(req, parent, name, &e);
|
|
+ if (err) {
|
|
+ fuse_reply_err(req, err);
|
|
+ } else {
|
|
+ fuse_reply_entry(req, &e);
|
|
+ }
|
|
}
|
|
|
|
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
|
|
- const char *name, mode_t mode, dev_t rdev,
|
|
- const char *link)
|
|
+ const char *name, mode_t mode, dev_t rdev,
|
|
+ const char *link)
|
|
{
|
|
- int res;
|
|
- int saverr;
|
|
- struct lo_inode *dir = lo_inode(req, parent);
|
|
- struct fuse_entry_param e;
|
|
+ int res;
|
|
+ int saverr;
|
|
+ struct lo_inode *dir = lo_inode(req, parent);
|
|
+ struct fuse_entry_param e;
|
|
|
|
- saverr = ENOMEM;
|
|
+ saverr = ENOMEM;
|
|
|
|
- res = mknod_wrapper(dir->fd, name, link, mode, rdev);
|
|
+ res = mknod_wrapper(dir->fd, name, link, mode, rdev);
|
|
|
|
- saverr = errno;
|
|
- if (res == -1)
|
|
- goto out;
|
|
+ saverr = errno;
|
|
+ if (res == -1) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- saverr = lo_do_lookup(req, parent, name, &e);
|
|
- if (saverr)
|
|
- goto out;
|
|
+ saverr = lo_do_lookup(req, parent, name, &e);
|
|
+ if (saverr) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
- (unsigned long long) parent, name, (unsigned long long) e.ino);
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
+ (unsigned long long)parent, name, (unsigned long long)e.ino);
|
|
+ }
|
|
|
|
- fuse_reply_entry(req, &e);
|
|
- return;
|
|
+ fuse_reply_entry(req, &e);
|
|
+ return;
|
|
|
|
out:
|
|
- fuse_reply_err(req, saverr);
|
|
+ fuse_reply_err(req, saverr);
|
|
}
|
|
|
|
-static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
|
|
- const char *name, mode_t mode, dev_t rdev)
|
|
+static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
+ mode_t mode, dev_t rdev)
|
|
{
|
|
- lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
|
|
+ lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
|
|
}
|
|
|
|
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- mode_t mode)
|
|
+ mode_t mode)
|
|
{
|
|
- lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
|
|
+ lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
|
|
}
|
|
|
|
-static void lo_symlink(fuse_req_t req, const char *link,
|
|
- fuse_ino_t parent, const char *name)
|
|
+static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent,
|
|
+ const char *name)
|
|
{
|
|
- lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
|
|
+ lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
|
|
}
|
|
|
|
static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
|
|
- const char *name)
|
|
+ const char *name)
|
|
{
|
|
- int res;
|
|
- char procname[64];
|
|
+ int res;
|
|
+ char procname[64];
|
|
|
|
- if (inode->is_symlink) {
|
|
- res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
|
|
- if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
|
|
- /* Sorry, no race free way to hard-link a symlink. */
|
|
- errno = EPERM;
|
|
- }
|
|
- return res;
|
|
- }
|
|
+ if (inode->is_symlink) {
|
|
+ res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
|
|
+ if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
|
|
+ /* Sorry, no race free way to hard-link a symlink. */
|
|
+ errno = EPERM;
|
|
+ }
|
|
+ return res;
|
|
+ }
|
|
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
|
|
- return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
|
|
+ return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
|
|
}
|
|
|
|
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
|
|
- const char *name)
|
|
+ const char *name)
|
|
{
|
|
- int res;
|
|
- struct lo_data *lo = lo_data(req);
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- struct fuse_entry_param e;
|
|
- int saverr;
|
|
-
|
|
- memset(&e, 0, sizeof(struct fuse_entry_param));
|
|
- e.attr_timeout = lo->timeout;
|
|
- e.entry_timeout = lo->timeout;
|
|
-
|
|
- res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
-
|
|
- res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
- if (res == -1)
|
|
- goto out_err;
|
|
-
|
|
- pthread_mutex_lock(&lo->mutex);
|
|
- inode->refcount++;
|
|
- pthread_mutex_unlock(&lo->mutex);
|
|
- e.ino = (uintptr_t) inode;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
- (unsigned long long) parent, name,
|
|
- (unsigned long long) e.ino);
|
|
-
|
|
- fuse_reply_entry(req, &e);
|
|
- return;
|
|
+ int res;
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ struct fuse_entry_param e;
|
|
+ int saverr;
|
|
+
|
|
+ memset(&e, 0, sizeof(struct fuse_entry_param));
|
|
+ e.attr_timeout = lo->timeout;
|
|
+ e.entry_timeout = lo->timeout;
|
|
+
|
|
+ res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
+ if (res == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ pthread_mutex_lock(&lo->mutex);
|
|
+ inode->refcount++;
|
|
+ pthread_mutex_unlock(&lo->mutex);
|
|
+ e.ino = (uintptr_t)inode;
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
|
|
+ (unsigned long long)parent, name, (unsigned long long)e.ino);
|
|
+ }
|
|
+
|
|
+ fuse_reply_entry(req, &e);
|
|
+ return;
|
|
|
|
out_err:
|
|
- saverr = errno;
|
|
- fuse_reply_err(req, saverr);
|
|
+ saverr = errno;
|
|
+ fuse_reply_err(req, saverr);
|
|
}
|
|
|
|
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
|
|
{
|
|
- int res;
|
|
+ int res;
|
|
|
|
- res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
|
|
+ res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
|
|
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- fuse_ino_t newparent, const char *newname,
|
|
- unsigned int flags)
|
|
+ fuse_ino_t newparent, const char *newname,
|
|
+ unsigned int flags)
|
|
{
|
|
- int res;
|
|
+ int res;
|
|
|
|
- if (flags) {
|
|
- fuse_reply_err(req, EINVAL);
|
|
- return;
|
|
- }
|
|
+ if (flags) {
|
|
+ fuse_reply_err(req, EINVAL);
|
|
+ return;
|
|
+ }
|
|
|
|
- res = renameat(lo_fd(req, parent), name,
|
|
- lo_fd(req, newparent), newname);
|
|
+ res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname);
|
|
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
|
|
{
|
|
- int res;
|
|
+ int res;
|
|
|
|
- res = unlinkat(lo_fd(req, parent), name, 0);
|
|
+ res = unlinkat(lo_fd(req, parent), name, 0);
|
|
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
|
|
{
|
|
- if (!inode)
|
|
- return;
|
|
-
|
|
- pthread_mutex_lock(&lo->mutex);
|
|
- assert(inode->refcount >= n);
|
|
- inode->refcount -= n;
|
|
- if (!inode->refcount) {
|
|
- struct lo_inode *prev, *next;
|
|
-
|
|
- prev = inode->prev;
|
|
- next = inode->next;
|
|
- next->prev = prev;
|
|
- prev->next = next;
|
|
-
|
|
- pthread_mutex_unlock(&lo->mutex);
|
|
- close(inode->fd);
|
|
- free(inode);
|
|
-
|
|
- } else {
|
|
- pthread_mutex_unlock(&lo->mutex);
|
|
- }
|
|
+ if (!inode) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pthread_mutex_lock(&lo->mutex);
|
|
+ assert(inode->refcount >= n);
|
|
+ inode->refcount -= n;
|
|
+ if (!inode->refcount) {
|
|
+ struct lo_inode *prev, *next;
|
|
+
|
|
+ prev = inode->prev;
|
|
+ next = inode->next;
|
|
+ next->prev = prev;
|
|
+ prev->next = next;
|
|
+
|
|
+ pthread_mutex_unlock(&lo->mutex);
|
|
+ close(inode->fd);
|
|
+ free(inode);
|
|
+
|
|
+ } else {
|
|
+ pthread_mutex_unlock(&lo->mutex);
|
|
+ }
|
|
}
|
|
|
|
static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
|
|
{
|
|
- struct lo_data *lo = lo_data(req);
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
|
|
- if (lo_debug(req)) {
|
|
- fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
|
|
- (unsigned long long) ino,
|
|
- (unsigned long long) inode->refcount,
|
|
- (unsigned long long) nlookup);
|
|
- }
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
|
|
+ (unsigned long long)ino, (unsigned long long)inode->refcount,
|
|
+ (unsigned long long)nlookup);
|
|
+ }
|
|
|
|
- unref_inode(lo, inode, nlookup);
|
|
+ unref_inode(lo, inode, nlookup);
|
|
}
|
|
|
|
static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
|
|
{
|
|
- lo_forget_one(req, ino, nlookup);
|
|
- fuse_reply_none(req);
|
|
+ lo_forget_one(req, ino, nlookup);
|
|
+ fuse_reply_none(req);
|
|
}
|
|
|
|
static void lo_forget_multi(fuse_req_t req, size_t count,
|
|
- struct fuse_forget_data *forgets)
|
|
+ struct fuse_forget_data *forgets)
|
|
{
|
|
- int i;
|
|
+ int i;
|
|
|
|
- for (i = 0; i < count; i++)
|
|
- lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
|
|
- fuse_reply_none(req);
|
|
+ for (i = 0; i < count; i++) {
|
|
+ lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
|
|
+ }
|
|
+ fuse_reply_none(req);
|
|
}
|
|
|
|
static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
|
|
{
|
|
- char buf[PATH_MAX + 1];
|
|
- int res;
|
|
+ char buf[PATH_MAX + 1];
|
|
+ int res;
|
|
|
|
- res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
|
|
- if (res == -1)
|
|
- return (void) fuse_reply_err(req, errno);
|
|
+ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
|
|
+ if (res == -1) {
|
|
+ return (void)fuse_reply_err(req, errno);
|
|
+ }
|
|
|
|
- if (res == sizeof(buf))
|
|
- return (void) fuse_reply_err(req, ENAMETOOLONG);
|
|
+ if (res == sizeof(buf)) {
|
|
+ return (void)fuse_reply_err(req, ENAMETOOLONG);
|
|
+ }
|
|
|
|
- buf[res] = '\0';
|
|
+ buf[res] = '\0';
|
|
|
|
- fuse_reply_readlink(req, buf);
|
|
+ fuse_reply_readlink(req, buf);
|
|
}
|
|
|
|
struct lo_dirp {
|
|
- DIR *dp;
|
|
- struct dirent *entry;
|
|
- off_t offset;
|
|
+ DIR *dp;
|
|
+ struct dirent *entry;
|
|
+ off_t offset;
|
|
};
|
|
|
|
static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
|
|
{
|
|
- return (struct lo_dirp *) (uintptr_t) fi->fh;
|
|
+ return (struct lo_dirp *)(uintptr_t)fi->fh;
|
|
}
|
|
|
|
-static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
|
+static void lo_opendir(fuse_req_t req, fuse_ino_t ino,
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- int error = ENOMEM;
|
|
- struct lo_data *lo = lo_data(req);
|
|
- struct lo_dirp *d;
|
|
- int fd;
|
|
-
|
|
- d = calloc(1, sizeof(struct lo_dirp));
|
|
- if (d == NULL)
|
|
- goto out_err;
|
|
-
|
|
- fd = openat(lo_fd(req, ino), ".", O_RDONLY);
|
|
- if (fd == -1)
|
|
- goto out_errno;
|
|
-
|
|
- d->dp = fdopendir(fd);
|
|
- if (d->dp == NULL)
|
|
- goto out_errno;
|
|
-
|
|
- d->offset = 0;
|
|
- d->entry = NULL;
|
|
-
|
|
- fi->fh = (uintptr_t) d;
|
|
- if (lo->cache == CACHE_ALWAYS)
|
|
- fi->keep_cache = 1;
|
|
- fuse_reply_open(req, fi);
|
|
- return;
|
|
+ int error = ENOMEM;
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+ struct lo_dirp *d;
|
|
+ int fd;
|
|
+
|
|
+ d = calloc(1, sizeof(struct lo_dirp));
|
|
+ if (d == NULL) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ fd = openat(lo_fd(req, ino), ".", O_RDONLY);
|
|
+ if (fd == -1) {
|
|
+ goto out_errno;
|
|
+ }
|
|
+
|
|
+ d->dp = fdopendir(fd);
|
|
+ if (d->dp == NULL) {
|
|
+ goto out_errno;
|
|
+ }
|
|
+
|
|
+ d->offset = 0;
|
|
+ d->entry = NULL;
|
|
+
|
|
+ fi->fh = (uintptr_t)d;
|
|
+ if (lo->cache == CACHE_ALWAYS) {
|
|
+ fi->keep_cache = 1;
|
|
+ }
|
|
+ fuse_reply_open(req, fi);
|
|
+ return;
|
|
|
|
out_errno:
|
|
- error = errno;
|
|
+ error = errno;
|
|
out_err:
|
|
- if (d) {
|
|
- if (fd != -1)
|
|
- close(fd);
|
|
- free(d);
|
|
- }
|
|
- fuse_reply_err(req, error);
|
|
+ if (d) {
|
|
+ if (fd != -1) {
|
|
+ close(fd);
|
|
+ }
|
|
+ free(d);
|
|
+ }
|
|
+ fuse_reply_err(req, error);
|
|
}
|
|
|
|
static int is_dot_or_dotdot(const char *name)
|
|
{
|
|
- return name[0] == '.' && (name[1] == '\0' ||
|
|
- (name[1] == '.' && name[2] == '\0'));
|
|
+ return name[0] == '.' &&
|
|
+ (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
|
|
}
|
|
|
|
static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
- off_t offset, struct fuse_file_info *fi, int plus)
|
|
+ off_t offset, struct fuse_file_info *fi, int plus)
|
|
{
|
|
- struct lo_dirp *d = lo_dirp(fi);
|
|
- char *buf;
|
|
- char *p;
|
|
- size_t rem = size;
|
|
- int err;
|
|
-
|
|
- (void) ino;
|
|
-
|
|
- buf = calloc(1, size);
|
|
- if (!buf) {
|
|
- err = ENOMEM;
|
|
- goto error;
|
|
- }
|
|
- p = buf;
|
|
-
|
|
- if (offset != d->offset) {
|
|
- seekdir(d->dp, offset);
|
|
- d->entry = NULL;
|
|
- d->offset = offset;
|
|
- }
|
|
- while (1) {
|
|
- size_t entsize;
|
|
- off_t nextoff;
|
|
- const char *name;
|
|
-
|
|
- if (!d->entry) {
|
|
- errno = 0;
|
|
- d->entry = readdir(d->dp);
|
|
- if (!d->entry) {
|
|
- if (errno) { // Error
|
|
- err = errno;
|
|
- goto error;
|
|
- } else { // End of stream
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
- nextoff = d->entry->d_off;
|
|
- name = d->entry->d_name;
|
|
- fuse_ino_t entry_ino = 0;
|
|
- if (plus) {
|
|
- struct fuse_entry_param e;
|
|
- if (is_dot_or_dotdot(name)) {
|
|
- e = (struct fuse_entry_param) {
|
|
- .attr.st_ino = d->entry->d_ino,
|
|
- .attr.st_mode = d->entry->d_type << 12,
|
|
- };
|
|
- } else {
|
|
- err = lo_do_lookup(req, ino, name, &e);
|
|
- if (err)
|
|
- goto error;
|
|
- entry_ino = e.ino;
|
|
- }
|
|
-
|
|
- entsize = fuse_add_direntry_plus(req, p, rem, name,
|
|
- &e, nextoff);
|
|
- } else {
|
|
- struct stat st = {
|
|
- .st_ino = d->entry->d_ino,
|
|
- .st_mode = d->entry->d_type << 12,
|
|
- };
|
|
- entsize = fuse_add_direntry(req, p, rem, name,
|
|
- &st, nextoff);
|
|
- }
|
|
- if (entsize > rem) {
|
|
- if (entry_ino != 0)
|
|
- lo_forget_one(req, entry_ino, 1);
|
|
- break;
|
|
- }
|
|
-
|
|
- p += entsize;
|
|
- rem -= entsize;
|
|
-
|
|
- d->entry = NULL;
|
|
- d->offset = nextoff;
|
|
- }
|
|
+ struct lo_dirp *d = lo_dirp(fi);
|
|
+ char *buf;
|
|
+ char *p;
|
|
+ size_t rem = size;
|
|
+ int err;
|
|
+
|
|
+ (void)ino;
|
|
+
|
|
+ buf = calloc(1, size);
|
|
+ if (!buf) {
|
|
+ err = ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+ p = buf;
|
|
+
|
|
+ if (offset != d->offset) {
|
|
+ seekdir(d->dp, offset);
|
|
+ d->entry = NULL;
|
|
+ d->offset = offset;
|
|
+ }
|
|
+ while (1) {
|
|
+ size_t entsize;
|
|
+ off_t nextoff;
|
|
+ const char *name;
|
|
+
|
|
+ if (!d->entry) {
|
|
+ errno = 0;
|
|
+ d->entry = readdir(d->dp);
|
|
+ if (!d->entry) {
|
|
+ if (errno) { /* Error */
|
|
+ err = errno;
|
|
+ goto error;
|
|
+ } else { /* End of stream */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ nextoff = d->entry->d_off;
|
|
+ name = d->entry->d_name;
|
|
+ fuse_ino_t entry_ino = 0;
|
|
+ if (plus) {
|
|
+ struct fuse_entry_param e;
|
|
+ if (is_dot_or_dotdot(name)) {
|
|
+ e = (struct fuse_entry_param){
|
|
+ .attr.st_ino = d->entry->d_ino,
|
|
+ .attr.st_mode = d->entry->d_type << 12,
|
|
+ };
|
|
+ } else {
|
|
+ err = lo_do_lookup(req, ino, name, &e);
|
|
+ if (err) {
|
|
+ goto error;
|
|
+ }
|
|
+ entry_ino = e.ino;
|
|
+ }
|
|
+
|
|
+ entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff);
|
|
+ } else {
|
|
+ struct stat st = {
|
|
+ .st_ino = d->entry->d_ino,
|
|
+ .st_mode = d->entry->d_type << 12,
|
|
+ };
|
|
+ entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff);
|
|
+ }
|
|
+ if (entsize > rem) {
|
|
+ if (entry_ino != 0) {
|
|
+ lo_forget_one(req, entry_ino, 1);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ p += entsize;
|
|
+ rem -= entsize;
|
|
+
|
|
+ d->entry = NULL;
|
|
+ d->offset = nextoff;
|
|
+ }
|
|
|
|
err = 0;
|
|
error:
|
|
- // If there's an error, we can only signal it if we haven't stored
|
|
- // any entries yet - otherwise we'd end up with wrong lookup
|
|
- // counts for the entries that are already in the buffer. So we
|
|
- // return what we've collected until that point.
|
|
- if (err && rem == size)
|
|
- fuse_reply_err(req, err);
|
|
- else
|
|
- fuse_reply_buf(req, buf, size - rem);
|
|
+ /*
|
|
+ * If there's an error, we can only signal it if we haven't stored
|
|
+ * any entries yet - otherwise we'd end up with wrong lookup
|
|
+ * counts for the entries that are already in the buffer. So we
|
|
+ * return what we've collected until that point.
|
|
+ */
|
|
+ if (err && rem == size) {
|
|
+ fuse_reply_err(req, err);
|
|
+ } else {
|
|
+ fuse_reply_buf(req, buf, size - rem);
|
|
+ }
|
|
free(buf);
|
|
}
|
|
|
|
static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
- off_t offset, struct fuse_file_info *fi)
|
|
+ off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
- lo_do_readdir(req, ino, size, offset, fi, 0);
|
|
+ lo_do_readdir(req, ino, size, offset, fi, 0);
|
|
}
|
|
|
|
static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
- off_t offset, struct fuse_file_info *fi)
|
|
+ off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
- lo_do_readdir(req, ino, size, offset, fi, 1);
|
|
+ lo_do_readdir(req, ino, size, offset, fi, 1);
|
|
}
|
|
|
|
-static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
|
+static void lo_releasedir(fuse_req_t req, fuse_ino_t ino,
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- struct lo_dirp *d = lo_dirp(fi);
|
|
- (void) ino;
|
|
- closedir(d->dp);
|
|
- free(d);
|
|
- fuse_reply_err(req, 0);
|
|
+ struct lo_dirp *d = lo_dirp(fi);
|
|
+ (void)ino;
|
|
+ closedir(d->dp);
|
|
+ free(d);
|
|
+ fuse_reply_err(req, 0);
|
|
}
|
|
|
|
static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|
- mode_t mode, struct fuse_file_info *fi)
|
|
+ mode_t mode, struct fuse_file_info *fi)
|
|
{
|
|
- int fd;
|
|
- struct lo_data *lo = lo_data(req);
|
|
- struct fuse_entry_param e;
|
|
- int err;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
|
|
- parent, name);
|
|
-
|
|
- fd = openat(lo_fd(req, parent), name,
|
|
- (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
|
|
- if (fd == -1)
|
|
- return (void) fuse_reply_err(req, errno);
|
|
-
|
|
- fi->fh = fd;
|
|
- if (lo->cache == CACHE_NEVER)
|
|
- fi->direct_io = 1;
|
|
- else if (lo->cache == CACHE_ALWAYS)
|
|
- fi->keep_cache = 1;
|
|
-
|
|
- err = lo_do_lookup(req, parent, name, &e);
|
|
- if (err)
|
|
- fuse_reply_err(req, err);
|
|
- else
|
|
- fuse_reply_create(req, &e, fi);
|
|
+ int fd;
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+ struct fuse_entry_param e;
|
|
+ int err;
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
|
|
+ parent, name);
|
|
+ }
|
|
+
|
|
+ fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
|
|
+ mode);
|
|
+ if (fd == -1) {
|
|
+ return (void)fuse_reply_err(req, errno);
|
|
+ }
|
|
+
|
|
+ fi->fh = fd;
|
|
+ if (lo->cache == CACHE_NEVER) {
|
|
+ fi->direct_io = 1;
|
|
+ } else if (lo->cache == CACHE_ALWAYS) {
|
|
+ fi->keep_cache = 1;
|
|
+ }
|
|
+
|
|
+ err = lo_do_lookup(req, parent, name, &e);
|
|
+ if (err) {
|
|
+ fuse_reply_err(req, err);
|
|
+ } else {
|
|
+ fuse_reply_create(req, &e, fi);
|
|
+ }
|
|
}
|
|
|
|
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
- struct fuse_file_info *fi)
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- int res;
|
|
- int fd = dirfd(lo_dirp(fi)->dp);
|
|
- (void) ino;
|
|
- if (datasync)
|
|
- res = fdatasync(fd);
|
|
- else
|
|
- res = fsync(fd);
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ int res;
|
|
+ int fd = dirfd(lo_dirp(fi)->dp);
|
|
+ (void)ino;
|
|
+ if (datasync) {
|
|
+ res = fdatasync(fd);
|
|
+ } else {
|
|
+ res = fsync(fd);
|
|
+ }
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
|
{
|
|
- int fd;
|
|
- char buf[64];
|
|
- struct lo_data *lo = lo_data(req);
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
|
|
- ino, fi->flags);
|
|
-
|
|
- /* With writeback cache, kernel may send read requests even
|
|
- when userspace opened write-only */
|
|
- if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
|
|
- fi->flags &= ~O_ACCMODE;
|
|
- fi->flags |= O_RDWR;
|
|
- }
|
|
-
|
|
- /* With writeback cache, O_APPEND is handled by the kernel.
|
|
- This breaks atomicity (since the file may change in the
|
|
- underlying filesystem, so that the kernel's idea of the
|
|
- end of the file isn't accurate anymore). In this example,
|
|
- we just accept that. A more rigorous filesystem may want
|
|
- to return an error here */
|
|
- if (lo->writeback && (fi->flags & O_APPEND))
|
|
- fi->flags &= ~O_APPEND;
|
|
-
|
|
- sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
|
|
- fd = open(buf, fi->flags & ~O_NOFOLLOW);
|
|
- if (fd == -1)
|
|
- return (void) fuse_reply_err(req, errno);
|
|
-
|
|
- fi->fh = fd;
|
|
- if (lo->cache == CACHE_NEVER)
|
|
- fi->direct_io = 1;
|
|
- else if (lo->cache == CACHE_ALWAYS)
|
|
- fi->keep_cache = 1;
|
|
- fuse_reply_open(req, fi);
|
|
+ int fd;
|
|
+ char buf[64];
|
|
+ struct lo_data *lo = lo_data(req);
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino,
|
|
+ fi->flags);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * With writeback cache, kernel may send read requests even
|
|
+ * when userspace opened write-only
|
|
+ */
|
|
+ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
|
|
+ fi->flags &= ~O_ACCMODE;
|
|
+ fi->flags |= O_RDWR;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * With writeback cache, O_APPEND is handled by the kernel.
|
|
+ * This breaks atomicity (since the file may change in the
|
|
+ * underlying filesystem, so that the kernel's idea of the
|
|
+ * end of the file isn't accurate anymore). In this example,
|
|
+ * we just accept that. A more rigorous filesystem may want
|
|
+ * to return an error here
|
|
+ */
|
|
+ if (lo->writeback && (fi->flags & O_APPEND)) {
|
|
+ fi->flags &= ~O_APPEND;
|
|
+ }
|
|
+
|
|
+ sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
|
|
+ fd = open(buf, fi->flags & ~O_NOFOLLOW);
|
|
+ if (fd == -1) {
|
|
+ return (void)fuse_reply_err(req, errno);
|
|
+ }
|
|
+
|
|
+ fi->fh = fd;
|
|
+ if (lo->cache == CACHE_NEVER) {
|
|
+ fi->direct_io = 1;
|
|
+ } else if (lo->cache == CACHE_ALWAYS) {
|
|
+ fi->keep_cache = 1;
|
|
+ }
|
|
+ fuse_reply_open(req, fi);
|
|
}
|
|
|
|
-static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
|
+static void lo_release(fuse_req_t req, fuse_ino_t ino,
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- (void) ino;
|
|
+ (void)ino;
|
|
|
|
- close(fi->fh);
|
|
- fuse_reply_err(req, 0);
|
|
+ close(fi->fh);
|
|
+ fuse_reply_err(req, 0);
|
|
}
|
|
|
|
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
|
{
|
|
- int res;
|
|
- (void) ino;
|
|
- res = close(dup(fi->fh));
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ int res;
|
|
+ (void)ino;
|
|
+ res = close(dup(fi->fh));
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
|
- struct fuse_file_info *fi)
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- int res;
|
|
- (void) ino;
|
|
- if (datasync)
|
|
- res = fdatasync(fi->fh);
|
|
- else
|
|
- res = fsync(fi->fh);
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ int res;
|
|
+ (void)ino;
|
|
+ if (datasync) {
|
|
+ res = fdatasync(fi->fh);
|
|
+ } else {
|
|
+ res = fsync(fi->fh);
|
|
+ }
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
-static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
- off_t offset, struct fuse_file_info *fi)
|
|
+static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset,
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
|
|
+ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
|
|
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
|
|
- "off=%lu)\n", ino, size, (unsigned long) offset);
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "lo_read(ino=%" PRIu64 ", size=%zd, "
|
|
+ "off=%lu)\n",
|
|
+ ino, size, (unsigned long)offset);
|
|
+ }
|
|
|
|
- buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
- buf.buf[0].fd = fi->fh;
|
|
- buf.buf[0].pos = offset;
|
|
+ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
+ buf.buf[0].fd = fi->fh;
|
|
+ buf.buf[0].pos = offset;
|
|
|
|
- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
|
|
+ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
|
|
}
|
|
|
|
static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
|
|
- struct fuse_bufvec *in_buf, off_t off,
|
|
- struct fuse_file_info *fi)
|
|
+ struct fuse_bufvec *in_buf, off_t off,
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- (void) ino;
|
|
- ssize_t res;
|
|
- struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
|
|
-
|
|
- out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
- out_buf.buf[0].fd = fi->fh;
|
|
- out_buf.buf[0].pos = off;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
|
|
- ino, out_buf.buf[0].size, (unsigned long) off);
|
|
-
|
|
- res = fuse_buf_copy(&out_buf, in_buf, 0);
|
|
- if(res < 0)
|
|
- fuse_reply_err(req, -res);
|
|
- else
|
|
- fuse_reply_write(req, (size_t) res);
|
|
+ (void)ino;
|
|
+ ssize_t res;
|
|
+ struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
|
|
+
|
|
+ out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
+ out_buf.buf[0].fd = fi->fh;
|
|
+ out_buf.buf[0].pos = off;
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino,
|
|
+ out_buf.buf[0].size, (unsigned long)off);
|
|
+ }
|
|
+
|
|
+ res = fuse_buf_copy(&out_buf, in_buf, 0);
|
|
+ if (res < 0) {
|
|
+ fuse_reply_err(req, -res);
|
|
+ } else {
|
|
+ fuse_reply_write(req, (size_t)res);
|
|
+ }
|
|
}
|
|
|
|
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
|
|
{
|
|
- int res;
|
|
- struct statvfs stbuf;
|
|
-
|
|
- res = fstatvfs(lo_fd(req, ino), &stbuf);
|
|
- if (res == -1)
|
|
- fuse_reply_err(req, errno);
|
|
- else
|
|
- fuse_reply_statfs(req, &stbuf);
|
|
+ int res;
|
|
+ struct statvfs stbuf;
|
|
+
|
|
+ res = fstatvfs(lo_fd(req, ino), &stbuf);
|
|
+ if (res == -1) {
|
|
+ fuse_reply_err(req, errno);
|
|
+ } else {
|
|
+ fuse_reply_statfs(req, &stbuf);
|
|
+ }
|
|
}
|
|
|
|
-static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
|
|
- off_t offset, off_t length, struct fuse_file_info *fi)
|
|
+static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset,
|
|
+ off_t length, struct fuse_file_info *fi)
|
|
{
|
|
- int err = EOPNOTSUPP;
|
|
- (void) ino;
|
|
+ int err = EOPNOTSUPP;
|
|
+ (void)ino;
|
|
|
|
#ifdef HAVE_FALLOCATE
|
|
- err = fallocate(fi->fh, mode, offset, length);
|
|
- if (err < 0)
|
|
- err = errno;
|
|
+ err = fallocate(fi->fh, mode, offset, length);
|
|
+ if (err < 0) {
|
|
+ err = errno;
|
|
+ }
|
|
|
|
#elif defined(HAVE_POSIX_FALLOCATE)
|
|
- if (mode) {
|
|
- fuse_reply_err(req, EOPNOTSUPP);
|
|
- return;
|
|
- }
|
|
+ if (mode) {
|
|
+ fuse_reply_err(req, EOPNOTSUPP);
|
|
+ return;
|
|
+ }
|
|
|
|
- err = posix_fallocate(fi->fh, offset, length);
|
|
+ err = posix_fallocate(fi->fh, offset, length);
|
|
#endif
|
|
|
|
- fuse_reply_err(req, err);
|
|
+ fuse_reply_err(req, err);
|
|
}
|
|
|
|
static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
|
- int op)
|
|
+ int op)
|
|
{
|
|
- int res;
|
|
- (void) ino;
|
|
+ int res;
|
|
+ (void)ino;
|
|
|
|
- res = flock(fi->fh, op);
|
|
+ res = flock(fi->fh, op);
|
|
|
|
- fuse_reply_err(req, res == -1 ? errno : 0);
|
|
+ fuse_reply_err(req, res == -1 ? errno : 0);
|
|
}
|
|
|
|
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
- size_t size)
|
|
+ size_t size)
|
|
{
|
|
- char *value = NULL;
|
|
- char procname[64];
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- ssize_t ret;
|
|
- int saverr;
|
|
-
|
|
- saverr = ENOSYS;
|
|
- if (!lo_data(req)->xattr)
|
|
- goto out;
|
|
-
|
|
- if (lo_debug(req)) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
|
|
- ino, name, size);
|
|
- }
|
|
-
|
|
- if (inode->is_symlink) {
|
|
- /* Sorry, no race free way to getxattr on symlink. */
|
|
- saverr = EPERM;
|
|
- goto out;
|
|
- }
|
|
-
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
-
|
|
- if (size) {
|
|
- value = malloc(size);
|
|
- if (!value)
|
|
- goto out_err;
|
|
-
|
|
- ret = getxattr(procname, name, value, size);
|
|
- if (ret == -1)
|
|
- goto out_err;
|
|
- saverr = 0;
|
|
- if (ret == 0)
|
|
- goto out;
|
|
-
|
|
- fuse_reply_buf(req, value, ret);
|
|
- } else {
|
|
- ret = getxattr(procname, name, NULL, 0);
|
|
- if (ret == -1)
|
|
- goto out_err;
|
|
-
|
|
- fuse_reply_xattr(req, ret);
|
|
- }
|
|
+ char *value = NULL;
|
|
+ char procname[64];
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ ssize_t ret;
|
|
+ int saverr;
|
|
+
|
|
+ saverr = ENOSYS;
|
|
+ if (!lo_data(req)->xattr) {
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name,
|
|
+ size);
|
|
+ }
|
|
+
|
|
+ if (inode->is_symlink) {
|
|
+ /* Sorry, no race free way to getxattr on symlink. */
|
|
+ saverr = EPERM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+
|
|
+ if (size) {
|
|
+ value = malloc(size);
|
|
+ if (!value) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ ret = getxattr(procname, name, value, size);
|
|
+ if (ret == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ saverr = 0;
|
|
+ if (ret == 0) {
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ fuse_reply_buf(req, value, ret);
|
|
+ } else {
|
|
+ ret = getxattr(procname, name, NULL, 0);
|
|
+ if (ret == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ fuse_reply_xattr(req, ret);
|
|
+ }
|
|
out_free:
|
|
- free(value);
|
|
- return;
|
|
+ free(value);
|
|
+ return;
|
|
|
|
out_err:
|
|
- saverr = errno;
|
|
+ saverr = errno;
|
|
out:
|
|
- fuse_reply_err(req, saverr);
|
|
- goto out_free;
|
|
+ fuse_reply_err(req, saverr);
|
|
+ goto out_free;
|
|
}
|
|
|
|
static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
|
|
{
|
|
- char *value = NULL;
|
|
- char procname[64];
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- ssize_t ret;
|
|
- int saverr;
|
|
-
|
|
- saverr = ENOSYS;
|
|
- if (!lo_data(req)->xattr)
|
|
- goto out;
|
|
-
|
|
- if (lo_debug(req)) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
|
|
- ino, size);
|
|
- }
|
|
-
|
|
- if (inode->is_symlink) {
|
|
- /* Sorry, no race free way to listxattr on symlink. */
|
|
- saverr = EPERM;
|
|
- goto out;
|
|
- }
|
|
-
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
-
|
|
- if (size) {
|
|
- value = malloc(size);
|
|
- if (!value)
|
|
- goto out_err;
|
|
-
|
|
- ret = listxattr(procname, value, size);
|
|
- if (ret == -1)
|
|
- goto out_err;
|
|
- saverr = 0;
|
|
- if (ret == 0)
|
|
- goto out;
|
|
-
|
|
- fuse_reply_buf(req, value, ret);
|
|
- } else {
|
|
- ret = listxattr(procname, NULL, 0);
|
|
- if (ret == -1)
|
|
- goto out_err;
|
|
-
|
|
- fuse_reply_xattr(req, ret);
|
|
- }
|
|
+ char *value = NULL;
|
|
+ char procname[64];
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ ssize_t ret;
|
|
+ int saverr;
|
|
+
|
|
+ saverr = ENOSYS;
|
|
+ if (!lo_data(req)->xattr) {
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
|
|
+ ino, size);
|
|
+ }
|
|
+
|
|
+ if (inode->is_symlink) {
|
|
+ /* Sorry, no race free way to listxattr on symlink. */
|
|
+ saverr = EPERM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+
|
|
+ if (size) {
|
|
+ value = malloc(size);
|
|
+ if (!value) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ ret = listxattr(procname, value, size);
|
|
+ if (ret == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+ saverr = 0;
|
|
+ if (ret == 0) {
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ fuse_reply_buf(req, value, ret);
|
|
+ } else {
|
|
+ ret = listxattr(procname, NULL, 0);
|
|
+ if (ret == -1) {
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ fuse_reply_xattr(req, ret);
|
|
+ }
|
|
out_free:
|
|
- free(value);
|
|
- return;
|
|
+ free(value);
|
|
+ return;
|
|
|
|
out_err:
|
|
- saverr = errno;
|
|
+ saverr = errno;
|
|
out:
|
|
- fuse_reply_err(req, saverr);
|
|
- goto out_free;
|
|
+ fuse_reply_err(req, saverr);
|
|
+ goto out_free;
|
|
}
|
|
|
|
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
|
|
- const char *value, size_t size, int flags)
|
|
+ const char *value, size_t size, int flags)
|
|
{
|
|
- char procname[64];
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- ssize_t ret;
|
|
- int saverr;
|
|
+ char procname[64];
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ ssize_t ret;
|
|
+ int saverr;
|
|
|
|
- saverr = ENOSYS;
|
|
- if (!lo_data(req)->xattr)
|
|
- goto out;
|
|
+ saverr = ENOSYS;
|
|
+ if (!lo_data(req)->xattr) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- if (lo_debug(req)) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
|
|
- ino, name, value, size);
|
|
- }
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
|
|
+ ino, name, value, size);
|
|
+ }
|
|
|
|
- if (inode->is_symlink) {
|
|
- /* Sorry, no race free way to setxattr on symlink. */
|
|
- saverr = EPERM;
|
|
- goto out;
|
|
- }
|
|
+ if (inode->is_symlink) {
|
|
+ /* Sorry, no race free way to setxattr on symlink. */
|
|
+ saverr = EPERM;
|
|
+ goto out;
|
|
+ }
|
|
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
|
|
- ret = setxattr(procname, name, value, size, flags);
|
|
- saverr = ret == -1 ? errno : 0;
|
|
+ ret = setxattr(procname, name, value, size, flags);
|
|
+ saverr = ret == -1 ? errno : 0;
|
|
|
|
out:
|
|
- fuse_reply_err(req, saverr);
|
|
+ fuse_reply_err(req, saverr);
|
|
}
|
|
|
|
static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
|
|
{
|
|
- char procname[64];
|
|
- struct lo_inode *inode = lo_inode(req, ino);
|
|
- ssize_t ret;
|
|
- int saverr;
|
|
+ char procname[64];
|
|
+ struct lo_inode *inode = lo_inode(req, ino);
|
|
+ ssize_t ret;
|
|
+ int saverr;
|
|
|
|
- saverr = ENOSYS;
|
|
- if (!lo_data(req)->xattr)
|
|
- goto out;
|
|
+ saverr = ENOSYS;
|
|
+ if (!lo_data(req)->xattr) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- if (lo_debug(req)) {
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
|
|
- ino, name);
|
|
- }
|
|
+ if (lo_debug(req)) {
|
|
+ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
|
|
+ ino, name);
|
|
+ }
|
|
|
|
- if (inode->is_symlink) {
|
|
- /* Sorry, no race free way to setxattr on symlink. */
|
|
- saverr = EPERM;
|
|
- goto out;
|
|
- }
|
|
+ if (inode->is_symlink) {
|
|
+ /* Sorry, no race free way to setxattr on symlink. */
|
|
+ saverr = EPERM;
|
|
+ goto out;
|
|
+ }
|
|
|
|
- sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
+ sprintf(procname, "/proc/self/fd/%i", inode->fd);
|
|
|
|
- ret = removexattr(procname, name);
|
|
- saverr = ret == -1 ? errno : 0;
|
|
+ ret = removexattr(procname, name);
|
|
+ saverr = ret == -1 ? errno : 0;
|
|
|
|
out:
|
|
- fuse_reply_err(req, saverr);
|
|
+ fuse_reply_err(req, saverr);
|
|
}
|
|
|
|
#ifdef HAVE_COPY_FILE_RANGE
|
|
static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
|
|
- struct fuse_file_info *fi_in,
|
|
- fuse_ino_t ino_out, off_t off_out,
|
|
- struct fuse_file_info *fi_out, size_t len,
|
|
- int flags)
|
|
+ struct fuse_file_info *fi_in, fuse_ino_t ino_out,
|
|
+ off_t off_out, struct fuse_file_info *fi_out,
|
|
+ size_t len, int flags)
|
|
{
|
|
- ssize_t res;
|
|
-
|
|
- if (lo_debug(req))
|
|
- fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
|
|
- "off=%lu, ino=%" PRIu64 "/fd=%lu, "
|
|
- "off=%lu, size=%zd, flags=0x%x)\n",
|
|
- ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out,
|
|
- len, flags);
|
|
-
|
|
- res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
|
|
- flags);
|
|
- if (res < 0)
|
|
- fuse_reply_err(req, -errno);
|
|
- else
|
|
- fuse_reply_write(req, res);
|
|
+ ssize_t res;
|
|
+
|
|
+ if (lo_debug(req))
|
|
+ fuse_log(FUSE_LOG_DEBUG,
|
|
+ "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
|
|
+ "off=%lu, ino=%" PRIu64 "/fd=%lu, "
|
|
+ "off=%lu, size=%zd, flags=0x%x)\n",
|
|
+ ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len,
|
|
+ flags);
|
|
+
|
|
+ res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags);
|
|
+ if (res < 0) {
|
|
+ fuse_reply_err(req, -errno);
|
|
+ } else {
|
|
+ fuse_reply_write(req, res);
|
|
+ }
|
|
}
|
|
#endif
|
|
|
|
static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
|
|
- struct fuse_file_info *fi)
|
|
+ struct fuse_file_info *fi)
|
|
{
|
|
- off_t res;
|
|
-
|
|
- (void)ino;
|
|
- res = lseek(fi->fh, off, whence);
|
|
- if (res != -1)
|
|
- fuse_reply_lseek(req, res);
|
|
- else
|
|
- fuse_reply_err(req, errno);
|
|
+ off_t res;
|
|
+
|
|
+ (void)ino;
|
|
+ res = lseek(fi->fh, off, whence);
|
|
+ if (res != -1) {
|
|
+ fuse_reply_lseek(req, res);
|
|
+ } else {
|
|
+ fuse_reply_err(req, errno);
|
|
+ }
|
|
}
|
|
|
|
static struct fuse_lowlevel_ops lo_oper = {
|
|
- .init = lo_init,
|
|
- .lookup = lo_lookup,
|
|
- .mkdir = lo_mkdir,
|
|
- .mknod = lo_mknod,
|
|
- .symlink = lo_symlink,
|
|
- .link = lo_link,
|
|
- .unlink = lo_unlink,
|
|
- .rmdir = lo_rmdir,
|
|
- .rename = lo_rename,
|
|
- .forget = lo_forget,
|
|
- .forget_multi = lo_forget_multi,
|
|
- .getattr = lo_getattr,
|
|
- .setattr = lo_setattr,
|
|
- .readlink = lo_readlink,
|
|
- .opendir = lo_opendir,
|
|
- .readdir = lo_readdir,
|
|
- .readdirplus = lo_readdirplus,
|
|
- .releasedir = lo_releasedir,
|
|
- .fsyncdir = lo_fsyncdir,
|
|
- .create = lo_create,
|
|
- .open = lo_open,
|
|
- .release = lo_release,
|
|
- .flush = lo_flush,
|
|
- .fsync = lo_fsync,
|
|
- .read = lo_read,
|
|
- .write_buf = lo_write_buf,
|
|
- .statfs = lo_statfs,
|
|
- .fallocate = lo_fallocate,
|
|
- .flock = lo_flock,
|
|
- .getxattr = lo_getxattr,
|
|
- .listxattr = lo_listxattr,
|
|
- .setxattr = lo_setxattr,
|
|
- .removexattr = lo_removexattr,
|
|
+ .init = lo_init,
|
|
+ .lookup = lo_lookup,
|
|
+ .mkdir = lo_mkdir,
|
|
+ .mknod = lo_mknod,
|
|
+ .symlink = lo_symlink,
|
|
+ .link = lo_link,
|
|
+ .unlink = lo_unlink,
|
|
+ .rmdir = lo_rmdir,
|
|
+ .rename = lo_rename,
|
|
+ .forget = lo_forget,
|
|
+ .forget_multi = lo_forget_multi,
|
|
+ .getattr = lo_getattr,
|
|
+ .setattr = lo_setattr,
|
|
+ .readlink = lo_readlink,
|
|
+ .opendir = lo_opendir,
|
|
+ .readdir = lo_readdir,
|
|
+ .readdirplus = lo_readdirplus,
|
|
+ .releasedir = lo_releasedir,
|
|
+ .fsyncdir = lo_fsyncdir,
|
|
+ .create = lo_create,
|
|
+ .open = lo_open,
|
|
+ .release = lo_release,
|
|
+ .flush = lo_flush,
|
|
+ .fsync = lo_fsync,
|
|
+ .read = lo_read,
|
|
+ .write_buf = lo_write_buf,
|
|
+ .statfs = lo_statfs,
|
|
+ .fallocate = lo_fallocate,
|
|
+ .flock = lo_flock,
|
|
+ .getxattr = lo_getxattr,
|
|
+ .listxattr = lo_listxattr,
|
|
+ .setxattr = lo_setxattr,
|
|
+ .removexattr = lo_removexattr,
|
|
#ifdef HAVE_COPY_FILE_RANGE
|
|
- .copy_file_range = lo_copy_file_range,
|
|
+ .copy_file_range = lo_copy_file_range,
|
|
#endif
|
|
- .lseek = lo_lseek,
|
|
+ .lseek = lo_lseek,
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
- struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
|
- struct fuse_session *se;
|
|
- struct fuse_cmdline_opts opts;
|
|
- struct lo_data lo = { .debug = 0,
|
|
- .writeback = 0 };
|
|
- int ret = -1;
|
|
-
|
|
- /* Don't mask creation mode, kernel already did that */
|
|
- umask(0);
|
|
-
|
|
- pthread_mutex_init(&lo.mutex, NULL);
|
|
- lo.root.next = lo.root.prev = &lo.root;
|
|
- lo.root.fd = -1;
|
|
- lo.cache = CACHE_NORMAL;
|
|
-
|
|
- if (fuse_parse_cmdline(&args, &opts) != 0)
|
|
- return 1;
|
|
- if (opts.show_help) {
|
|
- printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
|
|
- fuse_cmdline_help();
|
|
- fuse_lowlevel_help();
|
|
- ret = 0;
|
|
- goto err_out1;
|
|
- } else if (opts.show_version) {
|
|
- fuse_lowlevel_version();
|
|
- ret = 0;
|
|
- goto err_out1;
|
|
- }
|
|
-
|
|
- if(opts.mountpoint == NULL) {
|
|
- printf("usage: %s [options] <mountpoint>\n", argv[0]);
|
|
- printf(" %s --help\n", argv[0]);
|
|
- ret = 1;
|
|
- goto err_out1;
|
|
- }
|
|
-
|
|
- if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
|
|
- return 1;
|
|
-
|
|
- lo.debug = opts.debug;
|
|
- lo.root.refcount = 2;
|
|
- if (lo.source) {
|
|
- struct stat stat;
|
|
- int res;
|
|
-
|
|
- res = lstat(lo.source, &stat);
|
|
- if (res == -1) {
|
|
- fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
|
|
- lo.source);
|
|
- exit(1);
|
|
- }
|
|
- if (!S_ISDIR(stat.st_mode)) {
|
|
- fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
|
|
- exit(1);
|
|
- }
|
|
-
|
|
- } else {
|
|
- lo.source = "/";
|
|
- }
|
|
- lo.root.is_symlink = false;
|
|
- if (!lo.timeout_set) {
|
|
- switch (lo.cache) {
|
|
- case CACHE_NEVER:
|
|
- lo.timeout = 0.0;
|
|
- break;
|
|
-
|
|
- case CACHE_NORMAL:
|
|
- lo.timeout = 1.0;
|
|
- break;
|
|
-
|
|
- case CACHE_ALWAYS:
|
|
- lo.timeout = 86400.0;
|
|
- break;
|
|
- }
|
|
- } else if (lo.timeout < 0) {
|
|
- fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
|
|
- lo.timeout);
|
|
- exit(1);
|
|
- }
|
|
-
|
|
- lo.root.fd = open(lo.source, O_PATH);
|
|
- if (lo.root.fd == -1) {
|
|
- fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
|
|
- lo.source);
|
|
- exit(1);
|
|
- }
|
|
-
|
|
- se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
|
|
- if (se == NULL)
|
|
- goto err_out1;
|
|
-
|
|
- if (fuse_set_signal_handlers(se) != 0)
|
|
- goto err_out2;
|
|
-
|
|
- if (fuse_session_mount(se, opts.mountpoint) != 0)
|
|
- goto err_out3;
|
|
-
|
|
- fuse_daemonize(opts.foreground);
|
|
-
|
|
- /* Block until ctrl+c or fusermount -u */
|
|
- if (opts.singlethread)
|
|
- ret = fuse_session_loop(se);
|
|
- else
|
|
- ret = fuse_session_loop_mt(se, opts.clone_fd);
|
|
-
|
|
- fuse_session_unmount(se);
|
|
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
|
+ struct fuse_session *se;
|
|
+ struct fuse_cmdline_opts opts;
|
|
+ struct lo_data lo = { .debug = 0, .writeback = 0 };
|
|
+ int ret = -1;
|
|
+
|
|
+ /* Don't mask creation mode, kernel already did that */
|
|
+ umask(0);
|
|
+
|
|
+ pthread_mutex_init(&lo.mutex, NULL);
|
|
+ lo.root.next = lo.root.prev = &lo.root;
|
|
+ lo.root.fd = -1;
|
|
+ lo.cache = CACHE_NORMAL;
|
|
+
|
|
+ if (fuse_parse_cmdline(&args, &opts) != 0) {
|
|
+ return 1;
|
|
+ }
|
|
+ if (opts.show_help) {
|
|
+ printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
|
|
+ fuse_cmdline_help();
|
|
+ fuse_lowlevel_help();
|
|
+ ret = 0;
|
|
+ goto err_out1;
|
|
+ } else if (opts.show_version) {
|
|
+ fuse_lowlevel_version();
|
|
+ ret = 0;
|
|
+ goto err_out1;
|
|
+ }
|
|
+
|
|
+ if (opts.mountpoint == NULL) {
|
|
+ printf("usage: %s [options] <mountpoint>\n", argv[0]);
|
|
+ printf(" %s --help\n", argv[0]);
|
|
+ ret = 1;
|
|
+ goto err_out1;
|
|
+ }
|
|
+
|
|
+ if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) {
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ lo.debug = opts.debug;
|
|
+ lo.root.refcount = 2;
|
|
+ if (lo.source) {
|
|
+ struct stat stat;
|
|
+ int res;
|
|
+
|
|
+ res = lstat(lo.source, &stat);
|
|
+ if (res == -1) {
|
|
+ fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
|
|
+ lo.source);
|
|
+ exit(1);
|
|
+ }
|
|
+ if (!S_ISDIR(stat.st_mode)) {
|
|
+ fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ } else {
|
|
+ lo.source = "/";
|
|
+ }
|
|
+ lo.root.is_symlink = false;
|
|
+ if (!lo.timeout_set) {
|
|
+ switch (lo.cache) {
|
|
+ case CACHE_NEVER:
|
|
+ lo.timeout = 0.0;
|
|
+ break;
|
|
+
|
|
+ case CACHE_NORMAL:
|
|
+ lo.timeout = 1.0;
|
|
+ break;
|
|
+
|
|
+ case CACHE_ALWAYS:
|
|
+ lo.timeout = 86400.0;
|
|
+ break;
|
|
+ }
|
|
+ } else if (lo.timeout < 0) {
|
|
+ fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ lo.root.fd = open(lo.source, O_PATH);
|
|
+ if (lo.root.fd == -1) {
|
|
+ fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
|
|
+ if (se == NULL) {
|
|
+ goto err_out1;
|
|
+ }
|
|
+
|
|
+ if (fuse_set_signal_handlers(se) != 0) {
|
|
+ goto err_out2;
|
|
+ }
|
|
+
|
|
+ if (fuse_session_mount(se, opts.mountpoint) != 0) {
|
|
+ goto err_out3;
|
|
+ }
|
|
+
|
|
+ fuse_daemonize(opts.foreground);
|
|
+
|
|
+ /* Block until ctrl+c or fusermount -u */
|
|
+ if (opts.singlethread) {
|
|
+ ret = fuse_session_loop(se);
|
|
+ } else {
|
|
+ ret = fuse_session_loop_mt(se, opts.clone_fd);
|
|
+ }
|
|
+
|
|
+ fuse_session_unmount(se);
|
|
err_out3:
|
|
- fuse_remove_signal_handlers(se);
|
|
+ fuse_remove_signal_handlers(se);
|
|
err_out2:
|
|
- fuse_session_destroy(se);
|
|
+ fuse_session_destroy(se);
|
|
err_out1:
|
|
- free(opts.mountpoint);
|
|
- fuse_opt_free_args(&args);
|
|
+ free(opts.mountpoint);
|
|
+ fuse_opt_free_args(&args);
|
|
|
|
- if (lo.root.fd >= 0)
|
|
- close(lo.root.fd);
|
|
+ if (lo.root.fd >= 0) {
|
|
+ close(lo.root.fd);
|
|
+ }
|
|
|
|
- return ret ? 1 : 0;
|
|
+ return ret ? 1 : 0;
|
|
}
|