fuse_reply_create(req, &e, fi);
}
+static Inode *create_new_inode(int fd, fuse_entry_param *e)
+{
+ memset(e, 0, sizeof(*e));
+ e->attr_timeout = fs.timeout;
+ e->entry_timeout = fs.timeout;
+
+ auto res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ if (res == -1) {
+ if (fs.debug)
+ cerr << "DEBUG: lookup(): fstatat failed" << endl;
+ return NULL;
+ }
+
+ SrcId id {e->attr.st_ino, e->attr.st_dev};
+ unique_lock<mutex> fs_lock {fs.mutex};
+ Inode* p_inode;
+ try {
+ p_inode = &fs.inodes[id];
+ } catch (std::bad_alloc&) {
+ return NULL;
+ }
+
+ e->ino = reinterpret_cast<fuse_ino_t>(p_inode);
+ e->generation = p_inode->generation;
+
+ lock_guard<mutex> g {p_inode->m};
+ p_inode->src_ino = e->attr.st_ino;
+ p_inode->src_dev = e->attr.st_dev;
+
+ p_inode->nlookup++;
+ if (fs.debug)
+ cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " "
+ << "inode " << p_inode->src_ino
+ << " count " << p_inode->nlookup << endl;
+
+ p_inode->fd = fd;
+ fs_lock.unlock();
+
+ if (fs.debug)
+ cerr << "DEBUG: lookup(): created userspace inode " << e->attr.st_ino
+ << "; fd = " << p_inode->fd << endl;
+ return p_inode;
+}
+
+static void sfs_tmpfile(fuse_req_t req, fuse_ino_t parent,
+ mode_t mode, fuse_file_info *fi) {
+ Inode& parent_inode = get_inode(parent);
+
+ auto fd = openat(parent_inode.fd, ".",
+ (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
+ if (fd == -1) {
+ auto err = errno;
+ if (err == ENFILE || err == EMFILE)
+ cerr << "ERROR: Reached maximum number of file descriptors." << endl;
+ fuse_reply_err(req, err);
+ return;
+ }
+
+ fi->fh = fd;
+ fuse_entry_param e;
+
+ Inode *inode = create_new_inode(dup(fd), &e);
+ if (inode == NULL) {
+ auto err = errno;
+ cerr << "ERROR: could not create new inode." << endl;
+ close(fd);
+ fuse_reply_err(req, err);
+ return;
+ }
+
+ lock_guard<mutex> g {inode->m};
+
+ sfs_create_open_flags(fi);
+
+ if (fs.passthrough)
+ do_passthrough_open(req, e.ino, fd, fi);
+
+ fuse_reply_create(req, &e, fi);
+}
+
static void sfs_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
fuse_file_info *fi) {
sfs_oper.releasedir = sfs_releasedir;
sfs_oper.fsyncdir = sfs_fsyncdir;
sfs_oper.create = sfs_create;
+ sfs_oper.tmpfile = sfs_tmpfile;
sfs_oper.open = sfs_open;
sfs_oper.release = sfs_release;
sfs_oper.flush = sfs_flush;
return ret;
}
+
+static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
+{
+ struct lo_inode *inode = NULL;
+ struct lo_inode *prev, *next;
+
+ inode = calloc(1, sizeof(struct lo_inode));
+ if (!inode)
+ return NULL;
+
+ inode->refcount = 1;
+ inode->fd = fd;
+ 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);
+ return inode;
+}
+
+static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
+{
+ int res;
+ struct lo_data *lo = lo_data(req);
+
+ memset(e, 0, sizeof(*e));
+ e->attr_timeout = lo->timeout;
+ e->entry_timeout = lo->timeout;
+
+ res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return errno;
+
+ e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
+
+ if (lo_debug(req))
+ fuse_log(FUSE_LOG_DEBUG, " %lli/%lli -> %lli\n",
+ (unsigned long long) parent, fd, (unsigned long long) e->ino);
+
+ return 0;
+
+}
+
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
struct fuse_entry_param *e)
{
close(newfd);
newfd = -1;
} else {
- struct lo_inode *prev, *next;
-
- saverr = ENOMEM;
- inode = calloc(1, sizeof(struct lo_inode));
+ inode = create_new_inode(newfd, e, lo);
if (!inode)
goto out_err;
-
- 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;
fuse_reply_err(req, 0);
}
-static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
+static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct fuse_entry_param e;
int err;
+ if (lo_debug(req))
+ fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
+ parent);
+
+ fd = openat(lo_fd(req, parent), ".",
+ (fi->flags | O_TMPFILE) & ~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;
+
+ /* parallel_direct_writes feature depends on direct_io features.
+ To make parallel_direct_writes valid, need set fi->direct_io
+ in current function. */
+ fi->parallel_direct_writes = 1;
+
+ err = fill_entry_param_new_inode(req, parent, fd, &e);
+ if (err)
+ fuse_reply_err(req, err);
+ else
+ fuse_reply_create(req, &e, fi);
+}
+
+static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
+ 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);
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
+ .tmpfile = lo_tmpfile,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
*/
void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info *fi);
+
+
+ /**
+ * Create a tempfile
+ *
+ * Tempfile means an anonymous file. It can be made into a normal file later
+ * by using linkat or such.
+ *
+ * If this is answered with an error ENOSYS this is treated by the kernel as
+ * a permanent failure and it will disable the feature and not ask again.
+ *
+ * Valid replies:
+ * fuse_reply_create
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param mode file type and mode with which to create the new file
+ * @param fi file information
+ */
+ void (*tmpfile) (fuse_req_t req, fuse_ino_t parent,
+ mode_t mode, struct fuse_file_info *fi);
+
};
/**
fuse_reply_err(req, ENOSYS);
}
+static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
+
+ if (req->se->op.tmpfile) {
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+
+ if (req->se->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+
+ req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
+ } 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;
[FUSE_SETLKW] = { do_setlkw, "SETLKW" },
[FUSE_ACCESS] = { do_access, "ACCESS" },
[FUSE_CREATE] = { do_create, "CREATE" },
+ [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
[FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
[FUSE_BMAP] = { do_bmap, "BMAP" },
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_SETLKW] = { "SETLKW" },
[FUSE_ACCESS] = { "ACCESS" },
[FUSE_CREATE] = { "CREATE" },
+ [FUSE_TMPFILE] = { "TMPFILE" },
[FUSE_INTERRUPT] = { "INTERRUPT" },
[FUSE_BMAP] = { "BMAP" },
[FUSE_DESTROY] = { "DESTROY" },
return 0;
}
+/* this tests open with O_TMPFILE
+ note that this will only work with the fuse low level api
+ you will get ENOTSUP with the high level api */
+static int test_create_tmpfile(void)
+{
+ rmdir(testdir);
+ int res = mkdir(testdir, 0777);
+ if (res)
+ return -1;
+
+ start_test("create tmpfile");
+
+ int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+ if(fd == -1) {
+ if (errno == ENOTSUP) {
+ /* don't bother if we're working on an old kernel
+ or on the high level API */
+ return 0;
+ }
+
+ PERROR("open O_TMPFILE | O_RDWR");
+ return -1;
+ }
+ close(fd);
+
+ fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
+ if(fd == -1){
+ PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
+ return -1;
+ };
+ close(fd);
+
+ fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
+ if (fd != -1) {
+ ERROR("open with O_TMPFILE | O_RDONLY succeeded");
+ return -1;
+ }
+
+ success();
+ return 0;
+}
+
+static int test_create_and_link_tmpfile(void)
+{
+ /* skip this test for now since the github runner will fail in the linkat call below */
+ return 0;
+
+ rmdir(testdir);
+ unlink(testfile);
+
+ int res = mkdir(testdir, 0777);
+ if (res)
+ return -1;
+
+ start_test("create and link tmpfile");
+
+ int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
+ if(fd == -1) {
+ if (errno == ENOTSUP) {
+ /* don't bother if we're working on an old kernel
+ or on the high level API */
+ return 0;
+ }
+ PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
+ return -1;
+ }
+
+ if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
+ ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
+ return -1;
+ }
+ close(fd);
+
+ fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+ if(fd == -1) {
+ PERROR("open O_TMPFILE");
+ return -1;
+ }
+
+ if (check_nonexist(testfile)) {
+ return -1;
+ }
+
+ if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
+ PERROR("linkat tempfile");
+ return -1;
+ }
+ close(fd);
+
+ if (check_nlink(testfile, 1)) {
+ return -1;
+ }
+ unlink(testfile);
+
+ success();
+ return 0;
+}
+
int main(int argc, char *argv[])
{
int err = 0;
err += test_create_ro_dir(O_CREAT | O_WRONLY);
err += test_create_ro_dir(O_CREAT | O_TRUNC);
err += test_copy_file_range();
+ err += test_create_tmpfile();
+ err += test_create_and_link_tmpfile();
unlink(testfile2);
unlink(testsock);