struct lo_key {
ino_t ino;
dev_t dev;
+ uint64_t mnt_id;
};
struct lo_inode {
int readdirplus_set;
int readdirplus_clear;
int allow_direct_io;
+ bool use_statx;
struct lo_inode root;
GHashTable *inodes; /* protected by lo->mutex */
struct lo_map ino_map; /* protected by lo->mutex */
/* That we loaded cap-ng in the current thread from the saved */
static __thread bool cap_loaded = 0;
-static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st);
+static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st,
+ uint64_t mnt_id);
static int is_dot_or_dotdot(const char *name)
{
fuse_reply_err(req, saverr);
}
-static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
+static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st,
+ uint64_t mnt_id)
{
struct lo_inode *p;
struct lo_key key = {
.ino = st->st_ino,
.dev = st->st_dev,
+ .mnt_id = mnt_id,
};
pthread_mutex_lock(&lo->mutex);
free(plock);
}
+static int do_statx(struct lo_data *lo, int dirfd, const char *pathname,
+ struct stat *statbuf, int flags, uint64_t *mnt_id)
+{
+ int res;
+
+#if defined(CONFIG_STATX) && defined(STATX_MNT_ID)
+ if (lo->use_statx) {
+ struct statx statxbuf;
+
+ res = statx(dirfd, pathname, flags, STATX_BASIC_STATS | STATX_MNT_ID,
+ &statxbuf);
+ if (!res) {
+ memset(statbuf, 0, sizeof(*statbuf));
+ statbuf->st_dev = makedev(statxbuf.stx_dev_major,
+ statxbuf.stx_dev_minor);
+ statbuf->st_ino = statxbuf.stx_ino;
+ statbuf->st_mode = statxbuf.stx_mode;
+ statbuf->st_nlink = statxbuf.stx_nlink;
+ statbuf->st_uid = statxbuf.stx_uid;
+ statbuf->st_gid = statxbuf.stx_gid;
+ statbuf->st_rdev = makedev(statxbuf.stx_rdev_major,
+ statxbuf.stx_rdev_minor);
+ statbuf->st_size = statxbuf.stx_size;
+ statbuf->st_blksize = statxbuf.stx_blksize;
+ statbuf->st_blocks = statxbuf.stx_blocks;
+ statbuf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
+ statbuf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
+ statbuf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
+ statbuf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
+ statbuf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
+ statbuf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
+
+ if (statxbuf.stx_mask & STATX_MNT_ID) {
+ *mnt_id = statxbuf.stx_mnt_id;
+ } else {
+ *mnt_id = 0;
+ }
+ return 0;
+ } else if (errno != ENOSYS) {
+ return -1;
+ }
+ lo->use_statx = false;
+ /* fallback */
+ }
+#endif
+ res = fstatat(dirfd, pathname, statbuf, flags);
+ if (res == -1) {
+ return -1;
+ }
+ *mnt_id = 0;
+
+ return 0;
+}
+
/*
* Increments nlookup and caller must release refcount using
* lo_inode_put(&parent).
int newfd;
int res;
int saverr;
+ uint64_t mnt_id;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = NULL;
struct lo_inode *dir = lo_inode(req, parent);
goto out_err;
}
- res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ res = do_statx(lo, newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
+ &mnt_id);
if (res == -1) {
goto out_err;
}
- inode = lo_find(lo, &e->attr);
+ inode = lo_find(lo, &e->attr, mnt_id);
if (inode) {
close(newfd);
} else {
inode->fd = newfd;
inode->key.ino = e->attr.st_ino;
inode->key.dev = e->attr.st_dev;
+ inode->key.mnt_id = mnt_id;
pthread_mutex_init(&inode->plock_mutex, NULL);
inode->posix_locks = g_hash_table_new_full(
g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy);
const char *name)
{
int res;
+ uint64_t mnt_id;
struct stat attr;
+ struct lo_data *lo = lo_data(req);
+ struct lo_inode *dir = lo_inode(req, parent);
- res = fstatat(lo_fd(req, parent), name, &attr,
- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ if (!dir) {
+ return NULL;
+ }
+
+ res = do_statx(lo, dir->fd, name, &attr,
+ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id);
+ lo_inode_put(lo, &dir);
if (res == -1) {
return NULL;
}
- return lo_find(lo_data(req), &attr);
+ return lo_find(lo, &attr, mnt_id);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int fd, res;
struct stat stat;
+ uint64_t mnt_id;
fd = open("/", O_PATH);
if (fd == -1) {
exit(1);
}
- res = fstatat(fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ res = do_statx(lo, fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
+ &mnt_id);
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source);
exit(1);
root->fd = fd;
root->key.ino = stat.st_ino;
root->key.dev = stat.st_dev;
+ root->key.mnt_id = mnt_id;
root->nlookup = 2;
g_atomic_int_set(&root->refcount, 2);
}
{
const struct lo_key *lkey = key;
- return (guint)lkey->ino + (guint)lkey->dev;
+ return (guint)lkey->ino + (guint)lkey->dev + (guint)lkey->mnt_id;
}
static gboolean lo_key_equal(gconstpointer a, gconstpointer b)
const struct lo_key *la = a;
const struct lo_key *lb = b;
- return la->ino == lb->ino && la->dev == lb->dev;
+ return la->ino == lb->ino && la->dev == lb->dev && la->mnt_id == lb->mnt_id;
}
static void fuse_lo_data_cleanup(struct lo_data *lo)
exit(1);
}
+ lo.use_statx = true;
+
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if (se == NULL) {
goto err_out1;