summaryrefslogtreecommitdiff
path: root/document-portal
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2016-03-08 16:02:17 +0100
committerAlexander Larsson <alexl@redhat.com>2016-03-11 17:00:08 +0100
commit7a61eaa91db11e1d8f70eb36cfd42f91e103e201 (patch)
treed272740ff75c006e2efc2d68def01c2a1f92fbd5 /document-portal
parent3e5d4a02d8abb829b74a290a92b7431744e8ccda (diff)
downloadxdg-app-7a61eaa91db11e1d8f70eb36cfd42f91e103e201.tar.gz
Reimplement fuse backend
The magic inode numbers we used before are problematic. The inode nrs are tied to the file names, so inode changes on rename, which breaks posix expectations. Also, it relied on 64bit inode space which is not true on i386. So, this is a new implementation that uses a more traditional approach of dynamically allocating inodes as needed.
Diffstat (limited to 'document-portal')
-rw-r--r--document-portal/xdp-fuse.c2939
-rw-r--r--document-portal/xdp-fuse.h10
-rw-r--r--document-portal/xdp-main.c40
-rw-r--r--document-portal/xdp-util.c53
-rw-r--r--document-portal/xdp-util.h5
5 files changed, 1486 insertions, 1561 deletions
diff --git a/document-portal/xdp-fuse.c b/document-portal/xdp-fuse.c
index 1bd6a9e..c20618a 100644
--- a/document-portal/xdp-fuse.c
+++ b/document-portal/xdp-fuse.c
@@ -20,47 +20,81 @@
#include "xdp-util.h"
#include "xdg-app-utils.h"
-/* Layout:
-
- "/ (STD_DIRS:1)
- "by-app/" (STD_DIRS:2)
- "org.gnome.gedit/" (APP_DIR:app id)
- "$id/" (APP_DOC_DIR:app_id<<32|doc_id)
- <same as DOC_DIR>
- "$id" (APP_DOC_DIR:(app_id==0)<<32|doc_idid)
- $basename (APP_DOC_FILE:app_id<<32|doc_id) (app id == 0 if not in app dir)
- $tmpfile (TMPFILE:tmp_id)
-*/
-
-#define BY_APP_INO 2
-
#define NON_DOC_DIR_PERMS 0500
#define DOC_DIR_PERMS 0700
-/* The (fake) directories don't really change */
-#define DIRS_ATTR_CACHE_TIME 60.0
+/* TODO: What do we put here */
+#define ATTR_CACHE_TIME 60.0
+#define ENTRY_CACHE_TIME 60.0
/* We pretend that the file is hardlinked. This causes most apps to do
a truncating overwrite, which suits us better, as we do the atomic
- rename ourselves anyway. This way we don't weirdly change the inode
- after the rename. */
+ rename ourselves anyway. */
#define DOC_FILE_NLINK 2
typedef enum {
- STD_DIRS_INO_CLASS,
- TMPFILE_INO_CLASS,
- APP_DIR_INO_CLASS,
- APP_DOC_DIR_INO_CLASS,
- APP_DOC_FILE_INO_CLASS,
-} XdpInodeClass;
+ XDP_INODE_ROOT,
+ XDP_INODE_BY_APP,
+ XDP_INODE_APP_DIR, /* app id */
+ XDP_INODE_APP_DOC_DIR, /* app_id + doc id */
+ XDP_INODE_DOC_DIR, /* doc id */
+ XDP_INODE_DOC_FILE, /* doc id (NULL if tmp), name (== basename) */
+} XdpInodeType;
+
+typedef struct _XdpInode XdpInode;
+
+struct _XdpInode {
+ gint ref_count; /* atomic */
+
+ /* These are all immutable */
+ fuse_ino_t ino;
+ XdpInodeType type;
+ XdpInode *parent;
+ char *app_id;
+ char *doc_id;
+
+ /* For doc dirs */
+ char *dirname;
+ dev_t dir_dev;
+ ino_t dir_ino;
+
+ /* mutable data */
+
+ GList *children; /* lazily filled, protected by inodes lock */
+ char *filename; /* variable (for non-dirs), null if deleted,
+ protected by inodes lock *and* mutex */
+ gboolean is_doc; /* True if this is the document file for this dir */
+
+ /* Used when the file is open, protected by mutex */
+ GMutex mutex; /* Always lock inodes lock (if needed) before mutex */
+ GList *open_files;
+ int dir_fd;
+ int fd; /* RW fd for tempfiles, RO fd for doc files */
+ char *backing_filename;
+ char *trunc_filename;
+ int trunc_fd;
+ gboolean truncated;
+};
+
+typedef struct _XdpFile XdpFile;
+struct _XdpFile {
+ XdpInode *inode;
+ int open_mode;
+};
+
+#define ROOT_INODE 1
+#define BY_APP_INODE 2
#define BY_APP_NAME "by-app"
-static GHashTable *app_name_to_id;
-static GHashTable *app_id_to_name;
-static guint32 next_app_id = 1;
+static GHashTable *dir_to_inode_nr;
-G_LOCK_DEFINE(app_id);
+static GHashTable *inodes; /* The in memory XdpInode:s, protected by inodes lock */
+static XdpInode *root_inode;
+static XdpInode *by_app_inode;
+static fuse_ino_t next_inode_nr = 3;
+
+G_LOCK_DEFINE(inodes);
static GThread *fuse_thread = NULL;
static struct fuse_session *session = NULL;
@@ -69,724 +103,811 @@ static char *mount_path = NULL;
static pthread_t fuse_pthread = 0;
static int
-steal_fd (int *fdp)
+reopen_fd (int fd, int flags)
{
- int fd = *fdp;
- *fdp = -1;
- return fd;
+ g_autofree char *path = g_strdup_printf ("/proc/self/fd/%d", fd);
+ return open (path, flags | O_CLOEXEC);
}
-static int
-get_user_perms (const struct stat *stbuf)
+/* Call with inodes lock held */
+static fuse_ino_t
+allocate_inode_unlocked (void)
{
- /* Strip out exec and setuid bits */
- return stbuf->st_mode & 0666;
-}
+ fuse_ino_t next = next_inode_nr++;
-static double
-get_attr_cache_time (int st_mode)
-{
- if (S_ISDIR (st_mode))
- return DIRS_ATTR_CACHE_TIME;
- return 0.0;
-}
+ /* Bail out on overflow, to avoid reuse */
+ if (next <= 0)
+ g_assert_not_reached ();
-static double
-get_entry_cache_time (fuse_ino_t inode)
-{
- /* We have to disable entry caches because otherwise we have a race
- on rename. The kernel set the target inode as NOEXIST after a
- rename, which breaks in the tmp over real case due to us reusing
- the old non-temp inode. */
- return 0.0;
+ return next;
}
-/******************************* XdpTmp *******************************
- *
- * XdpTmp is a ref-counted object representing a temporary file created
- * on the outer filesystem which is stored next to a real file in the fuse
- * filesystem. Its useful to support write-to-tmp-then-rename-over-target
- * operations.
- *
- * locking:
- *
- * The global list of outstanding Tmp are protected by the tmp_files
- * lock. Use it when doing lookups by name or id, or when changing
- * the list (add/remove) or name of a tmpfile.
- *
- * Each instance has a mutex that locks access to the backing path,
- * as it can be removed at runtime. Use get/steal_backing_basename() to
- * safely access it.
- *
- ******************************* XdpTmp *******************************/
-
-static volatile gint next_tmp_id = 1;
-
-typedef struct
+static fuse_ino_t
+get_dir_inode_nr_unlocked (const char *app_id, const char *doc_id)
{
- volatile gint ref_count;
-
- /* These are immutable, no lock needed */
- guint64 parent_inode;
- guint32 tmp_id;
- XdgAppDbEntry *entry;
+ gpointer res;
+ fuse_ino_t allocated;
+ g_autofree char *dir = NULL;
- /* Changes always done under tmp_files lock */
- char *name;
-
- GMutex mutex;
+ if (app_id == NULL)
+ dir = g_strdup (doc_id);
+ else
+ {
+ if (doc_id == NULL)
+ dir = g_strconcat (app_id, "/", NULL);
+ else
+ dir = g_build_filename (app_id, doc_id, NULL);
+ }
- /* protected by mutex */
- char *backing_basename;
-} XdpTmp;
+ res = g_hash_table_lookup (dir_to_inode_nr, dir);
+ if (res != NULL)
+ return (fuse_ino_t)(gsize)res;
-/* Owns a ref to the files */
-static GList *tmp_files = NULL;
-G_LOCK_DEFINE(tmp_files);
+ allocated = allocate_inode_unlocked ();
+ g_hash_table_insert (dir_to_inode_nr, g_strdup (dir), (gpointer)allocated);
+ return allocated;
+}
-static XdpTmp *
-xdp_tmp_ref (XdpTmp *tmp)
+static fuse_ino_t
+get_dir_inode_nr (const char *app_id, const char *doc_id)
{
- g_atomic_int_inc (&tmp->ref_count);
- return tmp;
+ AUTOLOCK(inodes);
+ return get_dir_inode_nr_unlocked (app_id, doc_id);
}
static void
-xdp_tmp_unref (XdpTmp *tmp)
+allocate_app_dir_inode_nr (char **app_ids)
{
- if (g_atomic_int_dec_and_test (&tmp->ref_count))
- {
- xdg_app_db_entry_unref (tmp->entry);
- g_free (tmp->name);
- g_free (tmp->backing_basename);
- g_free (tmp);
- }
+ int i;
+ AUTOLOCK(inodes);
+ for (i = 0; app_ids[i] != NULL; i++)
+ get_dir_inode_nr_unlocked (app_ids[i], NULL);
}
-char *
-xdp_tmp_get_backing_basename (XdpTmp *tmp)
+static char **
+get_allocated_app_dirs (void)
{
- char *res;
- g_mutex_lock (&tmp->mutex);
- res = g_strdup (tmp->backing_basename);
- g_mutex_unlock (&tmp->mutex);
+ GHashTableIter iter;
+ gpointer key, value;
+ GPtrArray *array = g_ptr_array_new ();
- return res;
+ AUTOLOCK(inodes);
+ g_hash_table_iter_init (&iter, dir_to_inode_nr);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const char *name = key;
+
+ if (g_str_has_suffix (name, "/"))
+ {
+ char *app = strndup (name, strlen (name) - 1);
+ g_ptr_array_add (array, app);
+ }
+ }
+ g_ptr_array_add (array, NULL);
+ return (char **)g_ptr_array_free (array, FALSE);
}
-char *
-xdp_tmp_steal_backing_basename (XdpTmp *tmp)
-{
- char *res;
- g_mutex_lock (&tmp->mutex);
- res = tmp->backing_basename;
- tmp->backing_basename = NULL;
- g_mutex_unlock (&tmp->mutex);
+static void xdp_inode_unref_internal (XdpInode *inode, gboolean locked);
+static void xdp_inode_unref (XdpInode *inode);
- return res;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(XdpInode, xdp_inode_unref)
+
+static void
+xdp_inode_destroy (XdpInode *inode, gboolean locked)
+{
+ g_assert (inode->dir_fd == -1);
+ g_assert (inode->fd == -1);
+ g_assert (inode->trunc_fd == -1);
+ g_assert (inode->trunc_filename == NULL);
+ g_assert (inode->children == NULL);
+ xdp_inode_unref_internal (inode->parent, locked);
+ g_free (inode->backing_filename);
+ g_free (inode->filename);
+ g_free (inode->dirname);
+ g_free (inode->app_id);
+ g_free (inode->doc_id);
+ g_free (inode);
}
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(XdpTmp, xdp_tmp_unref)
+static XdpInode *
+xdp_inode_ref (XdpInode *inode)
+{
+ if (inode)
+ g_atomic_int_inc (&inode->ref_count);
+ return inode;
+}
-/* Must first take tmp_files lock */
-static XdpTmp *
-find_tmp_by_name_nolock (guint64 parent_inode,
- const char *name)
+static void
+xdp_inode_unref_internal (XdpInode *inode, gboolean locked)
{
- GList *l;
+ gint old_ref;
+
+ if (inode == NULL)
+ return;
- for (l = tmp_files; l != NULL; l = l->next)
+ /* here we want to atomically do: if (ref_count>1) { ref_count--; return; } */
+ retry_atomic_decrement1:
+ old_ref = g_atomic_int_get (&inode->ref_count);
+ if (old_ref > 1)
{
- XdpTmp *tmp = l->data;
- if (tmp->parent_inode == parent_inode &&
- strcmp (tmp->name, name) == 0)
- return xdp_tmp_ref (tmp);
+ if (!g_atomic_int_compare_and_exchange ((int *)&inode->ref_count, old_ref, old_ref - 1))
+ goto retry_atomic_decrement1;
}
+ else
+ {
+ if (old_ref <= 0)
+ {
+ g_warning ("Can't unref dead inode");
+ return;
+ }
+ /* Protect against revival from xdp_inode_lookup() */
+ if (!locked)
+ G_LOCK(inodes);
+ if (!g_atomic_int_compare_and_exchange ((int *)&inode->ref_count, old_ref, old_ref - 1))
+ {
+ if (!locked)
+ G_UNLOCK(inodes);
+ goto retry_atomic_decrement1;
+ }
- return NULL;
+ g_hash_table_remove (inodes, (gpointer)inode->ino);
+ if (inode->parent)
+ inode->parent->children = g_list_remove (inode->parent->children, inode);
+
+ if (!locked)
+ G_UNLOCK(inodes);
+
+ xdp_inode_destroy (inode, locked);
+ }
}
-/* Takes tmp_files lock */
-static XdpTmp *
-find_tmp_by_name (guint64 parent_inode,
- const char *name)
+static void
+xdp_inode_unref (XdpInode *inode)
{
- AUTOLOCK(tmp_files);
- return find_tmp_by_name_nolock (parent_inode, name);
+ return xdp_inode_unref_internal (inode, FALSE);
}
-/* Takes tmp_files lock */
-static XdpTmp *
-find_tmp_by_id (guint32 tmp_id)
+static XdpInode *
+xdp_inode_new_unlocked (fuse_ino_t ino,
+ XdpInodeType type,
+ XdpInode *parent,
+ const char *filename,
+ const char *app_id,
+ const char *doc_id)
{
- GList *l;
+ XdpInode *inode;
- AUTOLOCK(tmp_files);
+ inode = g_new0 (XdpInode, 1);
+ inode->ino = ino;
+ inode->type = type;
+ inode->parent = xdp_inode_ref (parent);
+ inode->filename = g_strdup (filename);
+ inode->app_id = g_strdup (app_id);
+ inode->doc_id = g_strdup (doc_id);
+ inode->ref_count = 1;
+ inode->dir_fd = -1;
+ inode->fd = -1;
+ inode->trunc_fd = -1;
- for (l = tmp_files; l != NULL; l = l->next)
- {
- XdpTmp *tmp = l->data;
- if (tmp->tmp_id == tmp_id)
- return xdp_tmp_ref (tmp);
- }
+ if (parent)
+ parent->children = g_list_prepend (parent->children, inode);
+ g_hash_table_insert (inodes, (gpointer)ino, inode);
- return NULL;
+ return inode;
}
-/* Caller must hold tmp_files lock */
-static XdpTmp *
-xdp_tmp_new_nolock (fuse_ino_t parent,
- XdgAppDbEntry *entry,
- const char *name,
- const char *tmp_basename)
+static XdpInode *
+xdp_inode_new (fuse_ino_t ino,
+ XdpInodeType type,
+ XdpInode *parent,
+ const char *filename,
+ const char *app_id,
+ const char *doc_id)
{
- XdpTmp *tmp;
- g_autofree char *tmp_dirname = NULL;
-
- /* We store the pathname instead of dir_fd + basename, because
- its very easy to get a lot of tempfiles leaking and that would
- mean quite a lot of open fds */
- tmp_dirname = xdp_entry_dup_dirname (entry);
-
- tmp = g_new0 (XdpTmp, 1);
- tmp->ref_count = 2; /* One owned by tmp_files */
- tmp->tmp_id = g_atomic_int_add (&next_tmp_id, 1);
- tmp->parent_inode = parent;
- tmp->name = g_strdup (name);
- tmp->entry = xdg_app_db_entry_ref (entry);
- tmp->backing_basename = g_strdup (tmp_basename);
+ AUTOLOCK(inodes);
+ return xdp_inode_new_unlocked (ino, type, parent, filename, app_id, doc_id);
+}
- tmp_files = g_list_prepend (tmp_files, tmp);
+static XdpInode *
+xdp_inode_lookup_unlocked (fuse_ino_t inode_nr)
+{
+ XdpInode *inode;
- return tmp;
+ inode = g_hash_table_lookup (inodes, (gpointer)inode_nr);
+ if (inode != NULL)
+ return xdp_inode_ref (inode);
+ return NULL;
}
-/* Caller must own tmp_files lock */
-static void
-xdp_tmp_unlink_nolock (XdpTmp *tmp)
+static GList *
+xdp_inode_list_children (XdpInode *inode)
{
+ GList *list = NULL, *l;
- g_autofree char *backing_basename = NULL;
-
- backing_basename = xdp_tmp_steal_backing_basename (tmp);
- if (backing_basename)
+ AUTOLOCK(inodes);
+ for (l = inode->children; l != NULL; l = l->next)
{
- glnx_fd_close int dir_fd = xdp_entry_open_dir (tmp->entry);
- if (dir_fd)
- unlinkat (dir_fd, backing_basename, 0);
+ XdpInode *child = l->data;
+
+ list = g_list_prepend (list, xdp_inode_ref (child));
}
- tmp_files = g_list_remove (tmp_files, tmp);
- xdp_tmp_unref (tmp);
+ return g_list_reverse (list);
}
-/******************************* XdpFh *******************************
- *
- * XdpFh is a ref-counted object representing an open file on the
- * filesystem. Normally it has a regular fd you can do only the allowed
- * i/o on, although in the case of a direct write to a document file
- * it has two fds, one is the read-only fd to the file, and the other
- * is a read-write to a temporary file which is only used once the
- * file is truncated (and is renamed over the real file on close).
- *
- * locking:
- *
- * The global list of outstanding Fh is protected by the open_files
- * lock. Use it when doing lookups by inode, or when changing
- * the list (add/remove), or when otherwise traversing the list.
- *
- * Each instance has a mutex that must be locked when doing some
- * kind of operation on the file handle, to serialize both lower
- * layer i/o as well as access to the members.
- *
- * To avoid deadlocks or just slow locking, never aquire the
- * open_files lock and a lock on a Fh at the same time.
- *
- ******************************* XdpFh *******************************/
-
-
-typedef struct
+static XdpInode *
+xdp_inode_lookup_child_unlocked (XdpInode *inode, const char *filename)
{
- volatile gint ref_count;
-
- /* These are immutable, no lock needed */
- guint32 tmp_id;
- fuse_ino_t inode;
- int dir_fd;
- char *trunc_basename;
- char *real_basename;
- gboolean can_write;
-
- /* These need a lock whenever they are used */
- int fd;
- int trunc_fd;
- gboolean truncated;
- gboolean readonly;
+ GList *l;
- GMutex mutex;
-} XdpFh;
+ for (l = inode->children; l != NULL; l = l->next)
+ {
+ XdpInode *child = l->data;
+ if (child->filename != NULL && strcmp (child->filename, filename) == 0)
+ return xdp_inode_ref (child);
+ }
-static GList *open_files = NULL;
-G_LOCK_DEFINE(open_files);
+ return NULL;
+}
-static XdpFh *
-xdp_fh_ref (XdpFh *fh)
+static XdpInode *
+xdp_inode_lookup_child (XdpInode *inode, const char *filename)
{
- g_atomic_int_inc (&fh->ref_count);
- return fh;
+ AUTOLOCK(inodes);
+ return xdp_inode_lookup_child_unlocked (inode, filename);
}
-static void
-xdp_fh_finalize (XdpFh *fh)
+static int
+xdp_inode_open_dir_fd (XdpInode *dir)
{
- if (fh->truncated)
- {
- fsync (fh->trunc_fd);
- if (renameat (fh->dir_fd, fh->trunc_basename,
- fh->dir_fd, fh->real_basename) != 0)
- g_warning ("Unable to replace truncated document");
- }
- else if (fh->trunc_basename)
- unlinkat (fh->dir_fd, fh->trunc_basename, 0);
+ struct stat st_buf;
+ glnx_fd_close int fd = -1;
- if (fh->fd >= 0)
- close (fh->fd);
+ g_assert (dir->dirname != NULL);
- if (fh->trunc_fd >= 0)
- close (fh->trunc_fd);
+ fd = open (dir->dirname, O_CLOEXEC | O_PATH | O_DIRECTORY);
+ if (fd == -1)
+ return -1;
- if (fh->dir_fd >= 0)
- close (fh->dir_fd);
+ if (fstat (fd, &st_buf) < 0)
+ {
+ errno = ENOENT;
+ return -1;
+ }
- g_clear_pointer (&fh->trunc_basename, g_free);
- g_clear_pointer (&fh->real_basename, g_free);
+ if (st_buf.st_ino != dir->dir_ino ||
+ st_buf.st_dev != dir->dir_dev)
+ {
+ errno = ENOENT;
+ return -1;
+ }
- g_free (fh);
+ return glnx_steal_fd (&fd);
}
static void
-xdp_fh_unref (XdpFh *fh)
+xdp_inode_unlink_backing_files (XdpInode *child_inode, int dir_fd)
{
- if (g_atomic_int_dec_and_test (&fh->ref_count))
+ if (dir_fd == -1)
{
- /* There is a tiny race here where fhs can be on the open_files list
- with refcount 0, so make sure to skip such while under the open_files
- lock */
- {
- AUTOLOCK (open_files);
- open_files = g_list_remove (open_files, fh);
- }
+ g_debug ("Can't unlink child inode due to no dir_fd");
+ return;
+ }
- xdp_fh_finalize (fh);
+ if (child_inode->is_doc)
+ {
+ g_debug ("unlinking doc file %s", child_inode->filename);
+ unlinkat (dir_fd, child_inode->filename, 0);
+ if (child_inode->trunc_filename != NULL)
+ {
+ g_debug ("unlinking doc trunc_file %s", child_inode->trunc_filename);
+ unlinkat (dir_fd, child_inode->trunc_filename, 0);
+ }
+ }
+ else
+ {
+ g_debug ("unlinking tmp_file %s", child_inode->backing_filename);
+ unlinkat (dir_fd, child_inode->backing_filename, 0);
}
}
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(XdpFh, xdp_fh_unref)
-
static void
-xdp_fh_lock (XdpFh *fh)
+xdp_inode_do_unlink (XdpInode *child_inode, int dir_fd, gboolean unlink_backing)
{
- g_mutex_lock (&fh->mutex);
-}
+ if (unlink_backing)
+ xdp_inode_unlink_backing_files (child_inode, dir_fd);
-static void
-xdp_fh_unlock (XdpFh *fh)
-{
- g_mutex_unlock (&fh->mutex);
-}
+ /* Zero out filename to mark it deleted */
+ g_free (child_inode->filename);
+ child_inode->filename = NULL;
-static inline void xdp_fh_auto_unlock_helper (XdpFh **fhp)
-{
- if (*fhp)
- xdp_fh_unlock (*fhp);
+ /* Drop keep-alive-until-unlink ref */
+ if (!child_inode->is_doc)
+ xdp_inode_unref (child_inode);
}
-static inline XdpFh *xdp_fh_auto_lock_helper (XdpFh *fh)
+static XdpInode *
+xdp_inode_unlink_child (XdpInode *dir, const char *filename)
{
- if (fh)
- xdp_fh_lock (fh);
- return fh;
-}
+ XdpInode *child_inode;
+ glnx_fd_close int dir_fd = -1;
-#define XDP_FH_AUTOLOCK(_fh) G_GNUC_UNUSED __attribute__((cleanup(xdp_fh_auto_unlock_helper))) XdpFh * G_PASTE(xdp_fh_auto_unlock, __LINE__) = xdp_fh_auto_lock_helper (fh)
+ AUTOLOCK(inodes);
+ child_inode = xdp_inode_lookup_child_unlocked (dir, filename);
+ if (child_inode == NULL)
+ return NULL;
-static XdpFh *
-xdp_fh_new (fuse_ino_t inode,
- struct fuse_file_info *fi,
- int fd,
- XdpTmp *tmp)
-{
- XdpFh *fh = g_new0 (XdpFh, 1);
- fh->inode = inode;
- fh->fd = fd;
- if (tmp)
- fh->tmp_id = tmp->tmp_id;
- fh->dir_fd = -1;
- fh->trunc_fd = -1;
- fh->ref_count = 1; /* Owned by fuse_file_info fi */
+ g_assert (child_inode->type == XDP_INODE_DOC_FILE);
+ g_assert (child_inode->filename != NULL);
- fi->fh = (gsize)fh;
+ /* Here we take *both* the inodes lock and the mutex.
+ The inodes lock is to make this safe against concurrent lookups,
+ but the mutex is to make it safe to access inode->filename inside
+ a mutex-only lock */
+ g_mutex_lock (&child_inode->mutex);
- AUTOLOCK (open_files);
+ dir_fd = xdp_inode_open_dir_fd (dir);
- open_files = g_list_prepend (open_files, fh);
+ xdp_inode_do_unlink (child_inode, dir_fd, TRUE);
- return fh;
-}
+ g_mutex_unlock (&child_inode->mutex);
-static int
-xdp_fh_get_fd_nolock (XdpFh *fh)
-{
- if (fh->truncated)
- return fh->trunc_fd;
- else
- return fh->fd;
+ return child_inode;
}
+/* Sets errno */
static int
-xdp_fh_fstat (XdpFh *fh,
- struct stat *stbuf)
-{
- struct stat tmp_stbuf;
- int fd;
-
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd < 0)
- return -ENOSYS;
-
- if (fstat (fd, &tmp_stbuf) != 0)
- return -errno;
-
- stbuf->st_nlink = DOC_FILE_NLINK;
- stbuf->st_mode = S_IFREG | get_user_perms (&tmp_stbuf);
- if (!fh->can_write)
- stbuf->st_mode &= ~(0222);
- stbuf->st_size = tmp_stbuf.st_size;
- stbuf->st_uid = tmp_stbuf.st_uid;
- stbuf->st_gid = tmp_stbuf.st_gid;
- stbuf->st_blksize = tmp_stbuf.st_blksize;
- stbuf->st_blocks = tmp_stbuf.st_blocks;
- stbuf->st_atim = tmp_stbuf.st_atim;
- stbuf->st_mtim = tmp_stbuf.st_mtim;
- stbuf->st_ctim = tmp_stbuf.st_ctim;
+xdp_inode_rename_child (XdpInode *dir,
+ const char *src_filename,
+ const char *dst_filename,
+ const char *doc_basename)
+{
+ g_autoptr(XdpInode) src_inode = NULL;
+ g_autoptr(XdpInode) dst_inode = NULL;
+ glnx_fd_close int dir_fd = -1;
+ int res;
- return 0;
-}
+ AUTOLOCK(inodes);
+ src_inode = xdp_inode_lookup_child_unlocked (dir, src_filename);
+ if (src_inode == NULL)
+ {
+ errno = ENOENT;
+ return -1;
+ }
-static int
-xdp_fh_fstat_locked (XdpFh *fh,
- struct stat *stbuf)
-{
- XDP_FH_AUTOLOCK (fh);
+ g_assert (src_inode->type == XDP_INODE_DOC_FILE);
+ g_assert (src_inode->filename != NULL);
- return xdp_fh_fstat (fh, stbuf);
-}
+ dst_inode = xdp_inode_lookup_child_unlocked (dir, dst_filename);
+ if (dst_inode)
+ {
+ g_assert (dst_inode->type == XDP_INODE_DOC_FILE);
+ g_assert (dst_inode->filename != NULL);
+ }
-static int
-xdp_fh_truncate_locked (XdpFh *fh, off_t size, struct stat *newattr)
-{
- int fd;
+ /* Here we take *both* the inodes lock and the mutex.
+ The inodes lock is to make this safe against concurrent lookups,
+ but the mutex is to make it safe to access inode->filename inside
+ a mutex-only lock */
+ g_mutex_lock (&src_inode->mutex);
+ if (dst_inode)
+ g_mutex_lock (&dst_inode->mutex);
- XDP_FH_AUTOLOCK (fh);
+ dir_fd = xdp_inode_open_dir_fd (dir);
+ res = 0;
- if (fh->trunc_fd >= 0 && !fh->truncated)
+ if (src_inode->is_doc)
{
- if (size != 0)
- return -EACCES;
-
- fh->truncated = TRUE;
- fd = fh->trunc_fd;
+ /* doc -> tmp */
+
+ /* We don't want to allow renaming an exiting doc file, because
+ doing so would make a tmpfile of the real doc-file which some
+ host-side app may have open. You have to make a copy and
+ remove instead. */
+ errno = EACCES;
+ res = -1;
}
- else
+ else if (strcmp (dst_filename, doc_basename) != 0)
{
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd == -1)
- return -EIO;
+ /* tmp -> tmp */
- if (ftruncate (fd, size) != 0)
- return - errno;
- }
+ if (dst_inode)
+ xdp_inode_do_unlink (dst_inode, dir_fd, TRUE);
- if (newattr)
- {
- int res = xdp_fh_fstat (fh, newattr);
- if (res < 0)
- return res;
+ g_free (src_inode->filename);
+ src_inode->filename = g_strdup (dst_filename);
}
+ else
+ {
+ /* tmp -> doc */
- return 0;
-}
-
-static void
-mark_open_tmp_file_readonly (guint32 tmp_id)
-{
- GList *found = NULL;
- GList *l;
+ g_debug ("atomic renaming %s to %s", src_inode->backing_filename, dst_filename);
+ res = renameat (dir_fd, src_inode->backing_filename,
+ dir_fd, dst_filename);
+ if (res == 0)
+ {
+ if (dst_inode != NULL)
+ {
+ /* Unlink, but don't remove backing files, which are now the new one */
+ xdp_inode_do_unlink (dst_inode, dir_fd, FALSE);
- {
- AUTOLOCK (open_files);
+ /* However, unlink trunc_file if its there */
+ if (dst_inode->trunc_filename)
+ unlinkat (dir_fd, dst_inode->trunc_filename, 0);
+ }
- for (l = open_files; l != NULL; l = l->next)
- {
- XdpFh *fh = l->data;
- /* See xdp_fh_unref for details of this ref_count check */
- if (g_atomic_int_get (&fh->ref_count) > 0 &&
- fh->tmp_id == tmp_id && fh->fd >= 0)
- found = g_list_prepend (found, xdp_fh_ref (fh));
- }
- }
+ src_inode->is_doc = TRUE;
+ g_free (src_inode->filename);
+ src_inode->filename = g_strdup (dst_filename);
+ g_free (src_inode->backing_filename);
+ src_inode->backing_filename = g_strdup (dst_filename);
- /* We do the actual updates outside of the open_files lock to avoid
- potentially blocking for a long time with it held */
+ /* Convert ->fd to read-only */
+ if (src_inode->fd != -1)
+ {
+ int new_fd = reopen_fd (src_inode->fd, O_RDONLY);
+ close (src_inode->fd);
+ src_inode->fd = new_fd;
+ }
- for (l = found; l != NULL; l = l->next)
- {
- XdpFh *fh = l->data;
- XDP_FH_AUTOLOCK (fh);
- fh->readonly = TRUE;
- xdp_fh_unref (fh);
+ /* This neuters any outstanding write files, since we have no trunc_fd at this point.
+ However, that is not really a problem, we would not support them well anyway as
+ a newly opened trunc file would have to have a truncate operation initially for
+ it to work anyway */
+ }
}
- g_list_free (found);
+
+ g_mutex_unlock (&src_inode->mutex);
+ if (dst_inode)
+ g_mutex_unlock (&dst_inode->mutex);
+
+ return res;
}
+/* NULL if removed */
+char *
+xdp_inode_get_filename (XdpInode *inode)
+{
+ AUTOLOCK(inodes);
+ return g_strdup (inode->filename);
+}
-static XdpFh *
-find_open_fh (fuse_ino_t ino)
+static XdpInode *
+xdp_inode_ensure_document_file (XdpInode *dir,
+ XdgAppDbEntry *entry)
{
- GList *l;
+ g_autofree char *basename = xdp_entry_dup_basename (entry);
+ XdpInode *inode;
+
+ g_assert (dir->type == XDP_INODE_APP_DOC_DIR || dir->type == XDP_INODE_DOC_DIR);
- AUTOLOCK (open_files);
+ AUTOLOCK(inodes);
- for (l = open_files; l != NULL; l = l->next)
+ inode = xdp_inode_lookup_child_unlocked (dir, basename);
+ if (inode == NULL)
{
- XdpFh *fh = l->data;
- /* See xdp_fh_unref for details of this ref_count check */
- if (fh->inode == ino &&
- g_atomic_int_get (&fh->ref_count) > 0)
- return xdp_fh_ref (fh);
+ inode = xdp_inode_new_unlocked (allocate_inode_unlocked (),
+ XDP_INODE_DOC_FILE,
+ dir,
+ basename,
+ dir->app_id,
+ dir->doc_id);
+ inode->backing_filename = g_steal_pointer (&basename);
+ inode->is_doc = TRUE;
}
- return NULL;
+ return inode;
}
-/******************************* Main *******************************/
-
-static XdpInodeClass
-get_class (guint64 inode)
+static char *
+create_tmp_for_doc (XdgAppDbEntry *entry, int dir_fd, int flags, mode_t mode, int *fd_out)
{
- return (inode >> (64-8)) & 0xff;
-}
+ g_autofree char *basename = xdp_entry_dup_basename (entry);
+ g_autofree char *template = g_strconcat (".xdp_", basename, ".XXXXXX", NULL);
+ int fd;
-static guint64
-get_class_ino (guint64 inode)
-{
- return inode & ((1L << (64-8)) - 1);
-}
+ fd = xdg_app_mkstempat (dir_fd, template, flags|O_CLOEXEC, mode);
+ if (fd == -1)
+ return NULL;
-static guint32
-get_app_id_from_app_doc_ino (guint64 inode)
-{
- return inode >> 32;
+ g_debug ("Created temp file %s", template);
+ *fd_out = fd;
+ return g_steal_pointer (&template);
}
-static guint32
-get_doc_id_from_app_doc_ino (guint64 inode)
+/* sets errno */
+static XdpInode *
+xdp_inode_create_file (XdpInode *dir,
+ XdgAppDbEntry *entry,
+ const char *filename,
+ mode_t mode,
+ gboolean truncate,
+ gboolean exclusive)
{
- return inode & 0xffffffff;
-}
+ XdpInode *inode;
+ g_autofree char *basename = xdp_entry_dup_basename (entry);
+ g_autofree char *backing_filename = NULL;
+ g_autofree char *trunc_filename = NULL;
+ gboolean is_doc;
+ glnx_fd_close int dir_fd = -1;
+ glnx_fd_close int fd = -1;
+ glnx_fd_close int trunc_fd = -1;
-static guint64
-make_inode (XdpInodeClass class, guint64 inode)
-{
- return ((guint64)class) << (64-8) | (inode & 0xffffffffffffff);
-}
+ g_assert (dir->type == XDP_INODE_APP_DOC_DIR || dir->type == XDP_INODE_DOC_DIR);
-static guint64
-make_app_doc_dir_inode (guint32 app_id, guint32 doc_id)
-{
- return make_inode (APP_DOC_DIR_INO_CLASS,
- ((guint64)app_id << 32) | (guint64)doc_id);
-}
+ AUTOLOCK(inodes);
-static guint64
-make_app_doc_file_inode (guint32 app_id, guint32 doc_id)
-{
- return make_inode (APP_DOC_FILE_INO_CLASS,
- ((guint64)app_id << 32) | (guint64)doc_id);
-}
+ inode = xdp_inode_lookup_child_unlocked (dir, filename);
+ if (inode != NULL)
+ {
+ if (exclusive)
+ {
+ xdp_inode_unref (inode);
+ errno = EEXIST;
+ return NULL;
+ }
-static gboolean
-name_looks_like_id (const char *name)
-{
- int i;
+ if (truncate)
+ {
+ /* TODO: Handle extra truncate for existing file */
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ return inode;
+ }
- /* No zeros in front, we need canonical form */
- if (name[0] == '0')
- return FALSE;
+ dir_fd = xdp_inode_open_dir_fd (dir);
+ if (dir_fd == -1)
+ return NULL;
- for (i = 0; i < 8; i++)
+ is_doc = strcmp (basename, filename) == 0;
+
+ if (is_doc)
+ {
+ backing_filename = g_strdup (filename);
+ int flags = O_CREAT|O_RDONLY|O_NOFOLLOW|O_CLOEXEC;
+
+ if (exclusive)
+ flags |= O_EXCL;
+
+ g_debug ("Creating doc file %s", basename);
+ fd = openat (dir_fd, basename, flags, mode & 0777);
+ if (fd < 0)
+ return NULL;
+
+ trunc_filename = create_tmp_for_doc (entry, dir_fd, O_RDWR, mode & 0777, &trunc_fd);
+ if (trunc_filename == NULL)
+ return NULL;
+ }
+ else
{
- char c = name[i];
- if (c == 0)
- break;
+ backing_filename = create_tmp_for_doc (entry, dir_fd, O_RDWR, mode & 0777, &fd);
+ if (backing_filename == NULL)
+ return NULL;
+ }
- if (!g_ascii_isdigit(c) &&
- !(c >= 'a' && c <= 'f'))
- return FALSE;
+ inode = xdp_inode_new_unlocked (allocate_inode_unlocked (),
+ XDP_INODE_DOC_FILE,
+ dir,
+ filename,
+ dir->app_id,
+ dir->doc_id);
+ inode->backing_filename = g_steal_pointer (&backing_filename);
+ inode->trunc_filename = g_steal_pointer (&trunc_filename);
+ inode->is_doc = is_doc;
+ inode->dir_fd = glnx_steal_fd (&dir_fd);
+ inode->fd = glnx_steal_fd (&fd);
+ inode->trunc_fd = glnx_steal_fd (&trunc_fd);
+ if (inode->trunc_fd != -1 && truncate)
+ {
+ inode->truncated = TRUE;
+ g_free (inode->backing_filename);
+ inode->backing_filename = g_strdup (inode->trunc_filename);
}
- if (name[i] != 0)
- return FALSE;
+ /* We add an extra ref for tmp files to keep them alive until unlink */
+ if (!is_doc)
+ xdp_inode_ref (inode);
- return TRUE;
+ return inode;
}
-static guint32
-get_app_id_from_name (const char *name)
+static XdpInode *
+xdp_inode_lookup (fuse_ino_t inode_nr)
{
- guint32 id;
- char *myname;
+ AUTOLOCK(inodes);
+ return xdp_inode_lookup_unlocked (inode_nr);
+}
+
+static XdpInode *
+xdp_inode_get_dir_unlocked (const char *app_id, const char *doc_id, XdgAppDbEntry *entry)
+{
+ fuse_ino_t ino;
+ XdpInode *inode;
+ XdpInode *parent = NULL;
+ XdpInodeType type;
+ const char *filename;
- AUTOLOCK(app_id);
+ ino = get_dir_inode_nr_unlocked (app_id, doc_id);
- id = GPOINTER_TO_UINT (g_hash_table_lookup (app_name_to_id, name));
+ inode = xdp_inode_lookup_unlocked (ino);
+ if (inode)
+ return inode;
- if (id != 0)
- return id;
+ if (app_id == NULL)
+ {
+ g_assert (doc_id != NULL);
+ parent = xdp_inode_ref (root_inode);
+ type = XDP_INODE_DOC_DIR;
+ filename = doc_id;
+ }
+ else
+ {
+ if (doc_id == NULL)
+ {
+ parent = xdp_inode_ref (by_app_inode);
+ filename = app_id;
+ type = XDP_INODE_APP_DIR;
+ }
+ else
+ {
+ parent = xdp_inode_get_dir_unlocked (app_id, NULL, NULL);
+ filename = doc_id;
+ type = XDP_INODE_APP_DOC_DIR;
+ }
+ }
- id = next_app_id++;
+ inode = xdp_inode_new_unlocked (ino, type, parent, filename, app_id, doc_id);
+ xdp_inode_unref_internal (parent, TRUE);
- /* We rely this to not overwrap into the high byte in the inode */
- g_assert (id < 0x00ffffff);
+ if (entry)
+ {
+ inode->dirname = xdp_entry_dup_dirname (entry);
+ inode->dir_ino = xdp_entry_get_inode (entry);
+ inode->dir_dev = xdp_entry_get_device (entry);
+ }
- myname = g_strdup (name);
- g_hash_table_insert (app_name_to_id, myname, GUINT_TO_POINTER (id));
- g_hash_table_insert (app_id_to_name, GUINT_TO_POINTER (id), myname);
- return id;
+ return inode;
}
-static const char *
-get_app_name_from_id (guint32 id)
+static XdpInode *
+xdp_inode_get_dir (const char *app_id, const char *doc_id, XdgAppDbEntry *entry)
{
- AUTOLOCK(app_id);
- return g_hash_table_lookup (app_id_to_name, GUINT_TO_POINTER (id));
+ AUTOLOCK(inodes);
+ return xdp_inode_get_dir_unlocked (app_id, doc_id, entry);
}
-static void
-fill_app_name_hash (void)
-{
- g_auto(GStrv) keys = NULL;
- int i;
+/********************************************************************** \
+ * FUSE Implementation
+\***********************************************************************/
- keys = xdp_list_apps ();
- for (i = 0; keys[i] != NULL; i++)
- get_app_id_from_name (keys[i]);
+static int
+get_user_perms (const struct stat *stbuf)
+{
+ /* Strip out exec and setuid bits */
+ return stbuf->st_mode & 0666;
}
static gboolean
-app_can_see_doc (XdgAppDbEntry *entry, guint32 app_id)
+app_can_write_doc (XdgAppDbEntry *entry, const char *app_id)
{
- const char *app_name = get_app_name_from_id (app_id);
-
- if (app_id == 0)
+ if (app_id == NULL)
return TRUE;
- if (app_name != NULL &&
- xdp_entry_has_permissions (entry, app_name, XDP_PERMISSION_FLAGS_READ))
+ if (xdp_entry_has_permissions (entry, app_id, XDP_PERMISSION_FLAGS_WRITE))
return TRUE;
return FALSE;
}
static gboolean
-app_can_write_doc (XdgAppDbEntry *entry, guint32 app_id)
+app_can_see_doc (XdgAppDbEntry *entry, const char *app_id)
{
- const char *app_name = get_app_name_from_id (app_id);
-
- if (app_id == 0)
+ if (app_id == NULL)
return TRUE;
- if (app_name != NULL &&
- xdp_entry_has_permissions (entry, app_name, XDP_PERMISSION_FLAGS_WRITE))
+ if (xdp_entry_has_permissions (entry, app_id, XDP_PERMISSION_FLAGS_READ))
return TRUE;
return FALSE;
}
-
+/* Call with mutex held! */
static int
-xdp_stat (fuse_ino_t ino,
- struct stat *stbuf,
- XdgAppDbEntry **entry_out)
+xdp_inode_locked_get_fd (XdpInode *inode)
{
- XdpInodeClass class = get_class (ino);
- guint64 class_ino = get_class_ino (ino);
- g_autoptr (XdgAppDbEntry) entry = NULL;
- struct stat tmp_stbuf;
- g_autoptr(XdpTmp) tmp = NULL;
- g_autofree char *backing_basename = NULL;
+ if (inode->truncated)
+ return inode->trunc_fd;
- stbuf->st_ino = ino;
+ return inode->fd;
+}
- switch (class)
+/* Call with mutex held! */
+static int
+xdp_inode_locked_get_write_fd (XdpInode *inode)
+{
+ if (inode->is_doc)
{
- case STD_DIRS_INO_CLASS:
-
- switch (class_ino)
+ if (!inode->truncated)
{
- case FUSE_ROOT_ID:
- stbuf->st_mode = S_IFDIR | NON_DOC_DIR_PERMS;
- stbuf->st_nlink = 2;
- break;
-
- case BY_APP_INO:
- stbuf->st_mode = S_IFDIR | NON_DOC_DIR_PERMS;
- stbuf->st_nlink = 2;
- break;
-
- default:
- return ENOENT;
+ errno = ENOSYS;
+ return -1;
}
- break;
+ return inode->trunc_fd;
+ }
- case APP_DIR_INO_CLASS:
- if (get_app_name_from_id (class_ino) == 0)
- return ENOENT;
+ return inode->fd;
+}
+
+static int
+xdp_inode_stat (XdpInode *inode,
+ struct stat *stbuf)
+{
+ stbuf->st_ino = inode->ino;
+ switch (inode->type)
+ {
+ case XDP_INODE_ROOT:
+ case XDP_INODE_BY_APP:
+ case XDP_INODE_APP_DIR:
stbuf->st_mode = S_IFDIR | NON_DOC_DIR_PERMS;
stbuf->st_nlink = 2;
break;
- case APP_DOC_DIR_INO_CLASS:
+ case XDP_INODE_DOC_DIR:
+ case XDP_INODE_APP_DOC_DIR:
+ stbuf->st_mode = S_IFDIR | DOC_DIR_PERMS;
+ stbuf->st_nlink = 2;
+ break;
+
+ case XDP_INODE_DOC_FILE:
{
- guint32 app_id = get_app_id_from_app_doc_ino (class_ino);
- guint32 doc_id = get_doc_id_from_app_doc_ino (class_ino);
+ g_autoptr (XdgAppDbEntry) entry = NULL;
+ struct stat tmp_stbuf;
+ gboolean can_see, can_write;
+ int fd, res, errsv;
- entry = xdp_lookup_doc (doc_id);
- if (entry == NULL || !app_can_see_doc (entry, app_id))
- return ENOENT;
+ entry = xdp_lookup_doc (inode->doc_id);
+ if (entry == NULL)
+ {
+ errno = ENOENT;
+ return -1;
+ }
- stbuf->st_mode = S_IFDIR | DOC_DIR_PERMS;
- stbuf->st_nlink = 2;
- break;
- }
+ can_see = app_can_see_doc (entry, inode->app_id);
+ can_write = app_can_write_doc (entry, inode->app_id);
- case APP_DOC_FILE_INO_CLASS:
- {
- guint32 app_id = get_app_id_from_app_doc_ino (class_ino);
- guint32 doc_id = get_doc_id_from_app_doc_ino (class_ino);
- gboolean can_write;
+ if (!can_see)
+ {
+ errno = ENOENT;
+ return -1;
+ }
- entry = xdp_lookup_doc (doc_id);
- if (entry == NULL)
- return ENOENT;
+ g_mutex_lock (&inode->mutex);
- can_write = app_can_write_doc (entry, app_id);
+ fd = xdp_inode_locked_get_fd (inode);
+ if (fd != -1)
+ res = fstat (fd, &tmp_stbuf);
+ else
+ {
+ glnx_fd_close int dir_fd = xdp_inode_open_dir_fd (inode->parent);
+
+ if (dir_fd == -1)
+ res = -1;
+ else
+ res = fstatat (dir_fd, inode->backing_filename,
+ &tmp_stbuf, AT_SYMLINK_NOFOLLOW);
+ }
+ errsv = errno;
- stbuf->st_nlink = DOC_FILE_NLINK;
+ g_mutex_unlock (&inode->mutex);
- if (xdp_entry_stat (entry, &tmp_stbuf, AT_SYMLINK_NOFOLLOW) != 0)
- return ENOENT;
+ if (res != 0)
+ {
+ errno = errsv;
+ return -1;
+ }
stbuf->st_mode = S_IFREG | get_user_perms (&tmp_stbuf);
if (!can_write)
@@ -799,239 +920,138 @@ xdp_stat (fuse_ino_t ino,
stbuf->st_atim = tmp_stbuf.st_atim;
stbuf->st_mtim = tmp_stbuf.st_mtim;
stbuf->st_ctim = tmp_stbuf.st_ctim;
- break;
}
-
- case TMPFILE_INO_CLASS:
- tmp = find_tmp_by_id (class_ino);
- if (tmp == NULL)
- return ENOENT;
-
- stbuf->st_mode = S_IFREG;
- stbuf->st_nlink = DOC_FILE_NLINK;
-
- backing_basename = xdp_tmp_get_backing_basename (tmp);
-
- {
- glnx_fd_close int dir_fd = xdp_entry_open_dir (tmp->entry);
-
- if (backing_basename == NULL ||
- dir_fd == -1 ||
- fstatat (dir_fd, backing_basename, &tmp_stbuf, 0) != 0)
- return ENOENT;
- }
-
- stbuf->st_mode = S_IFREG | get_user_perms (&tmp_stbuf);
- stbuf->st_size = tmp_stbuf.st_size;
- stbuf->st_uid = tmp_stbuf.st_uid;
- stbuf->st_gid = tmp_stbuf.st_gid;
- stbuf->st_blksize = tmp_stbuf.st_blksize;
- stbuf->st_blocks = tmp_stbuf.st_blocks;
- stbuf->st_atim = tmp_stbuf.st_atim;
- stbuf->st_mtim = tmp_stbuf.st_mtim;
- stbuf->st_ctim = tmp_stbuf.st_ctim;
break;
default:
- return ENOENT;
+ g_assert_not_reached ();
}
- if (entry && entry_out)
- *entry_out = g_steal_pointer (&entry);
-
return 0;
}
static void
-xdp_fuse_getattr (fuse_req_t req,
- fuse_ino_t ino,
- struct fuse_file_info *fi)
+xdp_fuse_lookup (fuse_req_t req,
+ fuse_ino_t parent,
+ const char *name)
{
- struct stat stbuf = { 0 };
- g_autoptr(XdpFh) fh = NULL;
- int res;
-
- g_debug ("xdp_fuse_getattr %lx (fi=%p)", ino, fi);
-
- /* Fuse passes fi in to verify EOF during read/write/seek, but not during fstat */
- if (fi != NULL)
- {
- XdpFh *fh = (gpointer)fi->fh;
-
- res = xdp_fh_fstat_locked (fh, &stbuf);
- if (res == 0)
- {
- fuse_reply_attr (req, &stbuf, get_attr_cache_time (stbuf.st_mode));
- return;
- }
- }
+ g_autoptr(XdpInode) parent_inode = NULL;
+ struct fuse_entry_param e = {0};
+ g_autoptr(XdpInode) child_inode = NULL;
+ g_autoptr (XdgAppDbEntry) entry = NULL;
+ g_debug ("xdp_fuse_lookup %lx/%s -> ", parent, name);
- fh = find_open_fh (ino);
- if (fh)
+ parent_inode = xdp_inode_lookup (parent);
+ if (parent_inode == NULL)
{
- res = xdp_fh_fstat_locked (fh, &stbuf);
- if (res == 0)
- {
- fuse_reply_attr (req, &stbuf, get_attr_cache_time (stbuf.st_mode));
- return;
- }
+ g_debug ("xdp_fuse_lookup <- error parent ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
}
- if ((res = xdp_stat (ino, &stbuf, NULL)) != 0)
- fuse_reply_err (req, res);
- else
- fuse_reply_attr (req, &stbuf, get_attr_cache_time (stbuf.st_mode));
-}
-
-static int
-xdp_lookup (fuse_ino_t parent,
- const char *name,
- fuse_ino_t *inode,
- struct stat *stbuf,
- XdgAppDbEntry **entry_out,
- XdpTmp **tmp_out)
-{
- XdpInodeClass parent_class = get_class (parent);
- guint64 parent_class_ino = get_class_ino (parent);
- g_autoptr (XdgAppDbEntry) entry = NULL;
- g_autoptr (XdpTmp) tmp = NULL;
-
- if (entry_out)
- *entry_out = NULL;
- if (tmp_out)
- *tmp_out = NULL;
+ /* Default */
+ e.attr_timeout = ATTR_CACHE_TIME;
+ e.entry_timeout = ENTRY_CACHE_TIME;
- switch (parent_class)
+ switch (parent_inode->type)
{
- case STD_DIRS_INO_CLASS:
-
- switch (parent_class_ino)
+ case XDP_INODE_ROOT:
+ if (strcmp (name, BY_APP_NAME) == 0)
+ child_inode = xdp_inode_ref (by_app_inode);
+ else
{
- case FUSE_ROOT_ID:
- if (strcmp (name, BY_APP_NAME) == 0)
- {
- *inode = make_inode (STD_DIRS_INO_CLASS, BY_APP_INO);
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- return 0;
- }
- else if (name_looks_like_id (name))
- {
- *inode = make_app_doc_dir_inode (0, xdp_id_from_name (name));
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- return 0;
- }
-
- break;
-
- case BY_APP_INO:
- if (xdg_app_is_valid_name (name))
- {
- guint32 app_id = get_app_id_from_name (name);
- *inode = make_inode (APP_DIR_INO_CLASS, app_id);
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- return 0;
- }
-
- break;
-
- default:
- break;
+ entry = xdp_lookup_doc (name);
+ if (entry != NULL)
+ child_inode = xdp_inode_get_dir (NULL, name, entry);
}
break;
- case APP_DIR_INO_CLASS:
- {
- if (name_looks_like_id (name))
- {
- *inode = make_app_doc_dir_inode (parent_class_ino,
- xdp_id_from_name (name));
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- return 0;
- }
- }
+ case XDP_INODE_BY_APP:
+ /* This lazily creates the app dir */
+ if (xdg_app_is_valid_name (name))
+ child_inode = xdp_inode_get_dir (name, NULL, NULL);
+ break;
+ case XDP_INODE_APP_DIR:
+ entry = xdp_lookup_doc (name);
+ if (entry != NULL &&
+ app_can_see_doc (entry, parent_inode->app_id))
+ child_inode = xdp_inode_get_dir (parent_inode->app_id, name, entry);
break;
- case APP_DOC_DIR_INO_CLASS:
+ case XDP_INODE_APP_DOC_DIR:
+ case XDP_INODE_DOC_DIR:
{
- guint32 app_id = get_app_id_from_app_doc_ino (parent_class_ino);
- guint32 doc_id = get_doc_id_from_app_doc_ino (parent_class_ino);
-
- entry = xdp_lookup_doc (doc_id);
- if (entry != NULL)
+ g_autoptr(XdpInode) doc_inode = NULL;
+ entry = xdp_lookup_doc (parent_inode->doc_id);
+ if (entry == NULL)
{
- g_autofree char *basename = xdp_entry_dup_basename (entry);
- if (strcmp (name, basename) == 0)
- {
- *inode = make_app_doc_file_inode (app_id, doc_id);
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- {
- if (entry_out)
- *entry_out = g_steal_pointer (&entry);
- return 0;
- }
-
- break;
- }
+ g_debug ("xdp_fuse_lookup <- error no parent entry ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
}
- tmp = find_tmp_by_name (parent, name);
- if (tmp != NULL)
- {
- *inode = make_inode (TMPFILE_INO_CLASS, tmp->tmp_id);
- if (xdp_stat (*inode, stbuf, NULL) == 0)
- {
- if (entry_out)
- *entry_out = g_steal_pointer (&entry);
- if (tmp_out)
- *tmp_out = g_steal_pointer (&tmp);
- return 0;
- }
+ /* Ensure it is alive at least during lookup_child () */
+ doc_inode = xdp_inode_ensure_document_file (parent_inode, entry);
- break;
- }
+ child_inode = xdp_inode_lookup_child (parent_inode, name);
+
+ /* We verify in the stat below if the backing file exists */
- break;
+ /* Files can be changed from outside the fuse fs, so don't cache any data */
+ e.attr_timeout = 0;
+ e.entry_timeout = 0;
}
+ break;
- case TMPFILE_INO_CLASS:
- case APP_DOC_FILE_INO_CLASS:
- return ENOTDIR;
+ case XDP_INODE_DOC_FILE:
+ fuse_reply_err (req, ENOTDIR);
+ return;
default:
break;
}
- return ENOENT;
-}
+ if (child_inode == NULL)
+ {
+ g_debug ("xdp_fuse_lookup <- error child ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
-static void
-xdp_fuse_lookup (fuse_req_t req,
- fuse_ino_t parent,
- const char *name)
-{
- struct fuse_entry_param e = {0};
- int res;
+ if (xdp_inode_stat (child_inode, &e.attr) != 0)
+ {
+ fuse_reply_err (req, errno);
+ return;
+ }
- g_debug ("xdp_fuse_lookup %lx/%s -> ", parent, name);
+ e.ino = child_inode->ino;
- memset (&e, 0, sizeof(e));
+ g_debug ("xdp_fuse_lookup <- inode %lx", (long)e.ino);
+ xdp_inode_ref (child_inode); /* Ref given to the kernel, returned in xdp_fuse_forget() */
+ fuse_reply_entry (req, &e);
+}
- res = xdp_lookup (parent, name, &e.ino, &e.attr, NULL, NULL);
+void
+xdp_fuse_forget (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)
+{
+ g_autoptr(XdpInode) inode = NULL;
+ g_debug ("xdp_fuse_forget %lx %ld -> ", ino, nlookup);
- if (res == 0)
- {
- g_debug ("xdp_fuse_lookup <- inode %lx", (long)e.ino);
- e.attr_timeout = get_attr_cache_time (e.attr.st_mode);
- e.entry_timeout = get_entry_cache_time (e.ino);
- fuse_reply_entry (req, &e);
- }
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
+ g_warning ("xdp_fuse_forget, unknown inode");
else
{
- g_debug ("xdp_fuse_lookup <- error %s", strerror (res));
- fuse_reply_err (req, res);
+ while (nlookup > 0)
+ {
+ xdp_inode_unref (inode);
+ nlookup--;
+ }
}
+
+ fuse_reply_none (req);
}
struct dirbuf {
@@ -1043,7 +1063,8 @@ static void
dirbuf_add (fuse_req_t req,
struct dirbuf *b,
const char *name,
- fuse_ino_t ino)
+ fuse_ino_t ino,
+ mode_t mode)
{
struct stat stbuf;
@@ -1052,6 +1073,7 @@ dirbuf_add (fuse_req_t req,
b->p = (char *) g_realloc (b->p, b->size);
memset (&stbuf, 0, sizeof (stbuf));
stbuf.st_ino = ino;
+ stbuf.st_mode = mode;
fuse_add_direntry (req, b->p + oldsize,
b->size - oldsize,
name, &stbuf,
@@ -1061,15 +1083,14 @@ dirbuf_add (fuse_req_t req,
static void
dirbuf_add_docs (fuse_req_t req,
struct dirbuf *b,
- guint32 app_id)
+ const char *app_id)
{
- g_autofree guint32 *docs = NULL;
- guint64 inode;
+ g_auto(GStrv) docs = NULL;
+ fuse_ino_t ino;
int i;
- g_autofree char *doc_name = NULL;
docs = xdp_list_docs ();
- for (i = 0; docs[i] != 0; i++)
+ for (i = 0; docs[i] != NULL; i++)
{
if (app_id)
{
@@ -1078,44 +1099,8 @@ dirbuf_add_docs (fuse_req_t req,
!app_can_see_doc (entry, app_id))
continue;
}
- inode = make_app_doc_dir_inode (app_id, docs[i]);
- doc_name = xdp_name_from_id (docs[i]);
- dirbuf_add (req, b, doc_name, inode);
- }
-}
-
-static void
-dirbuf_add_doc_file (fuse_req_t req,
- struct dirbuf *b,
- XdgAppDbEntry *entry,
- guint32 doc_id,
- guint32 app_id)
-{
- struct stat tmp_stbuf;
- guint64 inode;
- g_autofree char *basename = xdp_entry_dup_basename (entry);
-
- inode = make_app_doc_file_inode (app_id, doc_id);
-
- if (xdp_entry_stat (entry, &tmp_stbuf, AT_SYMLINK_NOFOLLOW) == 0)
- dirbuf_add (req, b, basename, inode);
-}
-
-static void
-dirbuf_add_tmp_files (fuse_req_t req,
- struct dirbuf *b,
- guint64 dir_inode)
-{
- GList *l;
-
- AUTOLOCK(tmp_files);
-
- for (l = tmp_files; l != NULL; l = l->next)
- {
- XdpTmp *tmp = l->data;
- if (tmp->parent_inode == dir_inode)
- dirbuf_add (req, b, tmp->name,
- make_inode (TMPFILE_INO_CLASS, tmp->tmp_id));
+ ino = get_dir_inode_nr (app_id, docs[i]);
+ dirbuf_add (req, b, docs[i], ino, S_IFDIR);
}
}
@@ -1147,107 +1132,101 @@ xdp_fuse_opendir (fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
- struct stat stbuf = {0};
+ g_autoptr(XdpInode) inode = NULL;
struct dirbuf b = {0};
- XdpInodeClass class;
- guint64 class_ino;
g_autoptr (XdgAppDbEntry) entry = NULL;
- int res;
g_debug ("xdp_fuse_opendir %lx", ino);
- if ((res = xdp_stat (ino, &stbuf, &entry)) != 0)
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
{
- fuse_reply_err (req, res);
+ g_debug ("xdp_fuse_opendir <- error ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
+ switch (inode->type)
{
- fuse_reply_err (req, ENOTDIR);
- return;
- }
+ case XDP_INODE_ROOT:
+ dirbuf_add (req, &b, ".", ROOT_INODE, S_IFDIR);
+ dirbuf_add (req, &b, "..", ROOT_INODE, S_IFDIR);
+ dirbuf_add (req, &b, BY_APP_NAME, BY_APP_INODE, S_IFDIR);
+ dirbuf_add_docs (req, &b, NULL);
+ break;
- class = get_class (ino);
- class_ino = get_class_ino (ino);
+ case XDP_INODE_BY_APP:
+ {
+ g_auto(GStrv) db_app_ids = NULL;
+ g_auto(GStrv) app_ids = NULL;
+ int i;
+
+ dirbuf_add (req, &b, ".", BY_APP_INODE, S_IFDIR);
+ dirbuf_add (req, &b, "..", ROOT_INODE, S_IFDIR);
+
+ /* Ensure that all apps from db are allocated */
+ db_app_ids = xdp_list_apps ();
+ allocate_app_dir_inode_nr (db_app_ids);
+
+ /* But return all allocated dirs. We might have app dirs
+ that have no permissions, and are thus not in the db */
+ app_ids = get_allocated_app_dirs ();
+ for (i = 0; app_ids[i] != NULL; i++)
+ dirbuf_add (req, &b, app_ids[i],
+ get_dir_inode_nr (app_ids[i], NULL), S_IFDIR);
+ }
+ break;
- switch (class)
- {
- case STD_DIRS_INO_CLASS:
- switch (class_ino)
- {
- case FUSE_ROOT_ID:
- dirbuf_add (req, &b, ".", FUSE_ROOT_ID);
- dirbuf_add (req, &b, "..", FUSE_ROOT_ID);
- dirbuf_add (req, &b, BY_APP_NAME,
- make_inode (STD_DIRS_INO_CLASS, BY_APP_INO));
- dirbuf_add_docs (req, &b, 0);
- break;
+ case XDP_INODE_APP_DIR:
+ dirbuf_add (req, &b, ".", inode->ino, S_IFDIR);
+ dirbuf_add (req, &b, "..", BY_APP_INODE, S_IFDIR);
+ dirbuf_add_docs (req, &b, inode->app_id);
+ break;
- case BY_APP_INO:
- dirbuf_add (req, &b, ".", ino);
- dirbuf_add (req, &b, "..", FUSE_ROOT_ID);
+ case XDP_INODE_DOC_FILE:
+ fuse_reply_err (req, ENOTDIR);
+ break;
- /* Update for any possible new app */
- fill_app_name_hash ();
+ case XDP_INODE_APP_DOC_DIR:
+ case XDP_INODE_DOC_DIR:
+ {
+ GList *children, *l;
+ g_autoptr(XdpInode) doc_inode = NULL;
+ g_autoptr (XdgAppDbEntry) entry = NULL;
+ entry = xdp_lookup_doc (inode->doc_id);
+ if (entry == NULL)
{
- GHashTableIter iter;
- gpointer key, value;
-
- AUTOLOCK(app_id);
-
- g_hash_table_iter_init (&iter, app_name_to_id);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- const char *name = key;
- guint32 id = GPOINTER_TO_UINT(value);
-
- if (strlen (name) > 0)
- dirbuf_add (req, &b, name,
- make_inode (APP_DIR_INO_CLASS, id));
- }
+ fuse_reply_err (req, ENOENT);
+ break;
}
- break;
- default:
- break;
- }
- break;
+ dirbuf_add (req, &b, ".", inode->ino, S_IFDIR);
+ dirbuf_add (req, &b, "..", inode->parent->ino, S_IFDIR);
- case APP_DIR_INO_CLASS:
- {
- dirbuf_add (req, &b, ".", ino);
- dirbuf_add (req, &b, "..", make_inode (STD_DIRS_INO_CLASS, BY_APP_INO));
- dirbuf_add_docs (req, &b, class_ino);
- break;
- }
+ /* Ensure it is alive at least during list_children () */
+ doc_inode = xdp_inode_ensure_document_file (inode, entry);
- break;
+ children = xdp_inode_list_children (inode);
- case APP_DOC_DIR_INO_CLASS:
- dirbuf_add (req, &b, ".", ino);
- if (get_app_id_from_app_doc_ino (class_ino) == 0)
- dirbuf_add (req, &b, "..", FUSE_ROOT_ID);
- else
- dirbuf_add (req, &b, "..", make_inode (APP_DIR_INO_CLASS,
- get_app_id_from_app_doc_ino (class_ino)));
- dirbuf_add_doc_file (req, &b, entry,
- get_doc_id_from_app_doc_ino (class_ino),
- get_app_id_from_app_doc_ino (class_ino));
- dirbuf_add_tmp_files (req, &b, ino);
+ for (l = children; l != NULL; l = l->next)
+ {
+ struct stat stbuf;
+ XdpInode *child = l->data;
+ g_autofree char *filename = xdp_inode_get_filename (child);
+ if (filename != NULL && xdp_inode_stat (child, &stbuf) == 0)
+ dirbuf_add (req, &b, filename, child->ino, stbuf.st_mode);
+ xdp_inode_unref (child);
+ }
+ g_list_free (children);
+ }
break;
- case APP_DOC_FILE_INO_CLASS:
- case TMPFILE_INO_CLASS:
- /* These should have returned ENOTDIR above */
default:
- break;
+ g_assert_not_reached ();
}
- if (b.p == NULL)
- fuse_reply_err (req, EIO);
- else
+ if (b.p != NULL)
{
fi->fh = (gsize)g_memdup (&b, sizeof (b));
if (fuse_reply_open (req, fi) == -ENOENT)
@@ -1269,551 +1248,497 @@ xdp_fuse_releasedir (fuse_req_t req,
fuse_reply_err (req, 0);
}
-static int
-get_open_flags (struct fuse_file_info *fi)
-{
- /* TODO: Maybe limit the flags set more */
- return fi->flags & ~(O_EXCL|O_CREAT);
-}
-static char *
-create_tmp_for_doc (XdgAppDbEntry *entry, int dir_fd, int flags, int *fd_out)
+
+static void
+xdp_fuse_getattr (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info *fi)
{
- g_autofree char *basename = xdp_entry_dup_basename (entry);
- g_autofree char *template = g_strconcat (".xdp_", basename, ".XXXXXX", NULL);
- int fd;
+ g_autoptr(XdpInode) inode = NULL;
+ struct stat stbuf = { 0 };
- fd = xdg_app_mkstempat (dir_fd, template, flags|O_CLOEXEC, 0600);
- if (fd == -1)
- return NULL;
+ g_debug ("xdp_fuse_getattr %lx (fi=%p)", ino, fi);
- *fd_out = fd;
- return g_steal_pointer (&template);
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
+ {
+ g_debug ("xdp_fuse_getattr <- error ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
+
+ if (xdp_inode_stat (inode, &stbuf) != 0)
+ {
+ fuse_reply_err (req, errno);
+ return;
+ }
+
+ fuse_reply_attr (req, &stbuf, ATTR_CACHE_TIME);
}
static void
-xdp_fuse_open (fuse_req_t req,
- fuse_ino_t ino,
- struct fuse_file_info *fi)
+xdp_fuse_fsyncdir (fuse_req_t req,
+ fuse_ino_t ino,
+ int datasync,
+ struct fuse_file_info *fi)
{
- XdpInodeClass class = get_class (ino);
- guint64 class_ino = get_class_ino (ino);
- struct stat stbuf = {0};
- g_autoptr (XdgAppDbEntry) entry = NULL;
- g_autoptr(XdpTmp) tmp = NULL;
- glnx_fd_close int fd = -1;
- int res;
- XdpFh *fh = NULL;
+ g_autoptr(XdpInode) inode = NULL;
- g_debug ("xdp_fuse_open %lx", ino);
+ g_debug ("xdp_fuse_fsyncdir %lx %p", ino, fi);
- if ((res = xdp_stat (ino, &stbuf, &entry)) != 0)
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
{
- fuse_reply_err (req, res);
+ g_debug ("xdp_fuse_fsyncdir <- error ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- if ((stbuf.st_mode & S_IFMT) != S_IFREG)
+ if (inode->type == XDP_INODE_APP_DOC_DIR ||
+ inode->type == XDP_INODE_DOC_DIR)
{
- fuse_reply_err (req, EISDIR);
- return;
+ g_autoptr (XdgAppDbEntry) entry = xdp_lookup_doc (inode->doc_id);
+ if (entry != NULL)
+ {
+ g_autofree char *dirname = xdp_entry_dup_dirname (entry);
+ int fd = open (dirname, O_DIRECTORY|O_RDONLY);
+ if (fd >= 0)
+ {
+ if (datasync)
+ fdatasync (fd);
+ else
+ fsync (fd);
+ close (fd);
+ }
+ }
}
- if (entry && class == APP_DOC_FILE_INO_CLASS)
+ fuse_reply_err (req, 0);
+}
+
+static XdpFile *
+xdp_file_new (XdpInode *inode,
+ int open_mode)
+{
+ XdpFile *file = g_new (XdpFile, 1);
+ file->inode = xdp_inode_ref (inode);
+ file->open_mode = open_mode;
+
+ return file;
+}
+
+/* Call with mutex held */
+static void
+xdp_inode_locked_close_unneeded_fds (XdpInode *inode)
+{
+ gboolean has_open_for_write = FALSE;
+ GList *l;
+
+ for (l = inode->open_files; l != NULL; l = l->next)
{
- g_autofree char *tmp_basename = NULL;
- glnx_fd_close int write_fd = -1;
- glnx_fd_close int dir_fd = -1;
- g_autofree char *basename = xdp_entry_dup_basename (entry);
- guint32 app_id = get_app_id_from_app_doc_ino (class_ino);
- gboolean can_write;
+ XdpFile *file = l->data;
- dir_fd = xdp_entry_open_dir (entry);
- if (dir_fd == -1)
+ if (file->open_mode != O_RDONLY)
{
- fuse_reply_err (req, errno);
- return;
+ has_open_for_write = TRUE;
+ break;
}
+ }
- can_write = app_can_write_doc (entry, app_id);
-
- if ((fi->flags & 3) != O_RDONLY)
+ if (!has_open_for_write)
+ {
+ if (inode->truncated)
{
- if (!can_write)
+ if (inode->open_files != NULL && inode->fd != -1)
{
- fuse_reply_err (req, EACCES);
- return;
+ /* We're not going to close the ->fd, so we repoint it to the trunc_fd, but reopened O_RDONLY */
+ close (inode->fd);
+ inode->fd = reopen_fd (inode->trunc_fd, O_RDONLY);
}
- if (faccessat (dir_fd, basename, W_OK, 0) != 0)
+ if (inode->filename != NULL)
{
- fuse_reply_err (req, errno);
- return;
+ /* not removed, replace original */
+ fsync (inode->trunc_fd);
+ g_free (inode->backing_filename);
+ inode->backing_filename = g_strdup (inode->filename);
+ g_debug ("moving %s to %s", inode->trunc_filename, inode->backing_filename);
+ if (renameat (inode->dir_fd, inode->trunc_filename,
+ inode->dir_fd, inode->backing_filename) != 0)
+ g_warning ("Unable to replace truncated document: %s", strerror (errno));
}
- tmp_basename = create_tmp_for_doc (entry, dir_fd, O_RDWR, &write_fd);
- if (tmp_basename == NULL)
- {
- fuse_reply_err (req, errno);
- return;
- }
+ inode->truncated = FALSE;
+ }
+ else if (inode->trunc_filename != NULL)
+ {
+ unlinkat (inode->dir_fd, inode->trunc_filename, 0);
+ g_debug ("unlinked truc_filename %s", inode->trunc_filename);
}
- fd = openat (dir_fd, basename, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
- if (fd < 0)
+ if (inode->trunc_fd != -1)
{
- fuse_reply_err (req, errno);
- return;
+ close (inode->trunc_fd);
+ inode->trunc_fd = -1;
+ g_free (inode->trunc_filename);
+ inode->trunc_filename = NULL;
}
- fh = xdp_fh_new (ino, fi, steal_fd(&fd), NULL);
- fh->can_write = can_write;
- fh->dir_fd = steal_fd (&dir_fd);
- fh->trunc_fd = steal_fd (&write_fd);
- fh->trunc_basename = g_steal_pointer (&tmp_basename);
- fh->real_basename = g_strdup (basename);
- if (fuse_reply_open (req, fi))
- xdp_fh_unref (fh);
}
- else if (class == TMPFILE_INO_CLASS &&
- (tmp = find_tmp_by_id (class_ino)))
+
+ if (inode->open_files == NULL)
{
- glnx_fd_close int dir_fd = xdp_entry_open_dir (tmp->entry);
- g_autofree char *backing_basename = xdp_tmp_get_backing_basename (tmp);
- if (dir_fd == -1 || backing_basename == NULL)
+ if (inode->fd != -1)
{
- fuse_reply_err (req, ENOENT);
- return;
+ close (inode->fd);
+ inode->fd = -1;
}
- fd = openat (dir_fd, backing_basename, get_open_flags (fi)|O_NOFOLLOW|O_CLOEXEC);
- if (fd < 0)
+ if (inode->dir_fd != -1)
{
- fuse_reply_err (req, errno);
- return;
+ close (inode->dir_fd);
+ inode->dir_fd = -1;
}
- fh = xdp_fh_new (ino, fi, steal_fd (&fd), tmp);
- fh->can_write = TRUE;
- if (fuse_reply_open (req, fi))
- xdp_fh_unref (fh);
}
- else
- fuse_reply_err (req, EIO);
}
static void
-xdp_fuse_create (fuse_req_t req,
- fuse_ino_t parent,
- const char *name,
- mode_t mode,
- struct fuse_file_info *fi)
+xdp_file_free (XdpFile *file)
{
- struct fuse_entry_param e = {0};
- XdpInodeClass parent_class = get_class (parent);
- guint64 parent_class_ino = get_class_ino (parent);
- struct stat stbuf;
- XdpFh *fh;
- g_autoptr(XdgAppDbEntry) entry = NULL;
- g_autofree char *basename = NULL;
- glnx_fd_close int fd = -1;
- gboolean can_write;
- int res;
- guint32 app_id = 0;
- guint32 doc_id;
+ XdpInode *inode = file->inode;
- g_debug ("xdp_fuse_create %lx/%s, flags %o", parent, name, fi->flags);
+ g_mutex_lock (&inode->mutex);
+ inode->open_files = g_list_remove (inode->open_files, file);
- if ((res = xdp_stat (parent, &stbuf, &entry)) != 0)
- {
- fuse_reply_err (req, res);
- return;
- }
+ xdp_inode_locked_close_unneeded_fds (inode);
- if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
- {
- fuse_reply_err (req, ENOTDIR);
- return;
- }
+ g_mutex_unlock (&inode->mutex);
+ xdp_inode_unref (inode);
+ g_free (file);
+}
- if (parent_class != APP_DOC_DIR_INO_CLASS)
+/* sets errno */
+static int
+xdp_inode_locked_ensure_fd_open (XdpInode *inode,
+ XdgAppDbEntry *entry,
+ gboolean for_write)
+{
+ /* Ensure all fds are open */
+ if (inode->dir_fd == -1)
{
- fuse_reply_err (req, EACCES);
- return;
+ inode->dir_fd = xdp_inode_open_dir_fd (inode->parent);
+ if (inode->dir_fd == -1)
+ return -1;
}
- app_id = get_app_id_from_app_doc_ino (parent_class_ino);
- doc_id = get_doc_id_from_app_doc_ino (parent_class_ino);
-
- can_write = app_can_write_doc (entry, app_id);
-
- basename = xdp_entry_dup_basename (entry);
- if (strcmp (name, basename) == 0)
+ if (for_write)
{
- g_autofree char *tmp_basename = NULL;
- glnx_fd_close int write_fd = -1;
- glnx_fd_close int dir_fd = -1;
-
- dir_fd = xdp_entry_open_dir (entry);
- if (dir_fd == -1)
- {
- fuse_reply_err (req, errno);
- return;
- }
-
- if (!can_write)
- {
- fuse_reply_err (req, EACCES);
- return;
- }
-
- tmp_basename = create_tmp_for_doc (entry, dir_fd, O_RDWR, &write_fd);
- if (tmp_basename == NULL)
- {
- fuse_reply_err (req, errno);
- return;
- }
-
- fd = openat (dir_fd, basename, O_CREAT|O_EXCL|O_RDONLY|O_NOFOLLOW|O_CLOEXEC, mode & 0777);
- if (fd < 0)
- {
- fuse_reply_err (req, errno);
- return;
- }
-
- e.ino = make_app_doc_file_inode (app_id, doc_id);
-
- fh = xdp_fh_new (e.ino, fi, steal_fd (&fd), NULL);
- fh->can_write = TRUE;
- fh->dir_fd = steal_fd (&dir_fd);
- fh->truncated = TRUE;
- fh->trunc_fd = steal_fd (&write_fd);
- fh->trunc_basename = g_steal_pointer (&tmp_basename);
- fh->real_basename = g_strdup (basename);
-
- if (xdp_fh_fstat_locked (fh, &e.attr) != 0)
- {
- xdp_fh_unref (fh);
- fuse_reply_err (req, EIO);
- return;
- }
-
- e.attr_timeout = get_attr_cache_time (e.attr.st_mode);
- e.entry_timeout = get_entry_cache_time (e.ino);
-
- if (fuse_reply_create (req, &e, fi))
- xdp_fh_unref (fh);
+ if (faccessat (inode->dir_fd, inode->backing_filename, W_OK, 0) != 0)
+ return -1;
}
- else
- {
- g_autoptr(XdpTmp) tmpfile = NULL;
-
- G_LOCK(tmp_files);
- tmpfile = find_tmp_by_name_nolock (parent, name);
- if (tmpfile != NULL && fi->flags & O_EXCL)
- {
- G_UNLOCK(tmp_files);
- fuse_reply_err (req, EEXIST);
- return;
- }
- if (!can_write)
- {
- fuse_reply_err (req, EACCES);
- return;
- }
-
- if (tmpfile)
- {
- glnx_fd_close int dir_fd = xdp_entry_open_dir (tmpfile->entry);
- g_autofree char *backing_basename = NULL;
-
- G_UNLOCK(tmp_files);
-
- backing_basename = xdp_tmp_get_backing_basename (tmpfile);
- if (dir_fd == -1 || backing_basename == NULL)
- {
- fuse_reply_err (req, EINVAL);
- return;
- }
+ if (inode->fd == -1)
+ {
+ int mode = O_NOFOLLOW|O_CLOEXEC;
- fd = openat (dir_fd, backing_basename, get_open_flags (fi)|O_NOFOLLOW|O_CLOEXEC);
- if (fd == -1)
- {
- fuse_reply_err (req, errno);
- return;
- }
- }
+ if (inode->is_doc)
+ mode |= O_RDONLY;
else
- {
- int errsv;
- g_autofree char *tmp_basename = NULL;
- glnx_fd_close int dir_fd = -1;
+ mode |= O_RDWR;
- dir_fd = xdp_entry_open_dir (entry);
- if (dir_fd == -1)
- {
- fuse_reply_err (req, errno);
- return;
- }
-
- tmp_basename = create_tmp_for_doc (entry, dir_fd, get_open_flags (fi), &fd);
- if (tmp_basename == NULL)
- return;
-
- tmpfile = xdp_tmp_new_nolock (parent, entry, name, tmp_basename);
- errsv = errno;
- G_UNLOCK(tmp_files);
+ inode->fd = openat (inode->dir_fd, inode->backing_filename, mode);
+ if (inode->fd < 0)
+ return -1;
+ }
- if (tmpfile == NULL)
- {
- fuse_reply_err (req, errsv);
- return;
- }
- }
+ if (inode->is_doc && for_write && inode->trunc_fd == -1)
+ {
+ struct stat st_buf;
+ mode_t mode = 0600;
- e.ino = make_inode (TMPFILE_INO_CLASS, tmpfile->tmp_id);
- if (xdp_stat (e.ino, &e.attr, NULL) != 0)
- {
- fuse_reply_err (req, EIO);
- return;
- }
- e.attr_timeout = get_attr_cache_time (e.attr.st_mode);
- e.entry_timeout = get_entry_cache_time (e.ino);
+ if (fstat (inode->fd, &st_buf) == 0)
+ mode = get_user_perms (&st_buf);
- fh = xdp_fh_new (e.ino, fi, steal_fd (&fd), tmpfile);
- fh->can_write = TRUE;
- if (fuse_reply_create (req, &e, fi))
- xdp_fh_unref (fh);
+ g_assert (inode->trunc_filename == NULL);
+ inode->trunc_filename = create_tmp_for_doc (entry, inode->dir_fd, O_RDWR, mode,
+ &inode->trunc_fd);
+ if (inode->trunc_filename == NULL)
+ return -1;
}
+
+ return 0;
}
static void
-xdp_fuse_read (fuse_req_t req,
+xdp_fuse_open (fuse_req_t req,
fuse_ino_t ino,
- size_t size,
- off_t off,
struct fuse_file_info *fi)
{
- XdpFh *fh = (gpointer)fi->fh;
- struct fuse_bufvec bufv = FUSE_BUFVEC_INIT (size);
- static char c = 'x';
- int fd;
+ g_autoptr(XdpInode) inode = NULL;
+ g_autoptr(XdgAppDbEntry) entry = NULL;
+ gboolean can_write;
+ int open_mode;
+ XdpFile *file = NULL;
+ int errsv;
- XDP_FH_AUTOLOCK (fh);
+ g_debug ("xdp_fuse_open %lx flags %o", ino, fi->flags);
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd == -1)
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
{
- bufv.buf[0].flags = 0;
- bufv.buf[0].mem = &c;
- bufv.buf[0].size = 0;
-
- fuse_reply_data (req, &bufv, FUSE_BUF_NO_SPLICE);
+ g_debug ("xdp_fuse_open <- no inode error ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- bufv.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
- bufv.buf[0].fd = fd;
- bufv.buf[0].pos = off;
-
- fuse_reply_data (req, &bufv, FUSE_BUF_SPLICE_MOVE);
-}
-
-static void
-xdp_fuse_write (fuse_req_t req,
- fuse_ino_t ino,
- const char *buf,
- size_t size,
- off_t off,
- struct fuse_file_info *fi)
-{
- XdpFh *fh = (gpointer)fi->fh;
- gssize res;
- int fd;
-
- XDP_FH_AUTOLOCK (fh);
-
- if (fh->readonly)
+ if (inode->type != XDP_INODE_DOC_FILE)
{
- fuse_reply_err (req, EACCES);
+ g_debug ("xdp_fuse_open <- error EISDIR");
+ fuse_reply_err (req, EISDIR);
return;
}
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd == -1)
+ entry = xdp_lookup_doc (inode->doc_id);
+ if (entry == NULL ||
+ !app_can_see_doc (entry, inode->app_id))
{
- fuse_reply_err (req, EIO);
+ g_debug ("xdp_fuse_open <- no entry error ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- res = pwrite (fd, buf, size, off);
- if (res < 0)
- fuse_reply_err (req, errno);
- else
- fuse_reply_write (req, res);
-}
-
-static void
-xdp_fuse_write_buf (fuse_req_t req,
- fuse_ino_t ino,
- struct fuse_bufvec *bufv,
- off_t off,
- struct fuse_file_info *fi)
-{
- XdpFh *fh = (gpointer)fi->fh;
- struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(bufv));
- gssize res;
- int fd;
+ can_write = app_can_write_doc (entry, inode->app_id);
- XDP_FH_AUTOLOCK (fh);
+ open_mode = fi->flags & 3;
- if (fh->readonly)
+ if (open_mode != O_RDONLY && !can_write)
{
+ g_debug ("xdp_fuse_open <- no write EACCES");
fuse_reply_err (req, EACCES);
return;
}
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd == -1)
+ g_mutex_lock (&inode->mutex);
+
+ if (xdp_inode_locked_ensure_fd_open (inode, entry,
+ open_mode != O_RDONLY) == 0)
{
- fuse_reply_err (req, EIO);
- return;
+ file = xdp_file_new (inode, open_mode);
+ inode->open_files = g_list_prepend (inode->open_files, file);
+ errsv = 0;
+ }
+ else
+ {
+ errsv = errno;
+ xdp_inode_locked_close_unneeded_fds (inode);
}
- dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
- dst.buf[0].fd = fd;
- dst.buf[0].pos = off;
+ g_mutex_unlock (&inode->mutex);
- res = fuse_buf_copy (&dst, bufv, FUSE_BUF_SPLICE_NONBLOCK);
- if (res < 0)
- fuse_reply_err (req, -res);
+ if (file != NULL)
+ {
+ fi->fh = (gsize)file;
+ if (fuse_reply_open (req, fi))
+ xdp_file_free (file);
+ }
else
- fuse_reply_write (req, res);
+ fuse_reply_err (req, errsv);
}
-static void
-xdp_fuse_release (fuse_req_t req,
- fuse_ino_t ino,
- struct fuse_file_info *fi)
-{
- XdpFh *fh = (gpointer)fi->fh;
-
- g_debug ("xdp_fuse_release %lx (fi=%p, refcount: %d)", ino, fi, fh->ref_count);
-
- xdp_fh_unref (fh);
- fuse_reply_err (req, 0);
-}
static void
-xdp_fuse_rename (fuse_req_t req,
+xdp_fuse_create (fuse_req_t req,
fuse_ino_t parent,
- const char *name,
- fuse_ino_t newparent,
- const char *newname)
+ const char *filename,
+ mode_t mode,
+ struct fuse_file_info *fi)
{
- XdpInodeClass parent_class = get_class (parent);
- guint64 parent_class_ino = get_class_ino (parent);
+ g_autoptr(XdpInode) parent_inode = NULL;
g_autoptr (XdgAppDbEntry) entry = NULL;
- int res;
- fuse_ino_t inode;
- struct stat stbuf = {0};
- g_autofree char *basename = NULL;
- g_autoptr(XdpTmp) tmp = NULL;
- g_autoptr(XdpTmp) other_tmp = NULL;
- guint32 app_id = 0;
- guint32 doc_id = 0;
- gboolean can_write;
+ struct fuse_entry_param e = {0};
+ gboolean can_see, can_write;
+ g_autofree char *tmpfile = NULL;
+ int open_mode;
+ XdpFile *file = NULL;
+ XdpInode *inode;
+ int errsv;
- g_debug ("xdp_fuse_rename %lx/%s -> %lx/%s", parent, name, newparent, newname);
+ g_debug ("xdp_fuse_create %lx/%s, flags %o", parent, filename, fi->flags);
- res = xdp_lookup (parent, name, &inode, &stbuf, &entry, &tmp);
- if (res != 0)
+ parent_inode = xdp_inode_lookup (parent);
+ if (parent_inode == NULL)
{
- fuse_reply_err (req, res);
+ g_debug ("xdp_fuse_create <- error parent ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- if (parent_class != APP_DOC_DIR_INO_CLASS)
+ if (parent_inode->type == XDP_INODE_DOC_FILE)
+ {
+ g_debug ("xdp_fuse_create <- error parent ENOTDIR");
+ fuse_reply_err (req, ENOTDIR);
+ return;
+ }
+
+ if (parent_inode->type != XDP_INODE_APP_DOC_DIR &&
+ parent_inode->type != XDP_INODE_DOC_DIR)
{
- /* Only allow renames in (app) doc dirs */
fuse_reply_err (req, EACCES);
return;
}
- app_id = get_app_id_from_app_doc_ino (parent_class_ino);
- doc_id = get_doc_id_from_app_doc_ino (parent_class_ino);
+ entry = xdp_lookup_doc (parent_inode->doc_id);
+ if (entry == NULL)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- can_write = app_can_write_doc (entry, app_id);
+ can_see = app_can_see_doc (entry, parent_inode->app_id);
+ if (!can_see)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- /* Only allow renames inside the same dir */
- if (!can_write ||
- parent != newparent ||
- entry == NULL ||
- /* Also, don't allow renaming non-tmpfiles */
- tmp == NULL)
+ can_write = app_can_write_doc (entry, parent_inode->app_id);
+ if (!can_write)
{
fuse_reply_err (req, EACCES);
return;
}
- basename = xdp_entry_dup_basename (entry);
+ inode = xdp_inode_create_file (parent_inode, entry, filename,
+ mode,
+ (fi->flags | O_TRUNC) != 0,
+ (fi->flags | O_EXCL) != 0);
+ if (inode == NULL)
+ {
+ fuse_reply_err (req, errno);
+ return;
+ }
+
+ g_mutex_lock (&inode->mutex);
- if (strcmp (newname, basename) == 0)
+ open_mode = fi->flags & 3;
+
+ if (xdp_inode_locked_ensure_fd_open (inode, entry,
+ open_mode != O_RDONLY) == 0)
{
- glnx_fd_close int dir_fd = -1;
- g_autofree char *backing_basename = NULL;
- /* Rename tmpfile to regular file */
+ file = xdp_file_new (inode, open_mode);
+ inode->open_files = g_list_prepend (inode->open_files, file);
+ errsv = 0;
+ }
+ else
+ {
+ errsv = errno;
+ xdp_inode_locked_close_unneeded_fds (inode);
+ }
- dir_fd = xdp_entry_open_dir (entry);
- if (dir_fd == -1)
+ g_mutex_unlock (&inode->mutex);
+
+ if (file != NULL)
+ {
+ if (xdp_inode_stat (inode, &e.attr) != 0)
{
+ xdp_file_free (file);
fuse_reply_err (req, errno);
return;
}
- /* Steal backing path so we don't delete it when unlinking tmp */
- backing_basename = xdp_tmp_steal_backing_basename (tmp);
- if (backing_basename == NULL)
+ e.ino = inode->ino;
+ if (inode->is_doc)
{
- fuse_reply_err (req, EINVAL);
- return;
+ e.attr_timeout = 0;
+ e.entry_timeout = 0;
+ }
+ else
+ {
+ e.attr_timeout = ATTR_CACHE_TIME;
+ e.entry_timeout = ENTRY_CACHE_TIME;
}
- /* Stop writes to all outstanding fds to the temp file */
- mark_open_tmp_file_readonly (tmp->tmp_id);
+ xdp_inode_ref (inode); /* Ref given to the kernel, returned in xdp_fuse_forget() */
- if (renameat (dir_fd, backing_basename,
- dir_fd, basename) != 0)
+ fi->fh = (gsize)file;
+ if (fuse_reply_create (req, &e, fi))
{
- fuse_reply_err (req, errno);
- return;
+ xdp_file_free (file);
+ xdp_inode_unref (inode);
}
+ }
+ else
+ fuse_reply_err (req, errsv);
+}
- AUTOLOCK(tmp_files);
+static void
+xdp_fuse_read (fuse_req_t req,
+ fuse_ino_t ino,
+ size_t size,
+ off_t off,
+ struct fuse_file_info *fi)
+{
+ XdpFile *file = (gpointer)fi->fh;
+ XdpInode *inode = file->inode;
+ struct fuse_bufvec bufv = FUSE_BUFVEC_INIT (size);
+ int fd;
- xdp_tmp_unlink_nolock (tmp);
+ g_debug ("xdp_fuse_real %lx %ld %ld", ino, (long)size, (long)off);
- fuse_reply_err (req, 0);
+ g_mutex_lock (&inode->mutex);
- /* We actually turn the old inode to a different one after the rename, so
- we need to invalidate the target entry */
+ fd = xdp_inode_locked_get_fd (inode);
+ if (fd == -1)
+ {
+ static char c = 'x';
+ bufv.buf[0].flags = 0;
+ bufv.buf[0].mem = &c;
+ bufv.buf[0].size = 0;
- fuse_lowlevel_notify_inval_entry (main_ch, make_app_doc_dir_inode (app_id, doc_id),
- basename, strlen (basename));
+ fuse_reply_data (req, &bufv, FUSE_BUF_NO_SPLICE);
}
else
{
- /* Rename tmpfile to other tmpfile name */
+ bufv.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ bufv.buf[0].fd = fd;
+ bufv.buf[0].pos = off;
- AUTOLOCK(tmp_files);
+ fuse_reply_data (req, &bufv, FUSE_BUF_SPLICE_MOVE);
+ }
- other_tmp = find_tmp_by_name_nolock (newparent, newname);
- if (other_tmp)
- xdp_tmp_unlink_nolock (other_tmp);
+ g_mutex_unlock (&inode->mutex);
+}
- g_free (tmp->name);
- tmp->name = g_strdup (newname);
- fuse_reply_err (req, 0);
- }
+static void
+xdp_fuse_release (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ XdpFile *file = (gpointer)fi->fh;
+
+ g_debug ("xdp_fuse_release %lx (fi=%p)", ino, fi);
+
+ xdp_file_free (file);
+ fuse_reply_err (req, 0);
+}
+
+static int
+truncateat (int dir_fd, const char *filename, int size)
+{
+ int fd;
+ int errsv, res;
+
+ fd = openat (dir_fd, filename, O_RDWR);
+ if (fd != -1)
+ return -1;
+
+ res = ftruncate (fd, size);
+ errsv = errno;
+
+ close (fd);
+
+ errno = errsv;
+ return res;
}
static void
@@ -1823,150 +1748,182 @@ xdp_fuse_setattr (fuse_req_t req,
int to_set,
struct fuse_file_info *fi)
{
+ g_autoptr(XdpInode) inode = NULL;
+ g_autoptr(XdgAppDbEntry) entry = NULL;
+ double attr_cache_time = ATTR_CACHE_TIME;
+ struct stat newattr = {0};
+ gboolean can_write;
+ int res = 0;
+
g_debug ("xdp_fuse_setattr %lx %x %p", ino, to_set, fi);
- if (to_set == FUSE_SET_ATTR_SIZE && fi != NULL)
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
{
- XdpFh *fh = (gpointer)fi->fh;
- int res;
- struct stat newattr = {0};
+ g_debug ("xdp_fuse_setattr <- error ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- /* ftruncate */
+ if (inode->type != XDP_INODE_DOC_FILE)
+ {
+ g_debug ("xdp_fuse_setattr <- not file ENOSYS");
+ fuse_reply_err (req, ENOSYS);
+ return;
+ }
- if (!fh->can_write)
- {
- fuse_reply_err (req, EACCES);
- return;
- }
+ entry = xdp_lookup_doc (inode->doc_id);
+ if (entry == NULL ||
+ !app_can_see_doc (entry, inode->app_id))
+ {
+ g_debug ("xdp_fuse_setattr <- no entry error ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- res = xdp_fh_truncate_locked (fh, attr->st_size, &newattr);
- if (res < 0)
- {
- fuse_reply_err (req, res);
- return;
- }
+ can_write = app_can_write_doc (entry, inode->app_id);
- fuse_reply_attr (req, &newattr, get_attr_cache_time (newattr.st_mode));
- }
- else if (to_set == FUSE_SET_ATTR_SIZE && fi == NULL)
+ if (to_set == FUSE_SET_ATTR_SIZE)
{
- int res = 0;
- struct stat newattr = {0};
- struct stat *newattrp = &newattr;
- g_autoptr(XdpFh) fh = NULL;
+ g_mutex_lock (&inode->mutex);
- /* truncate, truncate any open files (but EACCES if not open) */
-
- fh = find_open_fh (ino);
- if (fh)
+ if (!can_write)
+ res = EACCES;
+ else if (inode->is_doc)
{
- if (!fh->can_write)
+ /* Only allow ftruncate with the file open for write. We could
+ * allow a truncate, but it would have to be implemented as
+ * an atomic-replace-with-empty-file to not affect other apps
+ * having the file open.
+ * Also, only support truncate-to-zero on first truncation, to
+ * avoid having to copy lots of data from the old file to the
+ * trunc_fd.
+ */
+ if (inode->trunc_fd == -1)
+ res = EACCES;
+ else if (!inode->truncated && attr->st_size != 0)
+ res = ENOSYS;
+ else
{
- fuse_reply_err (req, EACCES);
- return;
+ if (ftruncate (inode->trunc_fd, attr->st_size) != 0)
+ res = errno;
+ else if (!inode->truncated)
+ {
+ inode->truncated = TRUE;
+ g_free (inode->backing_filename);
+ inode->backing_filename = g_strdup (inode->trunc_filename);
+ }
}
- res = xdp_fh_truncate_locked (fh, attr->st_size, newattrp);
- newattrp = NULL;
}
else
{
- fuse_reply_err (req, EACCES);
- return;
- }
-
- if (res < 0)
- {
- fuse_reply_err (req, -res);
- return;
+ if (inode->fd)
+ {
+ if (ftruncate (inode->fd, attr->st_size) != 0)
+ res = errno;
+ }
+ else
+ {
+ glnx_fd_close int dir_fd = xdp_inode_open_dir_fd (inode->parent);
+ if (dir_fd == -1 ||
+ truncateat (dir_fd, inode->backing_filename, attr->st_size) != 0)
+ res = errno;
+ }
}
-
- fuse_reply_attr (req, &newattr, get_attr_cache_time (newattr.st_mode));
+ g_mutex_unlock (&inode->mutex);
}
else if (to_set == FUSE_SET_ATTR_MODE)
{
- gboolean found = FALSE;
- int res, err = -1;
- struct stat newattr = {0};
- XdpFh *fh;
-
- fh = find_open_fh (ino);
- if (fh)
+ if (!can_write)
+ res = EACCES;
+ else
{
- int fd, errsv;
-
- if (!fh->can_write)
- {
- fuse_reply_err (req, EACCES);
- return;
- }
-
- XDP_FH_AUTOLOCK (fh);
-
- fd = xdp_fh_get_fd_nolock (fh);
- if (fd != -1)
- {
- res = fchmod (fd, get_user_perms (attr));
- errsv = errno;
+ int fd = xdp_inode_locked_get_write_fd (inode);
+ if (fd == -1 ||
+ fchmod (fd, get_user_perms (attr)) != 0)
+ res = errno;
+ }
+ }
+ else
+ res = ENOSYS;
- found = TRUE;
+ if (res != 0)
+ fuse_reply_err (req, ENOSYS);
+ else
+ {
+ if (xdp_inode_stat (inode, &newattr) != 0)
+ fuse_reply_err (req, errno);
+ else
+ fuse_reply_attr (req, &newattr, attr_cache_time);
+ }
+}
- if (res != 0)
- err = -errsv;
- else
- err = xdp_fh_fstat (fh, &newattr);
- }
- }
+static void
+xdp_fuse_write (fuse_req_t req,
+ fuse_ino_t ino,
+ const char *buf,
+ size_t size,
+ off_t off,
+ struct fuse_file_info *fi)
+{
+ XdpFile *file = (gpointer)fi->fh;
+ XdpInode *inode = file->inode;
+ int fd;
+ int res;
- if (!found)
- {
- fuse_reply_err (req, EACCES);
- return;
- }
+ g_debug ("xdp_fuse_write %lx %ld %ld", ino, (long)size, (long)off);
- if (err < 0)
- {
- fuse_reply_err (req, -err);
- return;
- }
+ g_mutex_lock (&inode->mutex);
- fuse_reply_attr (req, &newattr, get_attr_cache_time (newattr.st_mode));
- }
+ fd = xdp_inode_locked_get_write_fd (inode);
+ if (fd < 0)
+ fuse_reply_err (req, errno);
else
- fuse_reply_err (req, ENOSYS);
+ {
+ res = pwrite (fd, buf, size, off);
+ if (res < 0)
+ fuse_reply_err (req, errno);
+ else
+ fuse_reply_write (req, res);
+ }
+
+ g_mutex_unlock (&inode->mutex);
}
static void
-xdp_fuse_fsyncdir (fuse_req_t req,
- fuse_ino_t ino,
- int datasync,
- struct fuse_file_info *fi)
+xdp_fuse_write_buf (fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_bufvec *bufv,
+ off_t off,
+ struct fuse_file_info *fi)
{
- XdpInodeClass class = get_class (ino);
- guint64 class_ino = get_class_ino (ino);
- guint32 doc_id;
+ XdpFile *file = (gpointer)fi->fh;
+ struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(bufv));
+ XdpInode *inode = file->inode;
+ int fd;
+ int res;
+
+ g_debug ("xdp_fuse_write_buf %lx %ld", ino, (long)off);
- if (class == APP_DOC_DIR_INO_CLASS)
+ g_mutex_lock (&inode->mutex);
+
+ fd = xdp_inode_locked_get_write_fd (inode);
+ if (fd == -1)
+ fuse_reply_err (req, errno);
+ else
{
- g_autoptr (XdgAppDbEntry) entry = NULL;
- doc_id = get_doc_id_from_app_doc_ino (class_ino);
+ dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ dst.buf[0].fd = fd;
+ dst.buf[0].pos = off;
- entry = xdp_lookup_doc (doc_id);
- if (entry != NULL)
- {
- g_autofree char *dirname = xdp_entry_dup_dirname (entry);
- int fd = open (dirname, O_DIRECTORY|O_RDONLY);
- if (fd >= 0)
- {
- if (datasync)
- fdatasync (fd);
- else
- fsync (fd);
- close (fd);
- }
- }
+ res = fuse_buf_copy (&dst, bufv, FUSE_BUF_SPLICE_NONBLOCK);
+ if (res < 0)
+ fuse_reply_err (req, -res);
+ else
+ fuse_reply_write (req, res);
}
- fuse_reply_err (req, 0);
+ g_mutex_unlock (&inode->mutex);
}
static void
@@ -1975,176 +1932,226 @@ xdp_fuse_fsync (fuse_req_t req,
int datasync,
struct fuse_file_info *fi)
{
- XdpInodeClass class = get_class (ino);
+ g_autoptr(XdpInode) inode = NULL;
+ int fd;
+ int res = 0;
+
+ g_debug ("xdp_fuse_fsync %lx", ino);
+
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
+ {
+ g_debug ("xdp_fuse_setattr <- error ENOENT");
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- if (class == APP_DOC_FILE_INO_CLASS ||
- class == TMPFILE_INO_CLASS)
+ if (inode->type == XDP_INODE_DOC_FILE)
{
- XdpFh *fh = (gpointer)fi->fh;
+ g_mutex_lock (&inode->mutex);
- XDP_FH_AUTOLOCK (fh);
+ fd = xdp_inode_locked_get_write_fd (inode);
+ if (fd != -1 && fsync (fd) != 0)
+ res = errno;
- if (fh->fd >= 0)
- fsync (fh->fd);
- if (fh->truncated && fh->trunc_fd >= 0)
- fsync (fh->trunc_fd);
+ g_mutex_unlock (&inode->mutex);
}
- fuse_reply_err (req, 0);
+ fuse_reply_err (req, res);
}
static void
xdp_fuse_unlink (fuse_req_t req,
fuse_ino_t parent,
- const char *name)
+ const char *filename)
{
- XdpInodeClass parent_class = get_class (parent);
- guint64 parent_class_ino = get_class_ino (parent);
+ g_autoptr(XdpInode) parent_inode = NULL;
g_autoptr (XdgAppDbEntry) entry = NULL;
- int res;
- fuse_ino_t inode;
- struct stat stbuf = {0};
- g_autofree char *basename = NULL;
- g_autoptr (XdpTmp) tmp = NULL;
- guint32 app_id = 0;
- gboolean can_write;
+ g_autoptr(XdpInode) child_inode = NULL;
- g_debug ("xdp_fuse_unlink %lx/%s", parent, name);
+ g_debug ("xdp_fuse_unlink %lx/%s", parent, filename);
- res = xdp_lookup (parent, name, &inode, &stbuf, &entry, &tmp);
- if (res != 0)
+ parent_inode = xdp_inode_lookup (parent);
+ if (parent_inode == NULL)
{
- fuse_reply_err (req, res);
+ g_debug ("xdp_fuse_lookup <- error parent ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- if (entry == NULL)
+ if (parent_inode->type == XDP_INODE_DOC_FILE)
{
- fuse_reply_err (req, EACCES);
+ fuse_reply_err (req, ENOTDIR);
return;
}
- if (parent_class != APP_DOC_DIR_INO_CLASS)
+ if (parent_inode->type != XDP_INODE_APP_DOC_DIR &&
+ parent_inode->type != XDP_INODE_DOC_DIR)
{
- /* Only allow unlink in (app) doc dirs */
fuse_reply_err (req, EACCES);
return;
}
- app_id = get_app_id_from_app_doc_ino (parent_class_ino);
+ child_inode = xdp_inode_unlink_child (parent_inode, filename);
+ if (child_inode == NULL)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- can_write = app_can_write_doc (entry, app_id);
- if (!can_write)
+ fuse_reply_err (req, 0);
+}
+
+static void
+xdp_fuse_rename (fuse_req_t req,
+ fuse_ino_t parent,
+ const char *name,
+ fuse_ino_t newparent,
+ const char *newname)
+{
+ g_autoptr(XdpInode) parent_inode = NULL;
+ g_autoptr (XdgAppDbEntry) entry = NULL;
+ g_autofree char *basename = NULL;
+ gboolean can_see, can_write;
+
+ g_debug ("xdp_fuse_rename %lx/%s -> %lx/%s", parent, name, newparent, newname);
+
+ parent_inode = xdp_inode_lookup (parent);
+ if (parent_inode == NULL)
{
- fuse_reply_err (req, EACCES);
+ g_debug ("xdp_fuse_rename <- error parent ENOENT");
+ fuse_reply_err (req, ENOENT);
return;
}
- basename = xdp_entry_dup_basename (entry);
- if (strcmp (name, basename) == 0)
+ if (parent_inode->type == XDP_INODE_DOC_FILE)
{
- glnx_fd_close int dir_fd = -1;
+ fuse_reply_err (req, ENOTDIR);
+ return;
+ }
- dir_fd = xdp_entry_open_dir (entry);
- if (dir_fd == -1)
- {
- fuse_reply_err (req, errno);
- return;
- }
+ if (parent_inode->type != XDP_INODE_APP_DOC_DIR &&
+ parent_inode->type != XDP_INODE_DOC_DIR)
+ {
+ fuse_reply_err (req, EACCES);
+ return;
+ }
- if (unlinkat (dir_fd, basename, 0) != 0)
- {
- fuse_reply_err (req, errno);
- return;
- }
+ if (newparent != parent)
+ {
+ g_debug ("xdp_fuse_rename <- error different parents EACCES");
+ fuse_reply_err (req, EACCES);
+ return;
+ }
+ if (strcmp (name, newname) == 0)
+ {
fuse_reply_err (req, 0);
+ return;
}
- else
+
+ entry = xdp_lookup_doc (parent_inode->doc_id);
+ if (entry == NULL)
{
- AUTOLOCK(tmp_files);
- xdp_tmp_unlink_nolock (tmp);
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
- fuse_reply_err (req, 0);
- }
-}
+ can_see = app_can_see_doc (entry, parent_inode->app_id);
+ can_write = app_can_write_doc (entry, parent_inode->app_id);
+ if (!can_see)
+ {
+ fuse_reply_err (req, ENOENT);
+ return;
+ }
+
+ if (!can_write)
+ {
+ fuse_reply_err (req, EACCES);
+ return;
+ }
+
+ basename = xdp_entry_dup_basename (entry);
+
+ if (xdp_inode_rename_child (parent_inode, name, newname, basename) != 0)
+ fuse_reply_err (req, errno);
+ else
+ fuse_reply_err (req, 0);
+}
static struct fuse_lowlevel_ops xdp_fuse_oper = {
.lookup = xdp_fuse_lookup,
+ .forget = xdp_fuse_forget,
.getattr = xdp_fuse_getattr,
.opendir = xdp_fuse_opendir,
.readdir = xdp_fuse_readdir,
.releasedir = xdp_fuse_releasedir,
.fsyncdir = xdp_fuse_fsyncdir,
.open = xdp_fuse_open,
- .create = xdp_fuse_create,
.read = xdp_fuse_read,
- .write = xdp_fuse_write,
- .write_buf = xdp_fuse_write_buf,
.release = xdp_fuse_release,
- .rename = xdp_fuse_rename,
.setattr = xdp_fuse_setattr,
+ .write = xdp_fuse_write,
+ .write_buf = xdp_fuse_write_buf,
.fsync = xdp_fuse_fsync,
+ .create = xdp_fuse_create,
.unlink = xdp_fuse_unlink,
+ .rename = xdp_fuse_rename,
};
-/* Called when a apps permissions to see a document is changed */
+/* Called when a apps permissions to see a document is changed,
+ and with null opt_app_id when the doc is created/removed */
void
-xdp_fuse_invalidate_doc_app (const char *doc_id_s,
- const char *app_id_s,
+xdp_fuse_invalidate_doc_app (const char *doc_id,
+ const char *opt_app_id,
XdgAppDbEntry *entry)
{
- guint32 app_id = get_app_id_from_name (app_id_s);
- guint32 doc_id = xdp_id_from_name (doc_id_s);
- g_autofree char *basename = xdp_entry_dup_basename (entry);
-
- g_debug ("invalidate %s/%s\n", doc_id_s, app_id_s);
+ g_autoptr(XdpInode) inode = NULL;
+ fuse_ino_t ino;
+ GList *l;
/* This can happen if fuse is not initialized yet for the very
first dbus message that activated the service */
if (main_ch == NULL)
return;
- fuse_lowlevel_notify_inval_inode (main_ch, make_app_doc_file_inode (app_id, doc_id), 0, 0);
- fuse_lowlevel_notify_inval_entry (main_ch, make_app_doc_dir_inode (app_id, doc_id),
- basename, strlen (basename));
- fuse_lowlevel_notify_inval_inode (main_ch, make_app_doc_dir_inode (app_id, doc_id), 0, 0);
- fuse_lowlevel_notify_inval_entry (main_ch, make_inode (APP_DIR_INO_CLASS, app_id),
- doc_id_s, strlen (doc_id_s));
-}
-
-/* Called when a document id is created/removed */
-void
-xdp_fuse_invalidate_doc (const char *doc_id_s,
- XdgAppDbEntry *entry)
-{
- guint32 doc_id = xdp_id_from_name (doc_id_s);
- g_autofree char *basename = xdp_entry_dup_basename (entry);
+ g_debug ("invalidate %s/%s", doc_id, opt_app_id ? opt_app_id : "*");
- g_debug ("invalidate %s\n", doc_id_s);
+ AUTOLOCK(inodes);
+ ino = get_dir_inode_nr_unlocked (opt_app_id, doc_id);
+ inode = xdp_inode_lookup_unlocked (ino);
+ if (inode != NULL)
+ {
+ fuse_lowlevel_notify_inval_inode (main_ch, inode->ino, 0, 0);
+ fuse_lowlevel_notify_inval_entry (main_ch, inode->parent->ino,
+ inode->filename, strlen (inode->filename));
- /* This can happen if fuse is not initialized yet for the very
- first dbus message that activated the service */
- if (main_ch == NULL)
- return;
+ for (l = inode->children; l != NULL; l = l->next)
+ {
+ XdpInode *child = l->data;
- fuse_lowlevel_notify_inval_inode (main_ch, make_app_doc_file_inode (0, doc_id), 0, 0);
- fuse_lowlevel_notify_inval_entry (main_ch, make_app_doc_dir_inode (0, doc_id),
- basename, strlen (basename));
- fuse_lowlevel_notify_inval_inode (main_ch, make_app_doc_dir_inode (0, doc_id), 0, 0);
- fuse_lowlevel_notify_inval_entry (main_ch, FUSE_ROOT_ID, doc_id_s, strlen (doc_id_s));
+ fuse_lowlevel_notify_inval_inode (main_ch, child->ino, 0, 0);
+ if (child->filename != NULL)
+ fuse_lowlevel_notify_inval_entry (main_ch, inode->ino,
+ child->filename, strlen (child->filename));
+ }
+ }
}
-guint32
-xdp_fuse_lookup_id_for_inode (ino_t inode)
+char *
+xdp_fuse_lookup_id_for_inode (ino_t ino)
{
- XdpInodeClass class = get_class (inode);
- guint64 class_ino = get_class_ino (inode);
+ g_autoptr(XdpInode) inode = NULL;
- if (class != APP_DOC_FILE_INO_CLASS)
- return 0;
+ inode = xdp_inode_lookup (ino);
+ if (inode == NULL)
+ return NULL;
- return get_doc_id_from_app_doc_ino (class_ino);
+ if (inode->type != XDP_INODE_DOC_FILE ||
+ !inode->is_doc)
+ return NULL;
+
+ return g_strdup (inode->doc_id);
}
const char *
@@ -2189,10 +2196,12 @@ xdp_fuse_init (GError **error)
struct stat st;
const char *mount_path;
- app_name_to_id =
- g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- app_id_to_name =
+ inodes =
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+ root_inode = xdp_inode_new (ROOT_INODE, XDP_INODE_ROOT, NULL, "/", NULL, NULL);
+ by_app_inode = xdp_inode_new (BY_APP_INODE, XDP_INODE_BY_APP, root_inode, BY_APP_NAME, NULL, NULL);
+ dir_to_inode_nr =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
mount_path = xdp_fuse_get_mountpoint ();
@@ -2207,7 +2216,7 @@ xdp_fuse_init (GError **error)
if (g_mkdir_with_parents (mount_path, 0700))
{
g_set_error (error, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_FAILED,
- "Unable to create dir %s\n", mount_path);
+ "Unable to create dir %s", mount_path);
return FALSE;
}
diff --git a/document-portal/xdp-fuse.h b/document-portal/xdp-fuse.h
index b1e6c82..887db80 100644
--- a/document-portal/xdp-fuse.h
+++ b/document-portal/xdp-fuse.h
@@ -7,18 +7,16 @@
G_BEGIN_DECLS
char ** xdp_list_apps (void);
-guint32 * xdp_list_docs (void);
-XdgAppDbEntry *xdp_lookup_doc (guint32 id);
+char ** xdp_list_docs (void);
+XdgAppDbEntry *xdp_lookup_doc (const char *doc_id);
gboolean xdp_fuse_init (GError **error);
void xdp_fuse_exit (void);
const char *xdp_fuse_get_mountpoint (void);
void xdp_fuse_invalidate_doc_app (const char *doc_id,
- const char *app_id,
+ const char *opt_app_id,
XdgAppDbEntry *entry);
-void xdp_fuse_invalidate_doc (const char *doc_id,
- XdgAppDbEntry *entry);
-guint32 xdp_fuse_lookup_id_for_inode (ino_t inode);
+char *xdp_fuse_lookup_id_for_inode (ino_t inode);
G_END_DECLS
diff --git a/document-portal/xdp-main.c b/document-portal/xdp-main.c
index 5138c4c..611526f 100644
--- a/document-portal/xdp-main.c
+++ b/document-portal/xdp-main.c
@@ -51,37 +51,16 @@ xdp_list_apps (void)
return xdg_app_db_list_apps (db);
}
-guint32 *
+char **
xdp_list_docs (void)
{
- GArray *res;
- g_auto(GStrv) ids = NULL;
- guint32 id;
- int i;
-
AUTOLOCK(db);
-
- res = g_array_new (TRUE, FALSE, sizeof (guint32));
-
- ids = xdg_app_db_list_ids (db);
-
- for (i = 0; ids[i] != NULL; i++)
- {
- guint32 id = xdp_id_from_name (ids[i]);
- g_array_append_val (res, id);
- }
-
- id = 0;
- g_array_append_val (res, id);
-
- return (guint32 *)g_array_free (res, FALSE);
+ return xdg_app_db_list_ids (db);
}
XdgAppDbEntry *
-xdp_lookup_doc (guint32 id)
+xdp_lookup_doc (const char *doc_id)
{
- g_autofree char *doc_id = xdp_name_from_id (id);
-
AUTOLOCK(db);
return xdg_app_db_lookup (db, doc_id);
}
@@ -252,7 +231,7 @@ portal_delete (GDBusMethodInvocation *invocation,
old_apps = xdg_app_db_entry_list_apps (entry);
for (i = 0; old_apps[i] != NULL; i++)
xdp_fuse_invalidate_doc_app (id, old_apps[i], entry);
- xdp_fuse_invalidate_doc (id, entry);
+ xdp_fuse_invalidate_doc_app (id, NULL, entry);
if (persist_entry (entry))
xdg_app_permission_store_call_delete (permission_store, TABLE_NAME,
@@ -305,7 +284,7 @@ do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_exis
entry = xdg_app_db_entry_new (data);
xdg_app_db_set_entry (db, id, entry);
- xdp_fuse_invalidate_doc (id, entry);
+ xdp_fuse_invalidate_doc_app (id, NULL, entry);
if (persistent)
xdg_app_permission_store_call_set (permission_store,
@@ -402,12 +381,11 @@ portal_add (GDBusMethodInvocation *invocation,
if (st_buf.st_dev == fuse_dev)
{
/* The passed in fd is on the fuse filesystem itself */
- guint32 old_id;
g_autoptr(XdgAppDbEntry) old_entry = NULL;
- old_id = xdp_fuse_lookup_id_for_inode (st_buf.st_ino);
- g_debug ("path on fuse, id %x\n", old_id);
- if (old_id == 0)
+ id = xdp_fuse_lookup_id_for_inode (st_buf.st_ino);
+ g_debug ("path on fuse, id %s\n", id);
+ if (id == NULL)
{
g_dbus_method_invocation_return_error (invocation,
XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT,
@@ -415,8 +393,6 @@ portal_add (GDBusMethodInvocation *invocation,
return;
}
- id = xdp_name_from_id (old_id);
-
/* If the entry doesn't exist anymore, fail. Also fail if not
resuse_existing, because otherwise the user could use this to
get a copy with permissions and thus escape later permission
diff --git a/document-portal/xdp-util.c b/document-portal/xdp-util.c
index 5370b1e..6725f41 100644
--- a/document-portal/xdp-util.c
+++ b/document-portal/xdp-util.c
@@ -74,12 +74,6 @@ xdp_entry_has_permissions (XdgAppDbEntry *entry,
return (current_perms & perms) == perms;
}
-guint32
-xdp_id_from_name (const char *name)
-{
- return g_ascii_strtoull (name, NULL, 16);
-}
-
char *
xdp_name_from_id (guint32 doc_id)
{
@@ -133,50 +127,3 @@ xdp_entry_get_flags (XdgAppDbEntry *entry)
g_autoptr(GVariant) c = g_variant_get_child_value (v, 3);
return g_variant_get_uint32 (c);
}
-
-int
-xdp_entry_open_dir (XdgAppDbEntry *entry)
-{
- g_autofree char *dirname = xdp_entry_dup_dirname (entry);
- struct stat st_buf;
- int fd;
-
- fd = open (dirname, O_CLOEXEC | O_PATH | O_DIRECTORY);
- if (fd == -1)
- return -1;
-
- if (fstat (fd, &st_buf) < 0)
- {
- close (fd);
- errno = ENOENT;
- return -1;
- }
-
- if (st_buf.st_ino != xdp_entry_get_inode (entry) ||
- st_buf.st_dev != xdp_entry_get_device (entry))
- {
- close (fd);
- errno = ENOENT;
- return -1;
- }
-
- return fd;
-}
-
-int
-xdp_entry_stat (XdgAppDbEntry *entry,
- struct stat *buf,
- int flags)
-{
- glnx_fd_close int fd = -1;
- g_autofree char *basename = xdp_entry_dup_basename (entry);
-
- fd = xdp_entry_open_dir (entry);
- if (fd < 0)
- return -1;
-
- if (fstatat (fd, basename, buf, flags) != 0)
- return -1;
-
- return 0;
-}
diff --git a/document-portal/xdp-util.h b/document-portal/xdp-util.h
index bc1bfc6..77e0bae 100644
--- a/document-portal/xdp-util.h
+++ b/document-portal/xdp-util.h
@@ -24,12 +24,7 @@ char * xdp_entry_dup_dirname (XdgAppDbEntry *entry);
guint64 xdp_entry_get_device (XdgAppDbEntry *entry);
guint64 xdp_entry_get_inode (XdgAppDbEntry *entry);
guint32 xdp_entry_get_flags (XdgAppDbEntry *entry);
-int xdp_entry_open_dir (XdgAppDbEntry *entry);
-int xdp_entry_stat (XdgAppDbEntry *entry,
- struct stat *buf,
- int flags);
-guint32 xdp_id_from_name (const char *name);
char * xdp_name_from_id (guint32 doc_id);