[vlc-devel] [PATCH 2/2] access: add nfs module

Thomas Guillem thomas at gllm.fr
Fri Jan 8 18:36:28 CET 2016


This module implements nfs file read and browsing via libnfs, see
https://github.com/sahlberg/libnfs .

This module use the *_async functions of libnfs and is interruptible via
vlc_interrupt.
---
 NEWS                       |   1 +
 configure.ac               |   5 +
 modules/MODULES_LIST       |   1 +
 modules/access/Makefile.am |   7 +
 modules/access/nfs.c       | 499 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 513 insertions(+)
 create mode 100644 modules/access/nfs.c

diff --git a/NEWS b/NEWS
index 9a87193..fa89ef2 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ Core:
    SMB, SFTP, FTP, RTSP ...)
 
 Access:
+ * New NFS access module using libnfs
  * Support HDS (Http Dynamic Streaming) from Adobe (f4m, f4v, etc.)
  * New SMB access module using libdsm
  * Rewrite MPEG-DASH (Dynamic Adaptive Streaming over HTTP) support, including
diff --git a/configure.ac b/configure.ac
index d44151b..2ffe14d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1761,6 +1761,11 @@ dnl
 PKG_ENABLE_MODULES_VLC([SFTP], [sftp], [libssh2], (support SFTP file transfer via libssh2), [auto])
 
 dnl
