[vlc-commits] [Git][videolan/vlc][master] 8 commits: access: add a cache helper

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Mon Feb 14 09:00:34 UTC 2022



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
6dcd24dc by Thomas Guillem at 2022-02-13T21:30:58+00:00
access: add a cache helper

Helper that can be used by any accesses, that will save up to 5 contexts
up to 5 seconds (both configurable with a define).

This helper create its own thread that will release the resources when
unused.

This helper also use the gcc destructor attribute to clean everything
when the library is unloaded.

This helper need a destructor, thus it can only be used with gcc and
clang.

- - - - -
074209c0 by Thomas Guillem at 2022-02-13T21:30:58+00:00
smb2: handle smb2_connected state outside the disconnect fonction

- - - - -
25654510 by Thomas Guillem at 2022-02-13T21:30:58+00:00
smb2: pass smb2 and smb2fh as arguments

- - - - -
3b4a40e9 by Thomas Guillem at 2022-02-13T21:30:58+00:00
smb2: use an operation struct for all operations

Instead of using the same context from the access_sys struct.

- - - - -
e9510183 by Thomas Guillem at 2022-02-13T21:30:58+00:00
smb2: split vlc_smb2_open_share

No functional changes.

- - - - -
a2357357 by Thomas Guillem at 2022-02-13T21:30:58+00:00
smb2: use the cache helper

Cache the smb2 session when closing. It will be re-used if an other
access is opened on the same server/share/username within 5 seconds.

Small benchmark, time to open the smb2 access:
 - 200 - 250ms without cache
 - 20 - 30 ms with a cache

- - - - -
7dd7780d by Thomas Guillem at 2022-02-13T21:30:58+00:00
dsm: connect the session in login()

- - - - -
31f97cbe by Thomas Guillem at 2022-02-13T21:30:58+00:00
dsm: use the cache helper

Cache the dsm session when closing. It will be re-used if an other
access is opened on the same server/share/username within 5 seconds.

Small benchmark, time to open the dsm access:
 - 100 - 150 ms without cache
 - 1 ms with a cache

- - - - -


5 changed files:

- modules/access/Makefile.am
- + modules/access/cache.c
- + modules/access/cache.h
- modules/access/dsm/access.c
- modules/access/smb2.c


Changes:

=====================================
modules/access/Makefile.am
=====================================
@@ -369,14 +369,14 @@ EXTRA_LTLIBRARIES += libsmbc_plugin.la
 
 libdsm_plugin_la_SOURCES = access/dsm/access.c access/dsm/sd.c access/smb_common.h
 libdsm_plugin_la_CFLAGS = $(AM_CFLAGS) $(DSM_CFLAGS)
-libdsm_plugin_la_LIBADD = $(DSM_LIBS)
+libdsm_plugin_la_LIBADD = $(DSM_LIBS) libvlc_access_cache.la
 if HAVE_DSM
 access_LTLIBRARIES += libdsm_plugin.la
 endif
 
 libsmb2_plugin_la_SOURCES = access/smb2.c
 libsmb2_plugin_la_CFLAGS = $(AM_CFLAGS) $(SMB2_CFLAGS)
-libsmb2_plugin_la_LIBADD = $(SMB2_LIBS) $(SOCKET_LIBS)
+libsmb2_plugin_la_LIBADD = $(SMB2_LIBS) $(SOCKET_LIBS) libvlc_access_cache.la
 libsmb2_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
 if HAVE_DSM
 libsmb2_plugin_la_CFLAGS += $(DSM_CFLAGS) -DHAVE_DSM
@@ -463,3 +463,10 @@ librist_plugin_la_LIBADD = $(RIST_LIBS) $(SOCKET_LIBS)
 librist_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
 access_LTLIBRARIES += $(LTLIBrist)
 EXTRA_LTLIBRARIES += librist_plugin.la
