summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);