[vlc-commits] [Git][videolan/vlc][3.0.x] 28 commits: contrib: smb2: update to 4.0.0
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Thu Apr 28 09:01:32 UTC 2022
Felix Paul Kühne pushed to branch 3.0.x at VideoLAN / VLC
Commits:
6d83f125 by Thomas Guillem at 2022-04-28T08:07:46+00:00
contrib: smb2: update to 4.0.0
Changelog since 3.0.0:
- Add support for SMB3 encryption
- Add support for Anonymous NTLMSSP logins
- Add support for readlink.
- Add API to notify application of changes to which filehandles are used by
libsmb2.
- Add suppport for Big Endian DCERPC and allow it to be controlled from the
URL.
- Add support for 3.1.1 signing
- Add support for PS2(EE) and PS3
- Fixes to UCS2 when compose characters are used.
- Various MacOS fixes
- Fix a few NULL dereferences
All theses fixes were already included in the contrib version.
(cherry picked from commit 1cf1991b648df877d7138ef79a3381026be2dd80)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
cc5792d4 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: handle smb2_connected state outside the disconnect fonction
(cherry picked from commit 074209c0c0a056eae27fda39206f56091673181d)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
796de47e by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: pass smb2 and smb2fh as arguments
(cherry picked from commit 25654510a52d48f9960f3b42e77a9c637f8b9705)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
db782849 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: use an operation struct for all operations
Instead of using the same context from the access_sys struct.
(cherry picked from commit 3b4a40e9e33dc00b1b18a900ef62db38170f31b8)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
7816adfe by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: split vlc_smb2_open_share
No functional changes.
(cherry picked from commit e95101836566925d472d78173535e5bf42e9ab4f)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
5ed57918 by Thomas Guillem at 2022-04-28T08:07:46+00:00
dsm: connect the session in login()
(cherry picked from commit 7dd7780d8fca179b93ec4eb57e9da3c08333af5f)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
30543be1 by Thomas Guillem at 2022-04-28T08:07:46+00:00
contrib: libdsm: update to 0.4.2
And switch to meson.
(cherry picked from commit 305eae38b30be838235577229a7a81aa7885b557)
(cherry picked from commit 871efa3d88b14bdfe0c2bf09a9cdcb40262ccfbd)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
c52b65b2 by Thomas Guillem at 2022-04-28T08:07:46+00:00
dsm: make the smb_session interruptible
(cherry picked from commit 36ab008eb5f7b0029512867601f11a7995f3a432)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
9915bab8 by Steve Lhomme at 2022-04-28T08:07:46+00:00
dsm: fix mismatched pointer
Fixes this warning with clang10:
incompatible pointer types passing 'u_long *' (aka 'unsigned long *') to parameter of type 'uint32_t *' (aka 'unsigned int *')
Similar to what is done in access/smb2.c
(cherry picked from commit 009bce07f3263158e439f8b99cf6d74d782da303)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
5659b3d9 by Thomas Guillem at 2022-04-28T08:07:46+00:00
dsm: reduce netbios_ns lifetime
It's only needed from get_address(), from Open().
(cherry picked from commit d0ffce0f8c6e5f56e0b21f5229d2f14e89f08e79)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
8de7cd00 by Thomas Guillem at 2022-04-28T08:07:46+00:00
dsm: make netbios_ns interruptible
(cherry picked from commit 088e3783b3fb9bb580fc80b775ae06de725081a7)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
224a6b7c by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: add missing error check
(cherry picked from commit 014e84a30c54a1831ed4deeac6506d98cde503b8)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
34f09f40 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: make the netbios resolver interruptible
(cherry picked from commit f0c6da69cc02051112d2fc60f30a886b1f2aecc4)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
608c373b by Thomas Guillem at 2022-04-28T08:07:46+00:00
dsm: fix interrupt context mismatch
The dsm interrupt context was also registered when calling the
credential API that can end up in keystore modules that need to register
an interrupt context. To fix this issue, register the interrupt context
only before dsm calls.
(cherry picked from commit 6b4b7225e5ba757ecc4ca38eb6de2e21a43a9c6d)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
14c93651 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: don't use sys->error_status while opening
But return it directly from function and sub functions.
(cherry picked from commit 29b85334bfdd9f6a89b89c19c9575be6fc033112)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
a6d1d127 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: rework error handling
- Always call VLC_SMB2_SET_ERROR() in case of error,
- Don't loose the returned error code
- Fallback to -EINVAL in case of unknown error (very unlikely case)
(cherry picked from commit c23709134c77dc70d66cc3420ceadfe08b28ed44)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
3517facb by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: destroy the context in case of error
This fixes a potential stack-buffer-overflow when destroying a context
from Close() if an operation was aborted. Indeed, the smb2_destroy()
function might trigger callbacks with private data that was allocated on
an old function stack. To fix this issue, always destroy the smb2
context immediately after an error (when the struct vlc_smb2_op is
valid).
This issue is currently hidden by the teardown mechanism (but still
possible), that always try to close gracefully in case of error.
(cherry picked from commit 924c951518f02dd05436d46323301702df516f4f)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
c1ebabfb by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: remove teardown handling
If interrupted by the user, just close the connection whitout sending a
close request and don't save the context in the cache in that case.
(cherry picked from commit cf7d48cd027c59edffdb36c9f8f40492e6e6dd6e)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
86dcae85 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: always use smb2 timeout
If the smb2_timeout was valid, then not valid (infinite), the last value
was not taken into account.
(cherry picked from commit ac95bf19f9991f506170a3dcb3c57268cb944727)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
3681112e by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: homogenize status and error functions
(cherry picked from commit e9acccaca3fd5f148f44eb833dc2aa50ade2148c)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
cfdb5355 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: rework error handling from smb2_service_fd
(cherry picked from commit 93b55c9f53e066086250d19782ec65d8e5a07942)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
b0e727a0 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: return errno directly
No changes since this function is only checked for != 0 (for now).
(cherry picked from commit 0d51ab8a3e96cca03cdca31665bfacd44d77e3c0)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
c266ba81 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: also clear op->smb2 in case of error
op->smb2 won't be used in case of error but it is cleaner like that.
(cherry picked from commit 5264a62539fa2686680c7b32b03d1756eb1c4652)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
dd80816d by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: destroy the context in case or errors from smb2_generic_cb
Errors can also be reported via generic cbs, that will cause the
vlc_smb2_mainloop to abort. In that case, we should destroy the smb2
context to fix the issue mentioned by 924c951518f02dd05436d46323301702df516f4f
(cherry picked from commit 960ef3f8ef74e352d5e3caa42f49cce8a686c8b3)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
7e34f61f by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: fix anonymous login
Not sure when this regression happened on the libsmb2 side.
But setting the password to an empty string do enable anonymous login
now.
(cherry picked from commit 205963ad09401ab3cbe5bf92c7f9b109092f87f4)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
bf6dbbc4 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: rename logged seek function name
(cherry picked from commit d70173fc2ac7d02a69c1a71159a676b9e9aa51f1)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
096208ae by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: fix lseek return type
It was causing seek error when seeking past INT_MAX
Regression from c23709134c77dc70d66cc3420ceadfe08b28ed44
(cherry picked from commit 9646722d61ec6a2439dd2586ac30e908ac349a4e)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
cabe90d1 by Thomas Guillem at 2022-04-28T08:07:46+00:00
smb2: don't seek past INT64_MAX
(cherry picked from commit e6b0c071b6483bc521b12600482bd233f87b57b9)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
- - - - -
6 changed files:
- contrib/src/libdsm/SHA512SUMS
- contrib/src/libdsm/rules.mak
- contrib/src/smb2/SHA512SUMS
- contrib/src/smb2/rules.mak
- modules/access/dsm/access.c
- modules/access/smb2.c
Changes:
=====================================
contrib/src/libdsm/SHA512SUMS
=====================================
@@ -1 +1 @@
-59963c1d12839004c7e3717cf5f029ffc5f10d72b133d88c33c1367f4343befb170c2b10f7cbc690d1700b380e298436e34b80fc214546f6ded104c649ee21eb libdsm-0.3.2.tar.gz
+9ee82c8812a807054ebe3ceb2a6ba37c36d188052cfbd7f5ba5b435ede8f6bee09690fb0d0c94e1e4d0022bbbdfa2837f09c69ccc00d17f79efe002cc158825a libdsm-0.4.2.tar.xz
=====================================
contrib/src/libdsm/rules.mak
=====================================
@@ -1,14 +1,13 @@
# libdsm
-#LIBDSM_GITURL := git://github.com/videolabs/libdsm.git
-LIBDSM_VERSION := 0.3.2
-LIBDSM_URL := https://github.com/videolabs/libdsm/releases/download/v$(LIBDSM_VERSION)/libdsm-$(LIBDSM_VERSION).tar.gz
+LIBDSM_VERSION := 0.4.2
+LIBDSM_URL := https://github.com/videolabs/libdsm/releases/download/v$(LIBDSM_VERSION)/libdsm-$(LIBDSM_VERSION).tar.xz
ifeq ($(call need_pkg,"libdsm >= 0.2.0"),)
PKGS_FOUND += libdsm
endif
-$(TARBALLS)/libdsm-$(LIBDSM_VERSION).tar.gz:
+$(TARBALLS)/libdsm-$(LIBDSM_VERSION).tar.xz:
$(call download_pkg,$(LIBDSM_URL),libdsm)
LIBDSM_CONF = $(HOSTCONF)
@@ -16,9 +15,9 @@ LIBDSM_CONF = $(HOSTCONF)
ifndef WITH_OPTIMIZATION
LIBDSM_CONF += --enable-debug
endif
-.sum-libdsm: libdsm-$(LIBDSM_VERSION).tar.gz
+.sum-libdsm: libdsm-$(LIBDSM_VERSION).tar.xz
-libdsm: libdsm-$(LIBDSM_VERSION).tar.gz .sum-libdsm
+libdsm: libdsm-$(LIBDSM_VERSION).tar.xz .sum-libdsm
$(UNPACK)
$(MOVE)
@@ -27,11 +26,8 @@ ifdef HAVE_WIN32
DEPS_libdsm += pthreads $(DEPS_pthreads)
endif
-.libdsm: libdsm
- cd $< && touch "config.rpath"
- $(RECONF)
- cd $< && $(HOSTVARS_PIC) ./configure --disable-programs $(LIBDSM_CONF)
- cd $< && $(MAKE)
- $(call pkg_static,"libdsm.pc")
- cd $< && $(MAKE) install
+.libdsm: libdsm crossfile.meson
+ cd $< && rm -rf ./build
+ cd $< && $(HOSTVARS_MESON) $(MESON) -Dauto_features=disabled -Dbinaries=false build
+ cd $< && cd build && ninja install
touch $@
=====================================
contrib/src/smb2/SHA512SUMS
=====================================
@@ -1 +1 @@
-ba70459966ec2a927058d16870f5dd73960d38a36c28500b9f1186ad7aca94449b0e8908e4c7ce46cafb8d05a8e93517df615c3ef2dbd559fd409ba98bd3a824 libsmb2-08c1682f44b00fa694836703ed16ec74987f77d2.tar.gz
+d62d05f946d104995faefa8e4fdb3a9121f697a29aac031ddd6f2c3ec9ce97edbd18d84ace56103c19e0f56356b531f7d2db826428aff52abb4930603a5fb50f libsmb2-4.0.0.tar.gz
=====================================
contrib/src/smb2/rules.mak
=====================================
@@ -1,6 +1,6 @@
# SMB2
-SMB2_VERSION := 08c1682f44b00fa694836703ed16ec74987f77d2
-SMB2_URL := https://github.com/sahlberg/libsmb2/archive/$(SMB2_VERSION).tar.gz
+SMB2_VERSION := 4.0.0
+SMB2_URL := https://github.com/sahlberg/libsmb2/archive/v$(SMB2_VERSION).tar.gz
ifeq ($(call need_pkg,"smb2"),)
PKGS_FOUND += smb2
=====================================
modules/access/dsm/access.c
=====================================
@@ -121,7 +121,6 @@ static int add_item( stream_t *p_access, struct vlc_readdir_helper *p_rdh,
struct access_sys_t
{
- netbios_ns *p_ns; /**< Netbios name service */
smb_session *p_session; /**< bdsm SMB Session object */
vlc_url_t url;
@@ -136,6 +135,53 @@ struct access_sys_t
smb_tid i_tid; /**< SMB Tree ID we're connected to */
};
+#if BDSM_VERSION_CURRENT >= 5
+
+static void
+smb_session_interrupt_callback( void *data )
+{
+ smb_session_abort( data );
+}
+
+static inline void
+smb_session_interrupt_register( access_sys_t *sys )
+{
+ vlc_interrupt_register( smb_session_interrupt_callback, sys->p_session );
+}
+
+static inline void
+smb_session_interrupt_unregister( void )
+{
+ vlc_interrupt_unregister();
+}
+
+static void
+netbios_ns_interrupt_callback( void *data )
+{
+ netbios_ns_abort( data );
+}
+
+static inline void
+netbios_ns_interrupt_register( netbios_ns *ns )
+{
+ vlc_interrupt_register( netbios_ns_interrupt_callback, ns );
+}
+
+static inline void
+netbios_ns_interrupt_unregister( void )
+{
+ vlc_interrupt_unregister();
+}
+
+#else
+
+#define smb_session_interrupt_register( sys ) do {} while (0)
+#define smb_session_interrupt_unregister() do {} while(0)
+#define netbios_ns_interrupt_register( ns ) do {} while (0)
+#define netbios_ns_interrupt_unregister() do {} while (0)
+
+#endif
+
/*****************************************************************************
* Open: Initialize module's data structures and libdsm
*****************************************************************************/
@@ -151,10 +197,6 @@ static int Open( vlc_object_t *p_this )
if( p_access->p_sys == NULL )
return VLC_ENOMEM;
- p_sys->p_ns = netbios_ns_new();
- if( p_sys->p_ns == NULL )
- goto error;
-
p_sys->p_session = smb_session_new();
if( p_sys->p_session == NULL )
goto error;
@@ -171,18 +213,6 @@ static int Open( vlc_object_t *p_this )
msg_Dbg( p_access, "Session: Host name = %s, ip = %s", p_sys->netbios_name,
inet_ntoa( p_sys->addr ) );
- /* Now that we have the required data, let's establish a session */
- status = smb_session_connect( p_sys->p_session, p_sys->netbios_name,
- p_sys->addr.s_addr, SMB_TRANSPORT_TCP );
- if( status != DSM_SUCCESS )
- {
- msg_Err( p_access, "Unable to connect/negotiate SMB session");
- /* FIXME: libdsm wrongly return network error when the server can't
- * handle the SMBv1 protocol */
- status = DSM_ERROR_GENERIC;
- goto error;
- }
-
get_path( p_access );
if( login( p_access ) != VLC_SUCCESS )
@@ -194,20 +224,27 @@ static int Open( vlc_object_t *p_this )
/* If there is no shares, browse them */
if( !p_sys->psz_share )
+ {
return BrowserInit( p_access );
+ }
assert(p_sys->i_fd > 0);
msg_Dbg( p_access, "Path: Share name = %s, path = %s", p_sys->psz_share,
p_sys->psz_path );
+ smb_session_interrupt_register( p_sys );
+
st = smb_stat_fd( p_sys->p_session, p_sys->i_fd );
if( smb_stat_get( st, SMB_STAT_ISDIR ) )
{
smb_fclose( p_sys->p_session, p_sys->i_fd );
+ smb_session_interrupt_unregister();
return BrowserInit( p_access );
}
+ smb_session_interrupt_unregister();
+
msg_Dbg( p_access, "Successfully opened smb://%s", p_access->psz_location );
ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek );
@@ -250,8 +287,6 @@ static void Close( vlc_object_t *p_this )
stream_t *p_access = (stream_t*)p_this;
access_sys_t *p_sys = p_access->p_sys;
- if( p_sys->p_ns )
- netbios_ns_destroy( p_sys->p_ns );
if( p_sys->i_fd )
smb_fclose( p_sys->p_session, p_sys->i_fd );
if( p_sys->p_session )
@@ -277,11 +312,22 @@ static int get_address( stream_t *p_access )
/* This is not an ip address, let's try netbios/dns resolve */
struct addrinfo *p_info = NULL;
+ netbios_ns *p_ns = netbios_ns_new();
+ if( p_ns == NULL )
+ return VLC_EGENERIC;
+ netbios_ns_interrupt_register( p_ns );
+
/* Is this a netbios name on this LAN ? */
- if( netbios_ns_resolve( p_sys->p_ns, p_sys->url.psz_host,
- NETBIOS_FILESERVER,
- &p_sys->addr.s_addr) == 0 )
+ uint32_t ip4_addr;
+
+ int ret = netbios_ns_resolve( p_ns, p_sys->url.psz_host,
+ NETBIOS_FILESERVER, &ip4_addr);
+ netbios_ns_interrupt_unregister();
+ netbios_ns_destroy( p_ns );
+
+ if( ret == 0 )
{
+ p_sys->addr.s_addr = ip4_addr;
strlcpy( p_sys->netbios_name, p_sys->url.psz_host, 16);
return VLC_SUCCESS;
}
@@ -304,8 +350,17 @@ static int get_address( stream_t *p_access )
return VLC_EGENERIC;
}
+ netbios_ns *p_ns = netbios_ns_new();
+ if( p_ns == NULL )
+ return VLC_EGENERIC;
+ netbios_ns_interrupt_register( p_ns );
+
/* We have an IP address, let's find the NETBIOS name */
- const char *psz_nbt = netbios_ns_inverse( p_sys->p_ns, p_sys->addr.s_addr );
+ const char *psz_nbt = netbios_ns_inverse( p_ns, p_sys->addr.s_addr );
+
+ netbios_ns_interrupt_unregister();
+ netbios_ns_destroy( p_ns );
+
if( psz_nbt != NULL )
strlcpy( p_sys->netbios_name, psz_nbt, 16 );
else
@@ -382,10 +437,29 @@ static int login( stream_t *p_access )
}
psz_domain = credential.psz_realm ? credential.psz_realm : p_sys->netbios_name;
+ smb_session_interrupt_register( p_sys );
+
+ /* Now that we have the required data, let's establish a session */
+ int status = smb_session_connect( p_sys->p_session, p_sys->netbios_name,
+ p_sys->addr.s_addr, SMB_TRANSPORT_TCP );
+ if( status != DSM_SUCCESS )
+ {
+ msg_Err( p_access, "Unable to connect/negotiate SMB session");
+ /* FIXME: libdsm wrongly return network error when the server can't
+ * handle the SMBv1 protocol */
+ smb_session_interrupt_unregister();
+ goto error;
+ }
+
/* Try to authenticate on the remote machine */
int connect_err = smb_connect( p_access, psz_login, psz_password, psz_domain );
if( connect_err == ENOENT )
+ {
+ smb_session_interrupt_unregister();
goto error;
+ }
+
+ smb_session_interrupt_unregister();
if( connect_err == EACCES )
{
@@ -405,7 +479,10 @@ static int login( stream_t *p_access )
psz_password = credential.psz_password;
psz_domain = credential.psz_realm ? credential.psz_realm
: p_sys->netbios_name;
+
+ smb_session_interrupt_register( p_sys );
connect_err = smb_connect( p_access, psz_login, psz_password, psz_domain );
+ smb_session_interrupt_unregister();
}
if( connect_err != 0 )
@@ -505,7 +582,11 @@ static int Seek( stream_t *p_access, uint64_t i_pos )
msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
- if (smb_fseek(p_sys->p_session, p_sys->i_fd, i_pos, SMB_SEEK_SET) == -1)
+ smb_session_interrupt_register( p_sys );
+ int ret = smb_fseek(p_sys->p_session, p_sys->i_fd, i_pos, SMB_SEEK_SET);
+ smb_session_interrupt_unregister();
+
+ if (ret == -1)
return VLC_EGENERIC;
return VLC_SUCCESS;
@@ -519,7 +600,10 @@ static ssize_t Read( stream_t *p_access, void *p_buffer, size_t i_len )
access_sys_t *p_sys = p_access->p_sys;
int i_read;
+ smb_session_interrupt_register( p_sys );
i_read = smb_fread( p_sys->p_session, p_sys->i_fd, p_buffer, i_len );
+ smb_session_interrupt_unregister();
+
if( i_read < 0 )
{
msg_Err( p_access, "read failed" );
@@ -602,9 +686,16 @@ static int BrowseShare( stream_t *p_access, input_item_node_t *p_node )
size_t share_count;
int i_ret = VLC_SUCCESS;
+ smb_session_interrupt_register( p_sys );
+
if( smb_share_get_list( p_sys->p_session, &shares, &share_count )
!= DSM_SUCCESS )
+ {
+ smb_session_interrupt_unregister();
return VLC_EGENERIC;
+ }
+
+ smb_session_interrupt_unregister();
struct vlc_readdir_helper rdh;
vlc_readdir_helper_init( &rdh, p_access, p_node );
@@ -639,11 +730,18 @@ static int BrowseDirectory( stream_t *p_access, input_item_node_t *p_node )
{
if( asprintf( &psz_query, "%s\\*", p_sys->psz_path ) == -1 )
return VLC_ENOMEM;
+
+ smb_session_interrupt_register( p_sys );
files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
+ smb_session_interrupt_unregister();
free( psz_query );
}
else
+ {
+ smb_session_interrupt_register( p_sys );
files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
+ smb_session_interrupt_unregister();
+ }
if( files == NULL )
return VLC_EGENERIC;
=====================================
modules/access/smb2.c
=====================================
@@ -48,8 +48,34 @@
#include <smb2/libsmb2-raw.h>
#ifdef HAVE_DSM
-# include <bdsm/netbios_ns.h>
-# include <bdsm/netbios_defs.h>
+# include <bdsm/bdsm.h>
+
+#if BDSM_VERSION_CURRENT >= 5
+
+static void
+netbios_ns_interrupt_callback(void *data)
+{
+ netbios_ns_abort(data);
+}
+
+static inline void
+netbios_ns_interrupt_register(netbios_ns *ns)
+{
+ vlc_interrupt_register(netbios_ns_interrupt_callback, ns);
+}
+
+static inline void
+netbios_ns_interrupt_unregister(void)
+{
+ vlc_interrupt_unregister();
+}
+
+#else
+
+#define netbios_ns_interrupt_register( ns ) do {} while (0)
+#define netbios_ns_interrupt_unregister() do {} while (0)
+#endif
+
#endif
#ifdef HAVE_ARPA_INET_H
@@ -85,7 +111,16 @@ struct access_sys
vlc_url_t encoded_url;
bool eof;
bool smb2_connected;
- int error_status;
+};
+
+struct vlc_smb2_op
+{
+ vlc_object_t *log;
+
+ struct smb2_context *smb2;
+ struct smb2_context **smb2p;
+
+ int error_status;
bool res_done;
union {
@@ -93,43 +128,65 @@ struct access_sys
{
size_t len;
} read;
+ void *data;
} res;
};
-static int
-smb2_check_status(stream_t *access, int status, const char *psz_func)
+#define VLC_SMB2_OP(access, smb2p_) { \
+ .log = access ? VLC_OBJECT(access) : NULL, \
+ .smb2p = smb2p_, \
+ .smb2 = (assert(*smb2p_ != NULL), *smb2p_), \
+ .error_status = 0, \
+ .res_done = false, \
+};
+
+static inline void
+vlc_smb2_op_reset(struct vlc_smb2_op *op, struct smb2_context **smb2p)
{
- struct access_sys *sys = access->p_sys;
+ op->res_done = false;
+ op->smb2p = smb2p;
+ op->smb2 = *smb2p;
+ op->error_status = 0;
+}
+static int
+smb2_check_status(struct vlc_smb2_op *op, const char *psz_func, int status)
+{
if (status < 0)
{
- const char *psz_error = smb2_get_error(sys->smb2);
- msg_Warn(access, "%s failed: %d, '%s'", psz_func, status, psz_error);
- sys->error_status = status;
+ const char *psz_error = smb2_get_error(op->smb2);
+ if (op->log)
+ msg_Warn(op->log, "%s failed: %d, '%s'", psz_func, status, psz_error);
+ op->error_status = status;
return -1;
}
else
{
- sys->res_done = true;
+ op->res_done = true;
return 0;
}
}
static void
-smb2_set_error(stream_t *access, const char *psz_func, int err)
+smb2_set_error(struct vlc_smb2_op *op, const char *psz_func, int err)
{
- struct access_sys *sys = access->p_sys;
+ if (op->log && err != -EINTR)
+ msg_Err(op->log, "%s failed: %d, %s", psz_func, err, smb2_get_error(op->smb2));
- msg_Err(access, "%s failed: %d, %s", psz_func, err,
- smb2_get_error(sys->smb2));
- sys->error_status = err;
+ /* Don't override if set via smb2_check_status */
+ if (op->error_status == 0)
+ op->error_status = err;
+
+ smb2_destroy_context(op->smb2);
+ op->smb2 = NULL;
+ *op->smb2p = NULL;
}
-#define VLC_SMB2_CHECK_STATUS(access, status) \
- smb2_check_status(access, status, __func__)
+#define VLC_SMB2_CHECK_STATUS(op, status) \
+ smb2_check_status(op, __func__, status)
-#define VLC_SMB2_SET_ERROR(access, func, err) \
- smb2_set_error(access, func, err)
+#define VLC_SMB2_SET_ERROR(op, func, err) \
+ smb2_set_error(op, func, err)
#define VLC_SMB2_STATUS_DENIED(x) (x == -ECONNREFUSED || x == -EACCES)
@@ -162,37 +219,14 @@ smb2_service_fd(struct smb2_context *smb2, int fd, int revents)
#endif
static int
-vlc_smb2_mainloop(stream_t *access, bool teardown)
+vlc_smb2_mainloop(struct vlc_smb2_op *op)
{
-#define TEARDOWN_TIMEOUT 250 /* in ms */
- struct access_sys *sys = access->p_sys;
-
- int timeout = -1;
- int (*poll_func)(struct pollfd *, unsigned, int) = vlc_poll_i11e;
-
- /* vlc_smb2_mainloop() can be called to clean-up after an error, but this
- * function can override the error_status (from async cbs). Therefore,
- * store the original error_status in order to restore it at the end of
- * this call (since we want to keep the first and original error status). */
- int original_error_status = sys->error_status;
-
- if (teardown)
- {
- /* Don't use vlc_poll_i11e that will return immediately with the EINTR
- * errno if VLC's input is interrupted. Use the posix poll with a
- * timeout to let a chance for a clean teardown. */
- timeout = TEARDOWN_TIMEOUT;
- poll_func = (void *)poll;
- sys->error_status = 0;
- }
-
- sys->res_done = false;
- while (sys->error_status == 0 && !sys->res_done)
+ while (op->error_status == 0 && !op->res_done)
{
int ret, smb2_timeout;
size_t fd_count;
- const t_socket *fds = smb2_get_fds(sys->smb2, &fd_count, &smb2_timeout);
- int events = smb2_which_events(sys->smb2);
+ const t_socket *fds = smb2_get_fds(op->smb2, &fd_count, &smb2_timeout);
+ int events = smb2_which_events(op->smb2);
struct pollfd p_fds[fd_count];
for (size_t i = 0; i < fd_count; ++i)
@@ -200,61 +234,46 @@ vlc_smb2_mainloop(stream_t *access, bool teardown)
p_fds[i].events = events;
p_fds[i].fd = fds[i];
}
- if (smb2_timeout != -1)
- timeout = smb2_timeout;
- if (fds == NULL || (ret = poll_func(p_fds, fd_count, timeout)) < 0)
+ if (fds == NULL || (ret = vlc_poll_i11e(p_fds, fd_count, smb2_timeout)) < 0)
{
- if (errno == EINTR)
- {
- msg_Warn(access, "vlc_poll_i11e interrupted");
- if (poll_func != (void *) poll)
- {
- /* Try again with a timeout to let the command complete.
- * Indeed, if this command is interrupted, every future
- * commands will fail and we won't be able to teardown. */
- timeout = TEARDOWN_TIMEOUT;
- poll_func = (void *) poll;
- }
- else
- sys->error_status = -errno;
- }
- else
- {
- msg_Err(access, "vlc_poll_i11e failed");
- sys->error_status = -errno;
- }
+ if (op->log && errno == EINTR)
+ msg_Warn(op->log, "vlc_poll_i11e interrupted");
+ VLC_SMB2_SET_ERROR(op, "poll", -errno);
}
else if (ret == 0)
{
- if (teardown)
- sys->error_status = -ETIMEDOUT;
- else if (smb2_service_fd(sys->smb2, -1, 0) < 0)
- VLC_SMB2_SET_ERROR(access, "smb2_service", 1);
+ if (smb2_service_fd(op->smb2, -1, 0) < 0)
+ VLC_SMB2_SET_ERROR(op, "smb2_service", -EINVAL);
}
else
{
for (size_t i = 0; i < fd_count; ++i)
{
if (p_fds[i].revents
- && smb2_service_fd(sys->smb2, p_fds[i].fd, p_fds[i].revents) < 0)
- VLC_SMB2_SET_ERROR(access, "smb2_service", 1);
+ && smb2_service_fd(op->smb2, p_fds[i].fd, p_fds[i].revents) < 0)
+ VLC_SMB2_SET_ERROR(op, "smb2_service", -EINVAL);
}
}
}
- int ret = sys->error_status == 0 ? 0 : -1;
- if (original_error_status != 0)
- sys->error_status = original_error_status;
- return ret;
+ if (op->error_status != 0 && op->smb2 != NULL)
+ {
+ /* An error was signalled from a smb2 cb. Destroy the smb2 context now
+ * since this call might still trigger callbacks using the current op
+ * (that is allocated on the stack). */
+ smb2_destroy_context(op->smb2);
+ op->smb2 = NULL;
+ *op->smb2p = NULL;
+ }
+
+ return op->error_status;
}
#define VLC_SMB2_GENERIC_CB() \
- VLC_UNUSED(smb2); \
- stream_t *access = private_data; \
- struct access_sys *sys = access->p_sys; \
- assert(sys->smb2 == smb2); \
- if (VLC_SMB2_CHECK_STATUS(access, status)) \
+ struct vlc_smb2_op *op = private_data; \
+ assert(op->smb2 == smb2); (void) smb2; \
+ if (VLC_SMB2_CHECK_STATUS(op, status)) \
return
static void
@@ -272,10 +291,7 @@ smb2_read_cb(struct smb2_context *smb2, int status, void *data,
VLC_UNUSED(data);
VLC_SMB2_GENERIC_CB();
- if (status == 0)
- sys->eof = true;
- else
- sys->res.read.len = status;
+ op->res.read.len = status;
}
static ssize_t
@@ -283,7 +299,7 @@ FileRead(stream_t *access, void *buf, size_t len)
{
struct access_sys *sys = access->p_sys;
- if (sys->eof || sys->error_status != 0)
+ if (sys->eof || sys->smb2 == NULL)
return 0;
/* Limit the read size since smb2_read_async() will complete only after
@@ -292,18 +308,24 @@ FileRead(stream_t *access, void *buf, size_t len)
if (len > 262144)
len = 262144;
- sys->res.read.len = 0;
- if (smb2_read_async(sys->smb2, sys->smb2fh, buf, len,
- smb2_read_cb, access) < 0)
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, &sys->smb2);
+ op.res.read.len = 0;
+
+ int err = smb2_read_async(sys->smb2, sys->smb2fh, buf, len,
+ smb2_read_cb, &op);
+ if (err < 0)
{
- VLC_SMB2_SET_ERROR(access, "smb2_read_async", 1);
+ VLC_SMB2_SET_ERROR(&op, "smb2_read_async", err);
return 0;
}
- if (vlc_smb2_mainloop(access, false) < 0)
+ if (vlc_smb2_mainloop(&op) < 0)
return 0;
- return sys->res.read.len;
+ if (op.res.read.len == 0)
+ sys->eof = true;
+
+ return op.res.read.len;
}
static int
@@ -311,14 +333,24 @@ FileSeek(stream_t *access, uint64_t i_pos)
{
struct access_sys *sys = access->p_sys;
- if (sys->error_status != 0)
+ if (sys->smb2 == NULL)
return VLC_EGENERIC;
- if (smb2_lseek(sys->smb2, sys->smb2fh, i_pos, SEEK_SET, NULL) < 0)
+ if (i_pos > INT64_MAX)
{
- VLC_SMB2_SET_ERROR(access, "smb2_seek_async", 1);
+ msg_Err(access, "can't seek past INT64_MAX (requested: %"PRIu64")\n",
+ i_pos);
return VLC_EGENERIC;
}
+
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, &sys->smb2);
+
+ int64_t err = smb2_lseek(op.smb2, sys->smb2fh, i_pos, SEEK_SET, NULL);
+ if (err < 0)
+ {
+ VLC_SMB2_SET_ERROR(&op, "smb2_lseek", err);
+ return err;
+ }
sys->eof = false;
return VLC_SUCCESS;
@@ -465,49 +497,34 @@ ShareEnum(stream_t *access, input_item_node_t *p_node)
}
static int
-vlc_smb2_close_fh(stream_t *access)
+vlc_smb2_close_fh(stream_t *access, struct smb2_context **smb2p,
+ struct smb2fh *smb2fh)
{
- struct access_sys *sys = access->p_sys;
-
- assert(sys->smb2fh);
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, smb2p);
- if (smb2_close_async(sys->smb2, sys->smb2fh, smb2_generic_cb, access) < 0)
+ int err = smb2_close_async(op.smb2, smb2fh, smb2_generic_cb, &op);
+ if (err < 0)
{
- VLC_SMB2_SET_ERROR(access, "smb2_close_async", 1);
+ VLC_SMB2_SET_ERROR(&op, "smb2_close_async", err);
return -1;
}
- sys->smb2fh = NULL;
-
- return vlc_smb2_mainloop(access, true);
+ return vlc_smb2_mainloop(&op);
}
static int
-vlc_smb2_disconnect_share(stream_t *access)
+vlc_smb2_disconnect_share(stream_t *access, struct smb2_context **smb2p)
{
- struct access_sys *sys = access->p_sys;
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, smb2p);
- if (!sys->smb2_connected)
- return 0;
-
- if (smb2_disconnect_share_async(sys->smb2, smb2_generic_cb, access) < 0)
+ int err = smb2_disconnect_share_async(op.smb2, smb2_generic_cb, &op);
+ if (err < 0)
{
- VLC_SMB2_SET_ERROR(access, "smb2_connect_share_async", 1);
+ VLC_SMB2_SET_ERROR(&op, "smb2_connect_share_async", err);
return -1;
}
- int ret = vlc_smb2_mainloop(access, true);
- sys->smb2_connected = false;
- return ret;
-}
-
-static void
-smb2_opendir_cb(struct smb2_context *smb2, int status, void *data,
- void *private_data)
-{
- VLC_SMB2_GENERIC_CB();
-
- sys->smb2dir = data;
+ return vlc_smb2_mainloop(&op);
}
static void
@@ -516,16 +533,7 @@ smb2_open_cb(struct smb2_context *smb2, int status, void *data,
{
VLC_SMB2_GENERIC_CB();
- sys->smb2fh = data;
-}
-
-static void
-smb2_share_enum_cb(struct smb2_context *smb2, int status, void *data,
- void *private_data)
-{
- VLC_SMB2_GENERIC_CB();
-
- sys->share_enum = data;
+ op->res.data = data;
}
static void
@@ -561,8 +569,77 @@ vlc_smb2_print_addr(stream_t *access)
}
static int
-vlc_smb2_open_share(stream_t *access, const char *url,
- const vlc_credential *credential)
+vlc_smb2_open_share(stream_t *access, struct smb2_context **smb2p,
+ struct smb2_url *smb2_url, bool do_enum)
+{
+ struct access_sys *sys = access->p_sys;
+ struct smb2_stat_64 smb2_stat;
+
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, smb2p);
+
+ int ret;
+ if (do_enum)
+ ret = smb2_share_enum_async(op.smb2, smb2_open_cb, &op);
+ else
+ {
+ ret = smb2_stat_async(op.smb2, smb2_url->path, &smb2_stat,
+ smb2_generic_cb, &op);
+ if (ret < 0)
+ {
+ VLC_SMB2_SET_ERROR(&op, "smb2_stat_async", ret);
+ goto error;
+ }
+
+ if (vlc_smb2_mainloop(&op) != 0)
+ goto error;
+
+ if (smb2_stat.smb2_type == SMB2_TYPE_FILE)
+ {
+ vlc_smb2_op_reset(&op, smb2p);
+
+ sys->smb2_size = smb2_stat.smb2_size;
+ ret = smb2_open_async(op.smb2, smb2_url->path, O_RDONLY,
+ smb2_open_cb, &op);
+ }
+ else if (smb2_stat.smb2_type == SMB2_TYPE_DIRECTORY)
+ {
+ vlc_smb2_op_reset(&op, smb2p);
+
+ ret = smb2_opendir_async(op.smb2, smb2_url->path, smb2_open_cb, &op);
+ }
+ else
+ {
+ msg_Err(access, "smb2_stat_cb: file type not handled");
+ ret = -ENOENT;
+ }
+ }
+
+ if (ret < 0)
+ {
+ VLC_SMB2_SET_ERROR(&op, "smb2_open*_async", ret);
+ goto error;
+ }
+
+ if (vlc_smb2_mainloop(&op) != 0)
+ goto error;
+
+ if (do_enum)
+ sys->share_enum = op.res.data;
+ else if (smb2_stat.smb2_type == SMB2_TYPE_FILE)
+ sys->smb2fh = op.res.data;
+ else if (smb2_stat.smb2_type == SMB2_TYPE_DIRECTORY)
+ sys->smb2dir = op.res.data;
+ else
+ vlc_assert_unreachable();
+
+ return 0;
+error:
+ return op.error_status;
+}
+
+static int
+vlc_smb2_connect_open_share(stream_t *access, const char *url,
+ const vlc_credential *credential)
{
struct access_sys *sys = access->p_sys;
@@ -572,7 +649,7 @@ vlc_smb2_open_share(stream_t *access, const char *url,
if (sys->smb2 == NULL)
{
msg_Err(access, "smb2_init_context failed");
- goto error;
+ return -1;
}
smb2_url = smb2_parse_url(sys->smb2, url);
@@ -590,78 +667,57 @@ vlc_smb2_open_share(stream_t *access, const char *url,
if (!username)
{
username = "Guest";
- /* A NULL password enable ntlmssp anonymous login */
- password = NULL;
+ /* An empty password enable ntlmssp anonymous login */
+ password = "";
}
smb2_set_security_mode(sys->smb2, SMB2_NEGOTIATE_SIGNING_ENABLED);
smb2_set_password(sys->smb2, password);
smb2_set_domain(sys->smb2, domain ? domain : "");
+ struct vlc_smb2_op op = VLC_SMB2_OP(access, &sys->smb2);
int err = smb2_connect_share_async(sys->smb2, smb2_url->server, share,
- username, smb2_generic_cb, access);
+ username, smb2_generic_cb, &op);
if (err < 0)
{
- VLC_SMB2_SET_ERROR(access, "smb2_connect_share_async", err);
+ VLC_SMB2_SET_ERROR(&op, "smb2_connect_share_async", err);
goto error;
}
- if (vlc_smb2_mainloop(access, false) != 0)
+ if (vlc_smb2_mainloop(&op) != 0)
goto error;
+
sys->smb2_connected = true;
vlc_smb2_print_addr(access);
- int ret;
- if (do_enum)
- ret = smb2_share_enum_async(sys->smb2, smb2_share_enum_cb, access);
- else
- {
- struct smb2_stat_64 smb2_stat;
- if (smb2_stat_async(sys->smb2, smb2_url->path, &smb2_stat,
- smb2_generic_cb, access) < 0)
- VLC_SMB2_SET_ERROR(access, "smb2_stat_async", 1);
-
- if (vlc_smb2_mainloop(access, false) != 0)
- goto error;
-
- if (smb2_stat.smb2_type == SMB2_TYPE_FILE)
- {
- sys->smb2_size = smb2_stat.smb2_size;
- ret = smb2_open_async(sys->smb2, smb2_url->path, O_RDONLY,
- smb2_open_cb, access);
- }
- else if (smb2_stat.smb2_type == SMB2_TYPE_DIRECTORY)
- ret = smb2_opendir_async(sys->smb2, smb2_url->path,
- smb2_opendir_cb, access);
- else
- {
- msg_Err(access, "smb2_stat_cb: file type not handled");
- sys->error_status = 1;
- goto error;
- }
- }
-
- if (ret < 0)
+ err = vlc_smb2_open_share(access, &sys->smb2, smb2_url, do_enum);
+ if (err < 0)
{
- VLC_SMB2_SET_ERROR(access, "smb2_open*_async", 1);
+ op.error_status = err;
goto error;
}
- if (vlc_smb2_mainloop(access, false) != 0)
- goto error;
smb2_destroy_url(smb2_url);
return 0;
error:
if (smb2_url != NULL)
smb2_destroy_url(smb2_url);
+
if (sys->smb2 != NULL)
{
- vlc_smb2_disconnect_share(access);
- smb2_destroy_context(sys->smb2);
- sys->smb2 = NULL;
+ if (sys->smb2_connected)
+ {
+ vlc_smb2_disconnect_share(access, &sys->smb2);
+ sys->smb2_connected = false;
+ }
+ if (sys->smb2 != NULL)
+ {
+ smb2_destroy_context(sys->smb2);
+ sys->smb2 = NULL;
+ }
}
- return -1;
+ return op.error_status;
}
static char *
@@ -689,6 +745,9 @@ vlc_smb2_resolve(stream_t *access, const char *host, unsigned port)
/* Test if the host is a netbios name */
char *out_host = NULL;
netbios_ns *ns = netbios_ns_new();
+ if (!ns)
+ return NULL;
+ netbios_ns_interrupt_register(ns);
uint32_t ip4_addr;
if (netbios_ns_resolve(ns, host, NETBIOS_FILESERVER, &ip4_addr) == 0)
{
@@ -696,6 +755,7 @@ vlc_smb2_resolve(stream_t *access, const char *host, unsigned port)
if (inet_ntop(AF_INET, &ip4_addr, ip, sizeof(ip)))
out_host = strdup(ip);
}
+ netbios_ns_interrupt_unregister();
netbios_ns_destroy(ns);
return out_host;
#else
@@ -710,6 +770,7 @@ Open(vlc_object_t *p_obj)
stream_t *access = (stream_t *)p_obj;
struct access_sys *sys = vlc_obj_calloc(p_obj, 1, sizeof (*sys));
char *var_domain = NULL;
+ int ret;
if (unlikely(sys == NULL))
return VLC_ENOMEM;
@@ -742,10 +803,10 @@ Open(vlc_object_t *p_obj)
{
free(url);
free(resolved_host);
+ ret = -ENOMEM;
goto error;
}
- int ret = -1;
vlc_credential credential;
vlc_credential_init(&credential, &sys->encoded_url);
var_domain = var_InheritString(access, "smb-domain");
@@ -755,17 +816,13 @@ Open(vlc_object_t *p_obj)
* keystore/user interaction) */
vlc_credential_get(&credential, access, "smb-user", "smb-pwd", NULL,
NULL);
- ret = vlc_smb2_open_share(access, url, &credential);
+ ret = vlc_smb2_connect_open_share(access, url, &credential);
- while (ret == -1
- && (!sys->error_status || VLC_SMB2_STATUS_DENIED(sys->error_status))
+ while (VLC_SMB2_STATUS_DENIED(ret)
&& vlc_credential_get(&credential, access, "smb-user", "smb-pwd",
SMB_LOGIN_DIALOG_TITLE, SMB_LOGIN_DIALOG_TEXT,
sys->encoded_url.psz_host))
- {
- sys->error_status = 0;
- ret = vlc_smb2_open_share(access, url, &credential);
- }
+ ret = vlc_smb2_connect_open_share(access, url, &credential);
free(resolved_host);
free(url);
if (ret == 0)
@@ -821,8 +878,7 @@ error:
* case of network error (EIO) or when the user asked to cancel it
* (vlc_killed()). Indeed, in these cases, it is useless to try next smb
* modules. */
- return vlc_killed() || sys->error_status == -EIO ? VLC_ETIMEOUT
- : VLC_EGENERIC;
+ return vlc_killed() || ret == -EIO ? VLC_ETIMEOUT : VLC_EGENERIC;
}
static void
@@ -832,7 +888,10 @@ Close(vlc_object_t *p_obj)
struct access_sys *sys = access->p_sys;
if (sys->smb2fh != NULL)
- vlc_smb2_close_fh(access);
+ {
+ if (sys->smb2)
+ vlc_smb2_close_fh(access, &sys->smb2, sys->smb2fh);
+ }
else if (sys->smb2dir != NULL)
smb2_closedir(sys->smb2, sys->smb2dir);
else if (sys->share_enum != NULL)
@@ -840,8 +899,14 @@ Close(vlc_object_t *p_obj)
else
vlc_assert_unreachable();
- vlc_smb2_disconnect_share(access);
- smb2_destroy_context(sys->smb2);
+ assert(sys->smb2_connected);
+
+ if (sys->smb2 != NULL)
+ {
+ vlc_smb2_disconnect_share(access, &sys->smb2);
+ if (sys->smb2 != NULL)
+ smb2_destroy_context(sys->smb2);
+ }
vlc_UrlClean(&sys->encoded_url);
}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1eaadd70b0ca277bf038aa88dd90c1b2ae3fba49...cabe90d174ce3dd22db45887e9146456339790a0
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1eaadd70b0ca277bf038aa88dd90c1b2ae3fba49...cabe90d174ce3dd22db45887e9146456339790a0
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list