+
+### TOOLS ###
+
+libvlc_access_cache_la_SOURCES = access/cache.c access/cache.h
+libvlc_access_cache_la_LIBADD = $(LTLIBVLCCORE)
+libvlc_access_cache_la_LDFLAGS = -static
+noinst_LTLIBRARIES += libvlc_access_cache.la


=====================================
modules/access/cache.c
=====================================
@@ -0,0 +1,201 @@
+/*****************************************************************************
+ * cache.c: access cache helper
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_threads.h>
+
+#include "access/cache.h"
+
+#include <assert.h>
+
+#define VLC_ACCESS_CACHE_TTL VLC_TICK_FROM_SEC(5)
+#define VLC_ACCESS_CACHE_MAX_ENTRY 5
+
+void
+vlc_access_cache_entry_Delete(struct vlc_access_cache_entry *entry)
+{
+    free(entry->url);
+    free(entry->username);
+
+    entry->free_cb(entry->context);
+    free(entry);
+}
+
+struct vlc_access_cache_entry *
+vlc_access_cache_entry_New(void *context, const char *url, const char *username,
+                           void (*free_cb)(void *context))
+{
+    struct vlc_access_cache_entry *entry = malloc(sizeof(*entry));
+    if (unlikely(entry == NULL))
+        return NULL;
+
+    entry->url = strdup(url);
+    entry->username = username ? strdup(username) : NULL;
+    if (!entry->url || (entry->username == NULL) != (username == NULL))
+    {
+        free(entry->url);
+        free(entry);
+        return NULL;
+    }
+
+    entry->context = context;
+    entry->free_cb = free_cb;
+
+    return entry;
+}
+
+static void *
+vlc_access_cache_Thread(void *data)
+{
+    struct vlc_access_cache *cache = data;
+
+    vlc_mutex_lock(&cache->lock);
+    while (cache->running)
+    {
+        if (!vlc_list_is_empty(&cache->entries))
+        {
+            struct vlc_access_cache_entry *entry =
+                vlc_list_first_entry_or_null(&cache->entries,
+                                             struct vlc_access_cache_entry, node);
+
+            if (entry->timeout == 0 ||
+                vlc_cond_timedwait(&cache->cond, &cache->lock, entry->timeout) != 0)
+            {
+                vlc_list_remove(&entry->node);
+
+                vlc_mutex_unlock(&cache->lock);
+
+                vlc_access_cache_entry_Delete(entry);
+
+                vlc_mutex_lock(&cache->lock);
+            }
+        }
+        else
+            vlc_cond_wait(&cache->cond, &cache->lock);
+    }
+    vlc_mutex_unlock(&cache->lock);
+
+    return NULL;
+}
+
+static void
+vlc_access_cache_InitOnce(void *data)
+{
+    struct vlc_access_cache *cache = data;
+
+#ifdef VLC_ACCESS_CACHE_CAN_REGISTER
+    vlc_mutex_lock(&cache->lock);
+
+    cache->running = true;
+    int ret = vlc_clone(&cache->thread, vlc_access_cache_Thread, cache,
+                        VLC_THREAD_PRIORITY_LOW);
+    if (ret != 0)
+        cache->running = false;
+
+    vlc_mutex_unlock(&cache->lock);
+#endif
+}
+
+void
+vlc_access_cache_Destroy(struct vlc_access_cache *cache)
+{
+    vlc_mutex_lock(&cache->lock);
+    if (cache->running)
+    {
+        cache->running = false;
+        vlc_cond_signal(&cache->cond);
+        vlc_mutex_unlock(&cache->lock);
+        vlc_join(cache->thread, NULL);
+    }
+    else
+        vlc_mutex_unlock(&cache->lock);
+
+    struct vlc_access_cache_entry *entry;
+    vlc_list_foreach(entry, &cache->entries, node)
+        vlc_access_cache_entry_Delete(entry);
+}
+
+void
+vlc_access_cache_AddEntry(struct vlc_access_cache *cache,
+                          struct vlc_access_cache_entry *entry)
+{
+    vlc_once(&cache->once, vlc_access_cache_InitOnce, cache);
+
+    vlc_mutex_lock(&cache->lock);
+
+    if (!cache->running)
+    {
+        vlc_mutex_unlock(&cache->lock);
+        vlc_access_cache_entry_Delete(entry);
+        return;
+    }
+
+    struct vlc_access_cache_entry *it;
+    size_t count = 0;
+    vlc_list_foreach(it, &cache->entries, node)
+        count++;
+
+    if (count >= VLC_ACCESS_CACHE_MAX_ENTRY)
+    {
+        /* Too many entries, signal the thread that will delete the first one */
+        it = vlc_list_first_entry_or_null(&cache->entries,
+                                          struct vlc_access_cache_entry, node);
+        it->timeout = 0;
+    }
+
+    entry->timeout = vlc_tick_now() + VLC_ACCESS_CACHE_TTL;
+    vlc_list_append(&entry->node, &cache->entries);
+
+    vlc_cond_signal(&cache->cond);
+    vlc_mutex_unlock(&cache->lock);
+}
+
+struct vlc_access_cache_entry *
+vlc_access_cache_GetEntry(struct vlc_access_cache *cache,
+                          const char *url, const char *username)
+{
+    vlc_once(&cache->once, vlc_access_cache_InitOnce, cache);
+
+    vlc_mutex_lock(&cache->lock);
+
+    struct vlc_access_cache_entry *it;
+
+    vlc_list_foreach(it, &cache->entries, node)
+    {
+
+        if (strcmp(url, it->url) == 0
+         && (username == NULL) == (it->username == NULL)
+         && (username != NULL ? strcmp(username, it->username) == 0 : true))
+        {
+            vlc_list_remove(&it->node);
+            vlc_cond_signal(&cache->cond);
+            vlc_mutex_unlock(&cache->lock);
+            return it;
+        }
+    }
+
+    vlc_mutex_unlock(&cache->lock);
+
+    return NULL;
+}


