Add edit mode support for smb backend

Resolves: RHEL-71089
This commit is contained in:
Ondrej Holy 2025-02-13 09:34:55 +01:00
parent a4ea8b83a6
commit 0c0c3e8f18
8 changed files with 550 additions and 1 deletions

View File

@ -0,0 +1,94 @@
From 1d4acb8d95612384f603aa5c0c8d20060010fbc7 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 1 Aug 2024 12:35:43 +0200
Subject: [PATCH] daemon: Add support for edit mode
Currently, it is impossible to open the output stream without truncating
the file or appending it. This is a problem for many POSIX applications
relying on our FUSE daemon. Let's add an edit mode that could be used for
this purpose.
Related: https://gitlab.gnome.org/GNOME/gvfs/-/issues/249
---
daemon/gvfsbackend.h | 8 ++++++++
daemon/gvfsjobopenforwrite.c | 23 +++++++++++++++++++++++
daemon/gvfsjobopenforwrite.h | 3 ++-
3 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h
index 9c7476cf..0f97cff6 100644
--- a/daemon/gvfsbackend.h
+++ b/daemon/gvfsbackend.h
@@ -211,6 +211,14 @@ struct _GVfsBackendClass
const char *etag,
gboolean make_backup,
GFileCreateFlags flags);
+ gboolean (*try_edit) (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags);
+ void (*edit) (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags);
void (*close_write) (GVfsBackend *backend,
GVfsJobCloseWrite *job,
GVfsBackendHandle handle);
diff --git a/daemon/gvfsjobopenforwrite.c b/daemon/gvfsjobopenforwrite.c
index 439b63c4..5b6e8033 100644
--- a/daemon/gvfsjobopenforwrite.c
+++ b/daemon/gvfsjobopenforwrite.c
@@ -220,6 +220,20 @@ run (GVfsJob *job)
op_job->make_backup,
op_job->flags);
}
+ else if (op_job->mode == OPEN_FOR_WRITE_EDIT)
+ {
+ if (class->edit == NULL)
+ {
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported"));
+ return;
+ }
+
+ class->edit (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->flags);
+ }
else
g_assert_not_reached (); /* Handled in try */
}
@@ -259,6 +273,15 @@ try (GVfsJob *job)
op_job->make_backup,
op_job->flags);
}
+ else if (op_job->mode == OPEN_FOR_WRITE_EDIT)
+ {
+ if (class->try_edit == NULL)
+ return FALSE;
+ return class->try_edit (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->flags);
+ }
else
{
GError *error = NULL;
diff --git a/daemon/gvfsjobopenforwrite.h b/daemon/gvfsjobopenforwrite.h
index 141189f3..e070d0ea 100644
--- a/daemon/gvfsjobopenforwrite.h
+++ b/daemon/gvfsjobopenforwrite.h
@@ -41,7 +41,8 @@ typedef struct _GVfsJobOpenForWriteClass GVfsJobOpenForWriteClass;
typedef enum {
OPEN_FOR_WRITE_CREATE = 0,
OPEN_FOR_WRITE_APPEND = 1,
- OPEN_FOR_WRITE_REPLACE = 2
+ OPEN_FOR_WRITE_REPLACE = 2,
+ OPEN_FOR_WRITE_EDIT = 3
} GVfsJobOpenForWriteMode;
typedef enum {
--
2.46.2

View File

@ -0,0 +1,39 @@
From fabacfef29e24628f7a1a303be4b54cb006431bb Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 1 Aug 2024 12:40:15 +0200
Subject: [PATCH] fuse: Use edit mode instead of returning ENOTSUP
Currently, the FUSE daemon fails with the `ENOTSUP` error when opening
a file for writing without `O_APPEND` or `O_TRUNC`. This is a problem
for many POSIX applications. Let's try the newly added edit mode instead.
Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/249
---
client/gvfsfusedaemon.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/client/gvfsfusedaemon.c b/client/gvfsfusedaemon.c
index ff5641ae..9ec4e3d8 100644
--- a/client/gvfsfusedaemon.c
+++ b/client/gvfsfusedaemon.c
@@ -1003,6 +1003,8 @@ setup_input_stream (GFile *file, FileHandle *fh)
return result;
}
+#define _g_file_edit(file, flags, cancellable, error) g_file_append_to(file, flags | (1 << 15), cancellable, error)
+
static gint
setup_output_stream (GFile *file, FileHandle *fh, int flags)
{
@@ -1032,7 +1034,7 @@ setup_output_stream (GFile *file, FileHandle *fh, int flags)
else if (flags & O_APPEND)
fh->stream = g_file_append_to (file, 0, NULL, &error);
else
- result = -ENOTSUP;
+ fh->stream = _g_file_edit (file, 0, NULL, &error);
if (fh->stream)
fh->pos = g_seekable_tell (G_SEEKABLE (fh->stream));
}
--
2.46.2

View File

@ -0,0 +1,52 @@
From 5dd5f5b97bc753cdf32a04e3de4bddce651ceb9b Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 1 Aug 2024 12:39:49 +0200
Subject: [PATCH] gdaemonfile: Use edit mode when private edit flag is used
The newly added edit mode is meant only for private use by our FUSE
daemon. Let's add a new private flag for the append operation to enable
the edit mode.
---
client/gdaemonfile.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c
index 485698b2..d66d0934 100644
--- a/client/gdaemonfile.c
+++ b/client/gdaemonfile.c
@@ -57,6 +57,8 @@ G_DEFINE_TYPE_WITH_CODE (GDaemonFile, g_daemon_file, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
g_daemon_file_file_iface_init))
+#define PRIVATE_EDIT_FLAG (1 << 15)
+
static void
g_daemon_file_finalize (GObject *object)
{
@@ -1198,7 +1200,10 @@ g_daemon_file_append_to (GFile *file,
GCancellable *cancellable,
GError **error)
{
- return file_open_write (file, 1, "", FALSE, flags, cancellable, error);
+ if (flags & PRIVATE_EDIT_FLAG)
+ return file_open_write (file, 3, "", FALSE, flags, cancellable, error);
+ else
+ return file_open_write (file, 1, "", FALSE, flags, cancellable, error);
}
static GFileOutputStream *
@@ -3130,7 +3135,10 @@ g_daemon_file_append_to_async (GFile *file,
g_task_set_source_tag (task, g_daemon_file_append_to_async);
g_task_set_priority (task, io_priority);
- file_open_write_async (file, task, 1, "", FALSE, flags);
+ if (flags & PRIVATE_EDIT_FLAG)
+ file_open_write_async (file, task, 3, "", FALSE, flags);
+ else
+ file_open_write_async (file, task, 1, "", FALSE, flags);
}
static GFileOutputStream *
--
2.46.2

View File

@ -22,7 +22,7 @@
Name: gvfs
Version: 1.54.4
Release: 1%{?dist}
Release: 2%{?dist}
Summary: Backends for the gio framework in GLib
License: LGPL-2.0-or-later AND GPL-3.0-only AND MPL-2.0 AND BSD-3-Clause-Sun
@ -33,6 +33,15 @@ Source0: https://download.gnome.org/sources/gvfs/1.54/gvfs-%{version}.tar.xz
# https://issues.redhat.com/browse/RHEL-52355
Patch: trash-Add-support-for-x-gvfs-trash-mount-option.patch
# https://issues.redhat.com/browse/RHEL-71089
Patch: daemon-Add-support-for-edit-mode.patch
Patch: gdaemonfile-Use-edit-mode-when-private-edit-flag-is-.patch
Patch: fuse-Use-edit-mode-instead-of-returning-ENOTSUP.patch
Patch: smb-Fail-when-initial_offset-can-t-be-determined.patch
Patch: smb-Disable-seek-support-when-appening.patch
Patch: smb-Fix-offset-after-truncate-when-appending.patch
Patch: smb-Implement-support-for-edit-mode.patch
BuildRequires: meson
BuildRequires: gcc
BuildRequires: pkgconfig(glib-2.0) >= %{glib2_version}
@ -434,6 +443,9 @@ killall -USR1 gvfsd >&/dev/null || :
%changelog
* Thu Feb 13 2025 Ondrej Holy <oholy@redhat.com> - 1.54.4-2
- Add edit mode support for smb backend (RHEL-71089)
* Wed Nov 06 2024 nmontero <nmontero@redhat.com> - 1.54.4-1
- Update to 1.54.4

View File

@ -0,0 +1,36 @@
From b17f15558194ab3025c8a5cb1a2767c54333f28e Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 13 Nov 2024 11:22:27 +0100
Subject: [PATCH] smb: Disable seek support when appening
The seek is currently enabled when appending. However, the libsmbclient
doesn't support O_APPEND flag property [1]. The offset is not reset after
seeking. Let's disable seek support when appending to deal with it. The
seek operation doesn't make sense when appening anyway.
[1] https://github.com/samba-team/samba/blob/e4e3f05/source3/libsmb/libsmb_file.c#L162-L183
---
daemon/gvfsbackendsmb.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
index dafbb71c..c2075555 100644
--- a/daemon/gvfsbackendsmb.c
+++ b/daemon/gvfsbackendsmb.c
@@ -871,7 +871,12 @@ do_append_to (GVfsBackend *backend,
handle->file = file;
g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+
+ /* The O_APPEND flag is not properly supported by the libsmbclient library
+ * when seeking. See:
+ * https://github.com/samba-team/samba/blob/e4e3f05/source3/libsmb/libsmb_file.c#L162-L183
+ */
+ g_vfs_job_open_for_write_set_can_seek (job, FALSE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
g_vfs_job_open_for_write_set_handle (job, handle);
g_vfs_job_succeeded (G_VFS_JOB (job));
--
2.46.2

View File

@ -0,0 +1,54 @@
From 885bcc18b65ebd6d3c5bac56994f9f631cb9d363 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 6 Nov 2024 14:58:18 +0100
Subject: [PATCH] smb: Fail when initial_offset can't be determined
Currently, when the initial_offset can't be determined, the seek and
truncate operations are disabled. Let's fail immediatelly instead to
simplify the code as a preparation for the follow-up changes. Just a
note tha this situation can't happen with the current libsmbclient
codebase.
---
daemon/gvfsbackendsmb.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
index c1c2d5f0..dafbb71c 100644
--- a/daemon/gvfsbackendsmb.c
+++ b/daemon/gvfsbackendsmb.c
@@ -857,20 +857,22 @@ do_append_to (GVfsBackend *backend,
g_vfs_job_failed_from_errno (G_VFS_JOB (job), fixup_open_errno (errno));
else
{
- handle = g_new0 (SmbWriteHandle, 1);
- handle->file = file;
-
smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
initial_offset = smbc_lseek (op_backend->smb_context, file,
0, SEEK_CUR);
if (initial_offset == (off_t) -1)
- g_vfs_job_open_for_write_set_can_seek (job, FALSE);
- else
- {
- g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
- g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
- }
+ {
+ g_vfs_job_failed_from_errno (G_VFS_JOB (job),
+ fixup_open_errno (errno));
+ return;
+ }
+
+ handle = g_new0 (SmbWriteHandle, 1);
+ handle->file = file;
+
+ g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
+ g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+ g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
g_vfs_job_open_for_write_set_handle (job, handle);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
--
2.46.2

View File

@ -0,0 +1,85 @@
From b4ff6ddb32296f43439b4be79c7864f7ae7d14f1 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 13 Nov 2024 11:34:19 +0100
Subject: [PATCH] smb: Fix offset after truncate when appending
The truncate operation is currently enabled when appending. However,
the libsmbclient doesn't support O_APPEND flag property [1]. The
offset is not reset after the truncate operation. This may lead to
data corruptions. Let's explicitely seek to the desired offset when
truncating to prevent this.
[1] https://github.com/samba-team/samba/blob/e4e3f05/source3/libsmb/libsmb_file.c#L162-L183
---
daemon/gvfsbackendsmb.c | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
index c2075555..f287264b 100644
--- a/daemon/gvfsbackendsmb.c
+++ b/daemon/gvfsbackendsmb.c
@@ -780,6 +780,7 @@ typedef struct {
char *uri;
char *tmp_uri;
char *backup_uri;
+ GVfsJobOpenForWriteMode mode;
} SmbWriteHandle;
static void
@@ -824,6 +825,7 @@ do_create (GVfsBackend *backend,
{
handle = g_new0 (SmbWriteHandle, 1);
handle->file = file;
+ handle->mode = job->mode;
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
@@ -869,6 +871,7 @@ do_append_to (GVfsBackend *backend,
handle = g_new0 (SmbWriteHandle, 1);
handle->file = file;
+ handle->mode = job->mode;
g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
@@ -1141,6 +1144,7 @@ do_replace (GVfsBackend *backend,
handle->uri = uri;
handle->tmp_uri = tmp_uri;
handle->backup_uri = backup_uri;
+ handle->mode = job->mode;
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
@@ -1229,9 +1233,26 @@ do_truncate (GVfsBackend *backend,
smbc_ftruncate = smbc_getFunctionFtruncate (op_backend->smb_context);
if (smbc_ftruncate (op_backend->smb_context, handle->file, size) == -1)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ {
+ g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+ return;
+ }
+
+ if (handle->mode == OPEN_FOR_WRITE_APPEND)
+ {
+ smbc_lseek_fn smbc_lseek;
+ off_t res;
+
+ smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
+ res = smbc_lseek (op_backend->smb_context, handle->file, size, SEEK_SET);
+ if (res == (off_t)-1)
+ {
+ g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+ return;
+ }
+ }
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
--
2.46.2

View File

@ -0,0 +1,177 @@
From bcbf85cc1f9bd8c1cb673a5d7d3f9907d84e3caa Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 13 Nov 2024 11:52:28 +0100
Subject: [PATCH] smb: Implement support for edit mode
Implement support for the newly added edit mode in the SMB backend.
Related: https://gitlab.gnome.org/GNOME/gvfs/-/issues/249
---
daemon/gvfsbackendsmb.c | 109 +++++++++++++++++++---------------------
1 file changed, 53 insertions(+), 56 deletions(-)
diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
index f287264b..4fbdfd23 100644
--- a/daemon/gvfsbackendsmb.c
+++ b/daemon/gvfsbackendsmb.c
@@ -793,10 +793,11 @@ smb_write_handle_free (SmbWriteHandle *handle)
}
static void
-do_create (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
+open_for_write (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags,
+ int open_flags)
{
GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
char *uri;
@@ -804,12 +805,12 @@ do_create (GVfsBackend *backend,
SmbWriteHandle *handle;
smbc_open_fn smbc_open;
int errsv;
+ off_t initial_offset = 0;
uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, filename);
smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
errno = 0;
- file = smbc_open (op_backend->smb_context, uri,
- O_CREAT|O_RDWR|O_EXCL, 0666);
+ file = smbc_open (op_backend->smb_context, uri, open_flags, 0666);
g_free (uri);
if (file == NULL)
@@ -817,48 +818,16 @@ do_create (GVfsBackend *backend,
errsv = fixup_open_errno (errno);
/* We guarantee EEXIST on create on existing dir */
- if (errsv == EISDIR)
+ if (job->mode == OPEN_FOR_WRITE_CREATE && errsv == EISDIR)
errsv = EEXIST;
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
+ return;
}
- else
- {
- handle = g_new0 (SmbWriteHandle, 1);
- handle->file = file;
- handle->mode = job->mode;
-
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
- g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
- g_vfs_job_open_for_write_set_handle (job, handle);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- }
-}
-static void
-do_append_to (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
-{
- GVfsBackendSmb *op_backend = G_VFS_BACKEND_SMB (backend);
- char *uri;
- SMBCFILE *file;
- SmbWriteHandle *handle;
- off_t initial_offset;
- smbc_open_fn smbc_open;
- smbc_lseek_fn smbc_lseek;
-
- uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, filename);
- smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
- errno = 0;
- file = smbc_open (op_backend->smb_context, uri,
- O_CREAT|O_RDWR|O_APPEND, 0666);
- g_free (uri);
-
- if (file == NULL)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), fixup_open_errno (errno));
- else
+ if (job->mode == OPEN_FOR_WRITE_APPEND)
{
+ smbc_lseek_fn smbc_lseek;
+
smbc_lseek = smbc_getFunctionLseek (op_backend->smb_context);
initial_offset = smbc_lseek (op_backend->smb_context, file,
0, SEEK_CUR);
@@ -868,24 +837,51 @@ do_append_to (GVfsBackend *backend,
fixup_open_errno (errno));
return;
}
+ }
- handle = g_new0 (SmbWriteHandle, 1);
- handle->file = file;
- handle->mode = job->mode;
+ handle = g_new0 (SmbWriteHandle, 1);
+ handle->file = file;
+ handle->mode = job->mode;
- g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
+ g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
- /* The O_APPEND flag is not properly supported by the libsmbclient library
- * when seeking. See:
- * https://github.com/samba-team/samba/blob/e4e3f05/source3/libsmb/libsmb_file.c#L162-L183
- */
- g_vfs_job_open_for_write_set_can_seek (job, FALSE);
- g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
- g_vfs_job_open_for_write_set_handle (job, handle);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- }
+ /* The O_APPEND flag is not properly supported by the libsmbclient library
+ * when seeking. See:
+ * https://github.com/samba-team/samba/blob/e4e3f05/source3/libsmb/libsmb_file.c#L162-L183
+ */
+ g_vfs_job_open_for_write_set_can_seek (job,
+ job->mode != OPEN_FOR_WRITE_APPEND);
+ g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
+ g_vfs_job_open_for_write_set_handle (job, handle);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
}
+static void
+do_create (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ open_for_write (backend, job, filename, flags, O_CREAT|O_RDWR|O_EXCL);
+}
+
+static void
+do_append_to (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ open_for_write (backend, job, filename, flags, O_CREAT|O_RDWR|O_APPEND);
+}
+
+static void
+do_edit (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ open_for_write (backend, job, filename, flags, O_CREAT|O_RDWR);
+}
static char *
get_dir_from_uri (const char *uri)
@@ -2153,6 +2149,7 @@ g_vfs_backend_smb_class_init (GVfsBackendSmbClass *klass)
backend_class->close_read = do_close_read;
backend_class->create = do_create;
backend_class->append_to = do_append_to;
+ backend_class->edit = do_edit;
backend_class->replace = do_replace;
backend_class->write = do_write;
backend_class->seek_on_write = do_seek_on_write;
--
2.46.2