aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse.c108
-rw-r--r--lib/fuse_lowlevel.c66
-rw-r--r--lib/fuse_versionscript3
-rw-r--r--lib/modules/iconv.c19
-rw-r--r--lib/modules/subdir.c19
5 files changed, 214 insertions, 1 deletions
diff --git a/lib/fuse.c b/lib/fuse.c
index 68b61ce..333fdc7 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -1507,6 +1507,29 @@ static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
stbuf->st_gid = f->conf.gid;
}
+#ifdef HAVE_STATX
+static void set_statx(struct fuse *f, fuse_ino_t nodeid, struct statx *stxbuf)
+{
+ if (!f->conf.use_ino)
+ stxbuf->stx_ino = nodeid;
+ if (f->conf.set_mode) {
+ if (f->conf.dmask && S_ISDIR(stxbuf->stx_mode))
+ stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
+ (0777 & ~f->conf.dmask);
+ else if (f->conf.fmask)
+ stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
+ (0777 & ~f->conf.fmask);
+ else
+ stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
+ (0777 & ~f->conf.umask);
+ }
+ if (f->conf.set_uid)
+ stxbuf->stx_uid = f->conf.uid;
+ if (f->conf.set_gid)
+ stxbuf->stx_gid = f->conf.gid;
+}
+#endif
+
static struct fuse *req_fuse(fuse_req_t req)
{
return (struct fuse *) fuse_req_userdata(req);
@@ -2312,6 +2335,39 @@ off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
return fs->op.lseek(path, off, whence, fi);
}
+#ifdef HAVE_STATX
+int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
+ struct statx *stxbuf, struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.statx) {
+ if (fs->debug) {
+ char buf[10];
+
+ fuse_log(FUSE_LOG_DEBUG, "statx[%s] %s %d %d\n",
+ file_info_string(fi, buf, sizeof(buf)), path,
+ flags, mask);
+ }
+ return fs->op.statx(path, flags, mask, stxbuf, fi);
+ }
+
+ return -ENOSYS;
+}
+#else
+int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
+ struct statx *stxbuf, struct fuse_file_info *fi)
+{
+ (void)fs;
+ (void)path;
+ (void)flags;
+ (void)mask;
+ (void)stxbuf;
+ (void)fi;
+
+ return -ENOSYS;
+}
+#endif
+
static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
{
struct node *node;
@@ -4361,6 +4417,55 @@ static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence
reply_err(req, res);
}
+#ifdef HAVE_STATX
+static void fuse_lib_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct statx stxbuf;
+ char *path;
+ int err;
+
+ memset(&stxbuf, 0, sizeof(stxbuf));
+
+ if (fi != NULL)
+ err = get_path_nullok(f, ino, &path);
+ else
+ err = get_path(f, ino, &path);
+
+ if (!err) {
+ struct fuse_intr_data d;
+
+ if (!path)
+ flags |= AT_EMPTY_PATH;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_statx(f->fs, path, flags, mask, &stxbuf, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err) {
+ struct node *node;
+
+ pthread_mutex_lock(&f->lock);
+ node = get_node(f, ino);
+ if (node->is_hidden && stxbuf.stx_nlink > 0)
+ stxbuf.stx_nlink--;
+ if (f->conf.auto_cache) {
+ struct stat stbuf;
+
+ stbuf.st_mtime = stxbuf.stx_mtime.tv_nsec;
+ ST_MTIM_NSEC(&stbuf) = stxbuf.stx_mtime.tv_nsec;
+ stbuf.st_size = stxbuf.stx_size;
+ update_stat(node, &stbuf);
+ }
+ pthread_mutex_unlock(&f->lock);
+ set_statx(f, ino, &stxbuf);
+ fuse_reply_statx(req, 0, &stxbuf, f->conf.attr_timeout);
+ } else
+ reply_err(req, err);
+}
+#endif
+
static int clean_delay(struct fuse *f)
{
/*
@@ -4459,6 +4564,9 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
.fallocate = fuse_lib_fallocate,
.copy_file_range = fuse_lib_copy_file_range,
.lseek = fuse_lib_lseek,
+#ifdef HAVE_STATX
+ .statx = fuse_lib_statx,
+#endif
};
int fuse_notify_poll(struct fuse_pollhandle *ph)
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 91f4244..1ae3473 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -1216,6 +1216,33 @@ int fuse_reply_lseek(fuse_req_t req, off_t off)
return send_reply_ok(req, &arg, sizeof(arg));
}
+#ifdef HAVE_STATX
+int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
+ double attr_timeout)
+{
+ struct fuse_statx_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.flags = flags;
+ arg.attr_valid = calc_timeout_sec(attr_timeout);
+ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
+ memcpy(&arg.stat, statx, sizeof(arg.stat));
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+#else
+int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
+ double attr_timeout)
+{
+ (void)req;
+ (void)flags;
+ (void)statx;
+ (void)attr_timeout;
+
+ return -ENOSYS;
+}
+#endif
+
static void _do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
const void *op_in, const void *in_payload)
{
@@ -2428,6 +2455,42 @@ static void do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
_do_lseek(req, nodeid, inarg, NULL);
}
+#ifdef HAVE_STATX
+static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
+ const void *op_in, const void *in_payload)
+{
+ (void)in_payload;
+ const struct fuse_statx_in *arg = op_in;
+ struct fuse_file_info *fip = NULL;
+ struct fuse_file_info fi;
+
+ if (arg->getattr_flags & FUSE_GETATTR_FH) {
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fip = &fi;
+ }
+
+ if (req->se->op.statx)
+ req->se->op.statx(req, nodeid, arg->sx_flags, arg->sx_mask, fip);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+#else
+static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
+ const void *op_in, const void *in_payload)
+{
+ (void)in_payload;
+ (void)req;
+ (void)nodeid;
+ (void)op_in;
+}
+#endif
+
+static void do_statx(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ _do_statx(req, nodeid, inarg, NULL);
+}
+
static bool want_flags_valid(uint64_t capable, uint64_t want)
{
uint64_t unknown_flags = want & (~capable);
@@ -2510,7 +2573,6 @@ bool fuse_get_feature_flag(struct fuse_conn_info *conn,
return conn->capable_ext & flag ? true : false;
}
-
/* Prevent bogus data races (bogus since "init" is called before
* multi-threading becomes relevant */
static __attribute__((no_sanitize("thread"))) void
@@ -3297,6 +3359,7 @@ static struct {
[FUSE_RENAME2] = { do_rename2, "RENAME2" },
[FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
[FUSE_LSEEK] = { do_lseek, "LSEEK" },
+ [FUSE_STATX] = { do_statx, "STATX" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
@@ -3351,6 +3414,7 @@ static struct {
[FUSE_RENAME2] = { _do_rename2, "RENAME2" },
[FUSE_COPY_FILE_RANGE] = { _do_copy_file_range, "COPY_FILE_RANGE" },
[FUSE_LSEEK] = { _do_lseek, "LSEEK" },
+ [FUSE_STATX] = { _do_statx, "STATX" },
[CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" },
};
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 2d8884d..0e581f1 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -212,6 +212,9 @@ FUSE_3.18 {
# Not part of public API, for internal test use only
fuse_convert_to_conn_want_ext;
+
+ fuse_reply_statx;
+ fuse_fs_statx;
} FUSE_3.17;
# Local Variables:
diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c
index 599b8df..417c904 100644
--- a/lib/modules/iconv.c
+++ b/lib/modules/iconv.c
@@ -568,6 +568,22 @@ static off_t iconv_lseek(const char *path, off_t off, int whence,
return res;
}
+#ifdef HAVE_STATX
+static int iconv_statx(const char *path, int flags, int mask, struct statx *stxbuf,
+ struct fuse_file_info *fi)
+{
+ struct iconv *ic = iconv_get();
+ char *newpath;
+ int res = iconv_convpath(ic, path, &newpath, 0);
+
+ if (!res) {
+ res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
+ free(newpath);
+ }
+ return res;
+}
+#endif
+
static void *iconv_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
@@ -627,6 +643,9 @@ static const struct fuse_operations iconv_oper = {
.flock = iconv_flock,
.bmap = iconv_bmap,
.lseek = iconv_lseek,
+#ifdef HAVE_STATX
+ .statx = iconv_statx,
+#endif
};
static const struct fuse_opt iconv_opts[] = {
diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c
index dd2d49d..67c4697 100644
--- a/lib/modules/subdir.c
+++ b/lib/modules/subdir.c
@@ -553,6 +553,22 @@ static off_t subdir_lseek(const char *path, off_t off, int whence,
return res;
}
+#ifdef HAVE_STATX
+static int subdir_statx(const char *path, int flags, int mask, struct statx *stxbuf,
+ struct fuse_file_info *fi)
+{
+ struct subdir *ic = subdir_get();
+ char *newpath;
+ int res = subdir_addpath(ic, path, &newpath);
+
+ if (!res) {
+ res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
+ free(newpath);
+ }
+ return res;
+}
+#endif
+
static void *subdir_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
@@ -608,6 +624,9 @@ static const struct fuse_operations subdir_oper = {
.flock = subdir_flock,
.bmap = subdir_bmap,
.lseek = subdir_lseek,
+#ifdef HAVE_STATX
+ .statx = subdir_statx,
+#endif
};
static const struct fuse_opt subdir_opts[] = {