=====================================
modules/access/cache.h
=====================================
@@ -0,0 +1,135 @@
+/*****************************************************************************
+ * cache.h: access cache helper
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_list.h>
+
+struct vlc_access_cache_entry
+{
+    void *context;
+
+    char *url;
+    char *username;
+
+    vlc_tick_t timeout;
+    void (*free_cb)(void *context);
+
+    struct vlc_list node;
+};
+
+struct vlc_access_cache
+{
+    vlc_once_t once;
+    vlc_mutex_t lock;
+    vlc_cond_t cond;
+    vlc_thread_t thread;
+    bool running;
+
+    struct vlc_list entries;
+};
+
+#define VLC_ACCESS_CACHE_INITIALIZER(name) { \
+    .once = VLC_STATIC_ONCE, \
+    .lock = VLC_STATIC_MUTEX, \
+    .cond = VLC_STATIC_COND, \
+    .running = false, \
+    .entries = VLC_LIST_INITIALIZER(&name.entries), \
+}
+
+static inline char *
+vlc_access_cache_entry_CreateSmbUrl(const char *server, const char *share)
+{
+    char *url;
+    if (asprintf(&url, "smb://%s/%s", server, share) == -1)
+        return NULL;
+    return url;
+}
+
+struct vlc_access_cache_entry *
+vlc_access_cache_entry_New(void *context, const char *url, const char *username,
+                           void (*free_cb)(void *context));
+
+static inline struct vlc_access_cache_entry *
+vlc_access_cache_entry_NewSmb(void *context, const char *server,
+                              const char *share, const char *username,
+                              void (*free_cb)(void *context))
+{
+    char *url = vlc_access_cache_entry_CreateSmbUrl(server, share);
+    if (url == NULL)
+        return NULL;
+
+    struct vlc_access_cache_entry *entry =
+        vlc_access_cache_entry_New(context, url, username, free_cb);
+    free(url);
+    return entry;
+}
+
+void
+vlc_access_cache_entry_Delete(struct vlc_access_cache_entry *entry);
+
+void
+vlc_access_cache_Destroy(struct vlc_access_cache *cache);
+
+void
+vlc_access_cache_AddEntry(struct vlc_access_cache *cache,
+                          struct vlc_access_cache_entry *entry);
+
+struct vlc_access_cache_entry *
+vlc_access_cache_GetEntry(struct vlc_access_cache *cache,
+                          const char *url, const char *username);
+
+static inline struct vlc_access_cache_entry *
+vlc_access_cache_GetSmbEntry(struct vlc_access_cache *cache,
+                             const char *server, const char *share,
+                             const char *username)
+{
+    char *url = vlc_access_cache_entry_CreateSmbUrl(server, share);
+    if (url == NULL)
+        return NULL;
+
+    struct vlc_access_cache_entry *entry =
+        vlc_access_cache_GetEntry(cache, url, username);
+    free(url);
+
+    return entry;
+}
+
+#ifdef __has_attribute
+  #if __has_attribute(destructor)
+    #define VLC_ACCESS_CACHE_CAN_REGISTER
+  #endif
+#endif
+
+#ifdef VLC_ACCESS_CACHE_CAN_REGISTER
+#define VLC_ACCESS_CACHE_REGISTER(name) \
+static struct vlc_access_cache name = VLC_ACCESS_CACHE_INITIALIZER(name); \
+__attribute__((destructor)) static void vlc_access_cache_destructor_##name(void) \
+{ \
+    vlc_access_cache_Destroy(&name); \
+}
+#else
+#define VLC_ACCESS_CACHE_REGISTER(name) \
+static struct vlc_access_cache name = VLC_ACCESS_CACHE_INITIALIZER(name);
+#warning "can't register access cache"
+#endif


=====================================
modules/access/dsm/access.c
=====================================
@@ -51,6 +51,7 @@
 
 #include <bdsm/bdsm.h>
 #include "../smb_common.h"
+#include "../cache.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -107,6 +108,15 @@ vlc_module_end ()
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
+
+struct dsm_cache_context
+{
+    smb_session *session;
+    smb_tid tid;
+};
+
+VLC_ACCESS_CACHE_REGISTER(dsm_cache);
+
 static ssize_t Read( stream_t *, void *, size_t );
 static int Seek( stream_t *, uint64_t );
 static int Control( stream_t *, int, va_list );
@@ -133,6 +143,8 @@ typedef struct
 
     smb_fd              i_fd;               /**< SMB fd for the file we're reading */
     smb_tid             i_tid;              /**< SMB Tree ID we're connected to */