+dnl nfs access support
+dnl
+PKG_ENABLE_MODULES_VLC([NFS], [nfs], [libnfs], (support nfs protocol via libnfs), [auto])
+
+dnl
 dnl  Video4Linux 2
 dnl
 AC_ARG_ENABLE(v4l2, [AS_HELP_STRING([--disable-v4l2],
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 3dc2d9e..146fba5 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -253,6 +253,7 @@ $Id$
  * mux_wav: a WAV muxer
  * ncurses: interface module using the ncurses library
  * netsync: synchronizes the clock of remote VLCs with a server for synchronous playback
+ * nfs: NFS access module using libnfs
  * normvol: a audio filter for volume normalization
  * notify: notifications using libnotify
  * nsc: decoder for Microsoft proprietary NSC descriptors
diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index 254af65..6751bb3 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -423,6 +423,13 @@ libsftp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
 access_LTLIBRARIES += $(LTLIBsftp)
 EXTRA_LTLIBRARIES += libsftp_plugin.la
 
+libnfs_plugin_la_SOURCES = access/nfs.c
+libnfs_plugin_la_CFLAGS = $(AM_CFLAGS) $(NFS_CFLAGS)
+libnfs_plugin_la_LIBADD = $(NFS_LIBS)
+libnfs_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
+access_LTLIBRARIES += $(LTLIBnfs)
+EXTRA_LTLIBRARIES += libnfs_plugin.la
+
 libaccess_realrtsp_plugin_la_SOURCES = \
 	access/rtsp/access.c \
 	access/rtsp/rtsp.c access/rtsp/rtsp.h \
diff --git a/modules/access/nfs.c b/modules/access/nfs.c
new file mode 100644
index 0000000..0d307ac
--- /dev/null
+++ b/modules/access/nfs.c
@@ -0,0 +1,499 @@
+/*****************************************************************************
+ * nfs.c: NFS VLC access plug-in
+ *****************************************************************************
+ * Copyright © 2016  VLC authors, VideoLAN and VideoLabs
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif
+
+#include <vlc_common.h>
+#include <vlc_access.h>
+#include <vlc_dialog.h>
+#include <vlc_input_item.h>
+#include <vlc_plugin.h>
+#include <vlc_url.h>
+#include <vlc_interrupt.h>
+
+#include <nfsc/libnfs.h>
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_shortname(N_("NFS"))
+    set_description(N_("NFS input"))
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_ACCESS)
+    set_capability("access", 2)
+    add_shortcut("nfs")
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+struct access_sys_t
+{
+    struct nfs_context *    p_nfs;
+    struct nfs_url *        p_url;
+    struct nfs_stat_64      stat;
+    struct nfsfh *          p_nfsfh;
+    struct nfsdir *         p_nfsdir;
+    const char *            psz_path;
+    const char *            psz_file;
+    char *                  psz_option;
+    bool                    b_error;
+    union {
+        struct
+        {
+            uint8_t *p_buf;
+            size_t i_len;
+        } read;
+        struct
+        {
+            bool b_done;
+        } seek;
+    } res;
+};
+
+static bool
+nfs_check_status(access_t *p_access, int i_status, const char *psz_error,
+                 const char *psz_func)
+{
+    if (i_status < 0)
+    {
+        msg_Err(p_access, "%s failed: %d, '%s'", psz_func, i_status, psz_error);
+        if (!p_access->p_sys->b_error)
+            dialog_Fatal(p_access, _("NFS operation failed"), "%s", psz_error);
+        p_access->p_sys->b_error = true;
+        return true;
+    }
+    else
+        return false;
+}
+#define NFS_CHECK_STATUS(p_access, i_status, p_data) \
+    nfs_check_status(p_access, i_status, (const char *)p_data, __func__)
+
+static int
+vlc_nfs_mainloop(access_t *p_access, bool (*pf_until_cb)(access_t *))
+{
+    access_sys_t *p_sys = p_access->p_sys;
+
+    while (!p_sys->b_error && !pf_until_cb(p_access))
+    {
+        struct pollfd p_fds[1];
+        int i_ret;
+        p_fds[0].fd = nfs_get_fd(p_sys->p_nfs);
+        p_fds[0].events = nfs_which_events(p_sys->p_nfs);
+
+        if ((i_ret = vlc_poll_i11e(p_fds, 1, -1)) < 0)
+        {
+            msg_Err(p_access, "vlc_poll_i11e failed %s",
+                    errno == EINTR ? "(interrupted)" : "");
+            p_sys->b_error = true;
+        }
+        else if (i_ret > 0 && p_fds[0].revents
+             && nfs_service(p_sys->p_nfs, p_fds[0].revents) < 0)
+        {
+            msg_Err(p_access, "nfs_service failed");
+            p_sys->b_error = true;
+        }
+    }
+    return p_sys->b_error ? -1 : 0;
+}
+
+static void
+nfs_read_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+            void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    if (i_status == 0)
+        p_access->info.b_eof = true;
+    else
+    {
+        p_sys->res.read.i_len = i_status;
+        memcpy(p_sys->res.read.p_buf, p_data, i_status);
+    }
+}
+
+static bool
+read_finished_cb(access_t *p_access)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    return p_sys->res.read.i_len > 0 || p_access->info.b_eof;
+}
+
+static ssize_t
+FileRead(access_t *p_access, uint8_t *p_buf, size_t i_len)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+
+    p_sys->res.read.i_len = 0;
+    p_sys->res.read.p_buf = p_buf;
+    if (nfs_read_async(p_sys->p_nfs, p_sys->p_nfsfh, i_len, nfs_read_cb,
+                       p_access) < 0)
+    {
+        msg_Err(p_access, "nfs_read_async failed");
+        return -1;
+    }
+
+    if (vlc_nfs_mainloop(p_access, read_finished_cb) < 0)
+        return -1;
+
+    return p_sys->res.read.i_len;
+}
+
+static void
+nfs_seek_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+            void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    (void) p_data;
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    p_sys->res.seek.b_done = true;
+}
+
+static bool
+seek_finished_cb(access_t *p_access)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    return p_sys->res.seek.b_done;
+}
+
+static int
+FileSeek(access_t *p_access, uint64_t i_pos)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    p_access->info.b_eof = false;
+
+    p_sys->res.seek.b_done = false;
+    if (nfs_lseek_async(p_sys->p_nfs, p_sys->p_nfsfh, i_pos, SEEK_SET,
+                        nfs_seek_cb, p_access) < 0)
+    {
+        msg_Err(p_access, "nfs_seek_async failed");
+        return VLC_EGENERIC;
+    }
+
+    if (vlc_nfs_mainloop(p_access, seek_finished_cb) < 0)
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+static int
+FileControl(access_t *p_access, int i_query, va_list args)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+
+    switch (i_query)
+    {
+        case ACCESS_CAN_SEEK:
+            *va_arg(args, bool *) = true;
+            break;
+
+        case ACCESS_CAN_FASTSEEK:
+            *va_arg(args, bool *) = false;
+            break;
+
+        case ACCESS_CAN_PAUSE:
+        case ACCESS_CAN_CONTROL_PACE:
+            *va_arg(args, bool *) = true;
+            break;
+
+        case ACCESS_GET_SIZE:
+        {
+            *va_arg(args, uint64_t *) = p_sys->stat.nfs_size;
+            break;
+        }
+
+        case ACCESS_GET_PTS_DELAY:
+            *va_arg(args, int64_t *) = var_InheritInteger(p_access,
+                                                          "network-caching");
+            break;
+
+        case ACCESS_SET_PAUSE_STATE:
+            break;
+
+        default:
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static input_item_t *
+DirRead(access_t *p_access)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    struct nfsdirent *p_nfsdirent;
+    assert(p_sys->p_nfsdir);
+
+    p_nfsdirent = nfs_readdir(p_sys->p_nfs, p_sys->p_nfsdir);
+    if (p_nfsdirent == NULL)
+        return NULL;
+    else
+    {
+        input_item_t *p_item;
+        char *psz_uri;
+        int i_type;
+
+        if (asprintf(&psz_uri, "nfs://%s%s%s/%s%s%s", p_sys->p_url->server,
+                     p_sys->psz_path,
+                     strcmp(p_sys->psz_file, "/") == 0 ? "" : p_sys->psz_file,
+                     p_nfsdirent->name,
+                     p_sys->psz_option ? "?" : "",
+                     p_sys->psz_option ? p_sys->psz_option : "") == -1)
+            return NULL;
+
+        if (S_ISDIR(p_nfsdirent->mode))
+            i_type = ITEM_TYPE_DIRECTORY;
+        else if (S_ISREG(p_nfsdirent->mode) || S_ISLNK(p_nfsdirent->mode))
+            i_type = ITEM_TYPE_FILE;
+        else
+            i_type = ITEM_TYPE_UNKNOWN;
+
+        p_item = input_item_NewWithTypeExt(psz_uri, p_nfsdirent->name,
+                                           0, NULL, 0, -1, i_type, 1);
+        free(psz_uri);
+        return p_item;
+    }
+}
+
+static int
+DirControl(access_t *p_access, int i_query, va_list args)
+{
+    switch (i_query)
+    {
+    case ACCESS_IS_DIRECTORY:
+        *va_arg( args, bool * ) = false; /* is not sorted */
+        *va_arg( args, bool * ) = true; /* might loop */
+        break;
+    default:
+        return access_vaDirectoryControlHelper(p_access, i_query, args);
+    }
+
+    return VLC_SUCCESS;
+}
+
+static void
+nfs_opendir_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+               void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    p_sys->p_nfsdir = p_data;
+}
+
+static void
+nfs_open_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+            void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    p_sys->p_nfsfh = p_data;
+}
+
+static void
+nfs_stat64_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+              void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    struct nfs_stat_64 *p_stat = p_data;
+    p_sys->stat = *p_stat;
+
+    if (S_ISDIR(p_sys->stat.nfs_mode))
+    {
+        msg_Dbg(p_access, "nfs_opendir: '%s'", p_sys->psz_file);
+        if (nfs_opendir_async(p_sys->p_nfs, p_sys->psz_file, nfs_opendir_cb,
+                              p_access) != 0)
+        {
+            msg_Err(p_access, "nfs_opendir_async failed");
+            p_sys->b_error = true;
+        }
+    }
+    else if (S_ISREG(p_sys->stat.nfs_mode) || S_ISLNK(p_sys->stat.nfs_mode))
+    {
+        msg_Dbg(p_access, "nfs_open: '%s'", p_sys->psz_file);
+        if (nfs_open_async(p_sys->p_nfs, p_sys->psz_file, O_RDONLY,
+                           nfs_open_cb, p_access) < 0)
+        {
+            msg_Err(p_access, "nfs_open_async failed");
+            p_sys->b_error = true;
+        }
+    }
+    else
+    {
+        msg_Err(p_access, "nfs_stat64_cb: file type not handled");
+        p_sys->b_error = true;
+    }
+}
+
+static void
+nfs_mount_cb(int i_status, struct nfs_context *p_nfs, void *p_data,
+             void *p_private_data)
+{
+    access_t *p_access = p_private_data;
+    access_sys_t *p_sys = p_access->p_sys;
+    assert(p_sys->p_nfs == p_nfs);
+    (void) p_data;
+    if (NFS_CHECK_STATUS(p_access, i_status, p_data))
+        return;
+
+    if (nfs_stat64_async(p_sys->p_nfs, p_sys->psz_file, nfs_stat64_cb,
+                         p_access) < 0)
+    {
+        msg_Err(p_access, "nfs_stat64_async failed");
+        p_sys->b_error = true;
+    }
+}
+
+static bool
+open_finished_cb(access_t *p_access)
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    return p_sys->p_nfsfh != NULL || p_sys->p_nfsdir != NULL;
+}
+
+static int
+Open(vlc_object_t *p_obj)
+{
+    access_t *p_access = (access_t *)p_obj;
+
+    access_sys_t *p_sys = calloc(1, sizeof (*p_sys));
+
+    if (unlikely(p_sys == NULL))
+        goto error;
+    p_access->p_sys = p_sys;
+
+    p_sys->p_nfs = nfs_init_context();
+    if (p_sys->p_nfs == NULL)
+    {
+        msg_Err(p_access, "nfs_init_context failed");
+        goto error;
+    }
+
+    p_sys->p_url = nfs_parse_url_full(p_sys->p_nfs, p_access->psz_url);
+    if (p_sys->p_url == NULL)
+    {
+        msg_Err(p_access, "nfs_parse_url_full failed: '%s'",
+                nfs_get_error(p_sys->p_nfs));
+        goto error;
+    }
+    if (p_sys->p_url->path[0] == '\0')
+    {
+        p_sys->psz_path = p_sys->p_url->file;
+        p_sys->psz_file = "/";
+    }
+    else
+    {
+        p_sys->psz_path = p_sys->p_url->path;
+        p_sys->psz_file = p_sys->p_url->file;
+    }
+
+    msg_Dbg(p_access, "nfs_mount: server: '%s', path: '%s'",
+            p_sys->p_url->server, p_sys->psz_path);
+
+    if (nfs_mount_async(p_sys->p_nfs, p_sys->p_url->server,
+                        p_sys->psz_path, nfs_mount_cb, p_access) < 0)
+    {
+        msg_Err(p_access, "nfs_mount_async failed");
+        goto error;
+    }
+
+    if (vlc_nfs_mainloop(p_access, open_finished_cb) < 0)
+        goto error;
+
+    p_access->info.b_eof = false;
+    if (p_sys->p_nfsfh != NULL)
+    {
+        p_access->pf_read = FileRead;
+        p_access->pf_seek = FileSeek;
+        p_access->pf_control = FileControl;
+    }
+    else if (p_sys->p_nfsdir != NULL)
+    {
+        vlc_url_t url;
+        vlc_UrlParse(&url, p_access->psz_location);
+        if (url.psz_option)
+            p_sys->psz_option = strdup(url.psz_option);
+        vlc_UrlClean(&url);
+        p_access->pf_readdir = DirRead;
+        p_access->pf_seek = NULL;
+        p_access->pf_control = DirControl;
+    }
+    else
+        vlc_assert_unreachable();
+
+    return VLC_SUCCESS;
+
+error:
+    Close(p_obj);
+    return VLC_EGENERIC;
+}
+
+static void
+Close(vlc_object_t *p_obj)
+{
+    access_t *p_access = (access_t *)p_obj;
+    access_sys_t *p_sys = p_access->p_sys;
+
+    if (p_sys->p_nfsfh != NULL)
+        nfs_close(p_sys->p_nfs, p_sys->p_nfsfh);
+
+    if (p_sys->p_nfsdir != NULL)
+        nfs_closedir(p_sys->p_nfs, p_sys->p_nfsdir);
+
+    if (p_sys->p_nfs != NULL)
+        nfs_destroy_context(p_sys->p_nfs);
+
+    if (p_sys->p_url != NULL)
+        nfs_destroy_url(p_sys->p_url);
+
+    free(p_sys->psz_option);
+    free(p_sys);
+}
-- 
2.1.4



More information about the vlc-devel mailing list