+
+    struct vlc_access_cache_entry *cache_entry;
 } access_sys_t;
 
 /*****************************************************************************
@@ -170,18 +182,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 )
@@ -244,6 +244,7 @@ static int OpenNotForced( vlc_object_t *p_this )
 /*****************************************************************************
  * Close: free unused data structures
  *****************************************************************************/
+
 static void Close( vlc_object_t *p_this )
 {
     stream_t     *p_access = (stream_t*)p_this;
@@ -253,11 +254,12 @@ static void Close( vlc_object_t *p_this )
         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 )
-        smb_session_destroy( p_sys->p_session );
     vlc_UrlClean( &p_sys->url );
     free( p_sys->psz_fullpath );
 
+    if( p_sys->cache_entry )
+        vlc_access_cache_AddEntry( &dsm_cache, p_sys->cache_entry );
+
     free( p_sys );
 }
 
@@ -352,6 +354,14 @@ error:
         == NT_STATUS_ACCESS_DENIED ? EACCES : ENOENT;
 }
 
+static void
+dsm_FreeContext(void *context_)
+{
+    struct dsm_cache_context *context = context_;
+    smb_session_destroy( context->session );
+    free( context );
+}
+
 /* Performs login with existing credentials and ask the user for new ones on
    failure */
 static int login( stream_t *p_access )
@@ -383,6 +393,41 @@ static int login( stream_t *p_access )
     }
     psz_domain = credential.psz_realm ? credential.psz_realm : p_sys->netbios_name;
 
+    struct vlc_access_cache_entry *cache_entry =
+        vlc_access_cache_GetSmbEntry( &dsm_cache, p_sys->netbios_name, p_sys->psz_share,
+                                      credential.psz_username);
+
+    if( cache_entry != NULL )
+    {
+        struct dsm_cache_context *context = cache_entry->context;
+
+        int ret = smb_fopen( context->session, context->tid,
+                             p_sys->psz_path, SMB_MOD_RO, &p_sys->i_fd );
+        if( ret == DSM_SUCCESS )
+        {
+            p_sys->cache_entry = cache_entry;
+
+            smb_session_destroy( p_sys->p_session );
+
+            p_sys->p_session = context->session;
+            p_sys->i_tid = context->tid;
+            i_ret = VLC_SUCCESS;
+            msg_Dbg( p_access, "re-using old dsm session" );
+            goto error;
+        }
+    }
+
+    /* 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 */
+        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 )
@@ -428,6 +473,28 @@ static int login( stream_t *p_access )
     if( !b_guest )
         vlc_credential_store( &credential, p_access );
 
+    if( p_sys->psz_share )
+    {
+        struct dsm_cache_context *context = malloc(sizeof(*context));
+        if( context )
+        {
+            context->session = p_sys->p_session;
+            context->tid = p_sys->i_tid;
+            p_sys->cache_entry =
+                vlc_access_cache_entry_NewSmb( context, p_sys->netbios_name,
+                                               p_sys->psz_share,
+                                               credential.psz_username,
+                                               dsm_FreeContext);
+        }
+        else
+            p_sys->cache_entry = NULL;
+
+        if( p_sys->cache_entry == NULL )
+        {
+            smb_session_destroy( p_sys->p_session );
+            goto error;
+        }
+    }
     i_ret = VLC_SUCCESS;
 error:
     vlc_credential_clean( &credential );


=====================================
modules/access/smb2.c
=====================================
@@ -58,6 +58,7 @@
 #endif
 
 #include "smb_common.h"
+#include "cache.h"
 
 static int Open(vlc_object_t *);
 static void Close(vlc_object_t *);
@@ -75,6 +76,8 @@ vlc_module_begin()
     set_callbacks(Open, Close)
 vlc_module_end()
 
+VLC_ACCESS_CACHE_REGISTER(smb2_cache);
+
 struct access_sys
 {
     struct smb2_context *   smb2;
@@ -85,59 +88,83 @@ struct access_sys
     vlc_url_t               encoded_url;
     bool                    eof;
     bool                    smb2_connected;
+
     int                     error_status;
 
+    struct vlc_access_cache_entry *cache_entry;
+};
+
+struct vlc_smb2_op
+{
+    struct vlc_logger *log;
+
+    struct smb2_context *smb2;
+
+    int error_status;
+
     bool res_done;
     union {
         struct
         {
             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, smb2_) { \
+    .log = access ? vlc_object_logger(access) : NULL, \
+    .smb2 = smb2_, \
+    .error_status = access ? ((struct access_sys *)access->p_sys)->error_status : 0, \
+    .res_done = false, \
+};
+
+static inline void
+vlc_smb2_op_reset(struct vlc_smb2_op *op, struct smb2_context *smb2)
 {
-    struct access_sys *sys = access->p_sys;
+    op->res_done = false;
+    op->smb2 = smb2;
+    op->error_status = 0;
+}
 
+static int
+smb2_check_status(struct vlc_smb2_op *op, int status, const char *psz_func)
+{
     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)
+            vlc_warning(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;
-
-    msg_Err(access, "%s failed: %d, %s", psz_func, err,
-            smb2_get_error(sys->smb2));
-    sys->error_status = err;
+    if (op->log)
+        vlc_error(op->log, "%s failed: %d, %s", psz_func, err, smb2_get_error(op->smb2));
+    op->error_status = err;
 }
 
-#define VLC_SMB2_CHECK_STATUS(access, status) \
-    smb2_check_status(access, status, __func__)
+#define VLC_SMB2_CHECK_STATUS(op, status) \
+    smb2_check_status(op, status, __func__)
 
-#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)
 
 static int
-vlc_smb2_mainloop(stream_t *access, bool teardown)
+vlc_smb2_mainloop(struct vlc_smb2_op *op, bool teardown)
 {
 #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;
@@ -146,7 +173,7 @@ vlc_smb2_mainloop(stream_t *access, bool teardown)
      * 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;
+    int original_error_status = op->error_status;
 
     if (teardown)
     {
@@ -155,16 +182,15 @@ vlc_smb2_mainloop(stream_t *access, bool teardown)
          * timeout to let a chance for a clean teardown. */
         timeout = TEARDOWN_TIMEOUT;
         poll_func = (void *)poll;
-        sys->error_status = 0;
+        op->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)
@@ -179,7 +205,8 @@ vlc_smb2_mainloop(stream_t *access, bool teardown)
         {
             if (errno == EINTR)
             {
-                msg_Warn(access, "vlc_poll_i11e interrupted");
+                if (op->log)
+                    vlc_warning(op->log, "vlc_poll_i11e interrupted");
                 if (poll_func != (void *) poll)
                 {
                     /* Try again with a timeout to let the command complete.
@@ -189,44 +216,43 @@ vlc_smb2_mainloop(stream_t *access, bool teardown)
                     poll_func = (void *) poll;
                 }
                 else
-                    sys->error_status = -errno;
+                    op->error_status = -errno;
             }
             else
             {
-                msg_Err(access, "vlc_poll_i11e failed");
-                sys->error_status = -errno;
+                if (op->log)
+                    vlc_error(op->log, "vlc_poll_i11e failed");
+                op->error_status = -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);
+                op->error_status = -ETIMEDOUT;
+            else if (smb2_service_fd(op->smb2, -1, 0) < 0)
+                VLC_SMB2_SET_ERROR(op, "smb2_service", 1);
         }
         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", 1);
             }
         }
     }
 
-    int ret = sys->error_status == 0 ? 0 : -1;
+    int ret = op->error_status == 0 ? 0 : -1;
     if (original_error_status != 0)
-        sys->error_status = original_error_status;
+        op->error_status = original_error_status;
     return ret;
 }
 
 #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
@@ -244,10 +270,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
@@ -267,18 +290,26 @@ FileRead(stream_t *access, void *buf, size_t len)
     if (len > 262144)
         len = 262144;
 
-    sys->res.read.len = 0;
+    struct vlc_smb2_op op = VLC_SMB2_OP(access, sys->smb2);
+    op.res.read.len = 0;
+
     if (smb2_read_async(sys->smb2, sys->smb2fh, buf, len,
-                        smb2_read_cb, access) < 0)
+                        smb2_read_cb, &op) < 0)
     {
-        VLC_SMB2_SET_ERROR(access, "smb2_read_async", 1);
+        VLC_SMB2_SET_ERROR(&op, "smb2_read_async", 1);
         return -1;
     }
 
-    if (vlc_smb2_mainloop(access, false) < 0)
+    if (vlc_smb2_mainloop(&op, false) < 0)
+    {
+        sys->error_status = op.error_status;
         return -1;
+    }
+
+    if (op.res.read.len == 0)
+        sys->eof = true;
 
-    return sys->res.read.len;
+    return op.res.read.len;
 }
 
 static int
@@ -289,9 +320,12 @@ FileSeek(stream_t *access, uint64_t i_pos)
     if (sys->error_status != 0)
         return VLC_EGENERIC;
 
-    if (smb2_lseek(sys->smb2, sys->smb2fh, i_pos, SEEK_SET, NULL) < 0)
+    struct vlc_smb2_op op = VLC_SMB2_OP(access, sys->smb2);
+
+    if (smb2_lseek(op.smb2, sys->smb2fh, i_pos, SEEK_SET, NULL) < 0)
     {
-        VLC_SMB2_SET_ERROR(access, "smb2_seek_async", 1);
+        VLC_SMB2_SET_ERROR(&op, "smb2_seek_async", 1);
+        sys->error_status = op.error_status;
         return VLC_EGENERIC;
     }
     sys->eof = false;
@@ -463,49 +497,32 @@ 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 *smb2,
+                  struct smb2fh *smb2fh)
 {
-    struct access_sys *sys = access->p_sys;
+    struct vlc_smb2_op op = VLC_SMB2_OP(access, smb2);
 
-    assert(sys->smb2fh);
-
-    if (smb2_close_async(sys->smb2, sys->smb2fh, smb2_generic_cb, access) < 0)
+    if (smb2_close_async(smb2, smb2fh, smb2_generic_cb, &op) < 0)
     {
-        VLC_SMB2_SET_ERROR(access, "smb2_close_async", 1);
+        VLC_SMB2_SET_ERROR(&op, "smb2_close_async", 1);
         return -1;
     }
 
-    sys->smb2fh = NULL;
-
-    return vlc_smb2_mainloop(access, true);
+    return vlc_smb2_mainloop(&op, true);
 }
 
 static int
-vlc_smb2_disconnect_share(stream_t *access)
+vlc_smb2_disconnect_share(stream_t *access, struct smb2_context *smb2)
 {
-    struct access_sys *sys = access->p_sys;
-
-    if (!sys->smb2_connected)
-        return 0;
+    struct vlc_smb2_op op = VLC_SMB2_OP(access, smb2);
 
-    if (smb2_disconnect_share_async(sys->smb2, smb2_generic_cb, access) < 0)
+    if (smb2_disconnect_share_async(smb2, smb2_generic_cb, &op) < 0)
     {
-        VLC_SMB2_SET_ERROR(access, "smb2_connect_share_async", 1);
+        VLC_SMB2_SET_ERROR(&op, "smb2_connect_share_async", 1);
         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, true);
 }
 
 static void
@@ -514,16 +531,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
@@ -559,8 +567,84 @@ 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 *smb2,
+                    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, smb2);
+
+    int ret;
+    if (do_enum)
+        ret = smb2_share_enum_async(smb2, smb2_open_cb, &op);
+    else
+    {
+        if (smb2_stat_async(smb2, smb2_url->path, &smb2_stat,
+                            smb2_generic_cb, &op) < 0)
+            VLC_SMB2_SET_ERROR(&op, "smb2_stat_async", 1);
+
+        if (vlc_smb2_mainloop(&op, false) != 0)
+            goto error;
+
+        if (smb2_stat.smb2_type == SMB2_TYPE_FILE)
+        {
+            vlc_smb2_op_reset(&op, smb2);
+
+            sys->smb2_size = smb2_stat.smb2_size;
+            ret = smb2_open_async(smb2, smb2_url->path, O_RDONLY,
+                                  smb2_open_cb, &op);
+        }
+        else if (smb2_stat.smb2_type == SMB2_TYPE_DIRECTORY)
+        {
+            vlc_smb2_op_reset(&op, smb2);
+
+            ret = smb2_opendir_async(smb2, smb2_url->path, smb2_open_cb, &op);
+        }
+        else
+        {
+            msg_Err(access, "smb2_stat_cb: file type not handled");
+            op.error_status = 1;
+            goto error;
+        }
+    }
+
+    if (ret < 0)
+    {
+        VLC_SMB2_SET_ERROR(&op, "smb2_open*_async", 1);
+        goto error;
+    }
+
+    if (vlc_smb2_mainloop(&op, false) != 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:
+    sys->error_status = op.error_status;
+    return -1;
+}
+
+static void
+vlc_smb2_FreeContext(void *context)
+{
+    struct smb2_context *smb2 = context;
+
+    vlc_smb2_disconnect_share(NULL, smb2);
+    smb2_destroy_context(smb2);
+}
+
+static int
+vlc_smb2_connect_open_share(stream_t *access, const char *url,
+                            const vlc_credential *credential)
 {
     struct access_sys *sys = access->p_sys;
 
@@ -570,7 +654,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);
 
@@ -592,70 +676,72 @@ vlc_smb2_open_share(stream_t *access, const char *url,
         password = NULL;
     }
 
+    struct vlc_access_cache_entry *cache_entry =
+        vlc_access_cache_GetSmbEntry(&smb2_cache, smb2_url->server, share,
+                                     credential->psz_username);
+    if (cache_entry != NULL)
+    {
+        int err = vlc_smb2_open_share(access, cache_entry->context, smb2_url, do_enum);
+        if (err == 0)
+        {
+            smb2_destroy_context(sys->smb2);
+            sys->smb2 = cache_entry->context;
+            sys->smb2_connected = true;
+            sys->cache_entry = cache_entry;
+
+            smb2_destroy_url(smb2_url);
+            msg_Dbg(access, "re-using old smb2 session");
+            return 0;
+        }
+    }
+
     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);
+        sys->error_status = op.error_status;
         goto error;
     }
-    if (vlc_smb2_mainloop(access, false) != 0)
+    if (vlc_smb2_mainloop(&op, false) != 0)
+    {
+        sys->error_status = op.error_status;
         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)
-    {
-        VLC_SMB2_SET_ERROR(access, "smb2_open*_async", 1);
+    err = vlc_smb2_open_share(access, sys->smb2, smb2_url, do_enum);
+    if (err < 0)
         goto error;
-    }
 
-    if (vlc_smb2_mainloop(access, false) != 0)
+    sys->cache_entry = vlc_access_cache_entry_NewSmb(sys->smb2, smb2_url->server, share,
+                                                     credential->psz_username,
+                                                     vlc_smb2_FreeContext);
+    if (sys->cache_entry == NULL)
         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);
+        if (sys->smb2_connected)
+        {
+            vlc_smb2_disconnect_share(access, sys->smb2);
+            sys->smb2_connected = false;
+        }
         smb2_destroy_context(sys->smb2);
         sys->smb2 = NULL;
     }
@@ -755,7 +841,7 @@ 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))
@@ -764,7 +850,7 @@ Open(vlc_object_t *p_obj)
                               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);
@@ -832,7 +918,7 @@ Close(vlc_object_t *p_obj)
     struct access_sys *sys = access->p_sys;
 
     if (sys->smb2fh != NULL)
-        vlc_smb2_close_fh(access);
+        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 +926,9 @@ Close(vlc_object_t *p_obj)
     else
         vlc_assert_unreachable();
 
-    vlc_smb2_disconnect_share(access);
-    smb2_destroy_context(sys->smb2);
+    assert(sys->smb2_connected);
+
+    vlc_access_cache_AddEntry(&smb2_cache, sys->cache_entry);
 
     vlc_UrlClean(&sys->encoded_url);
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2408ff7c9d276b79fd4b8c493eb195fdd249ad27...31f97cbea3e4d24874817ae2cc09e2c3b6a38d81

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2408ff7c9d276b79fd4b8c493eb195fdd249ad27...31f97cbea3e4d24874817ae2cc09e2c3b6a38d81
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