[vlc-devel] [PATCH 2/2] access: add nfs module
Thomas Guillem
thomas at gllm.fr
Mon Jan 11 10:08:48 CET 2016
On Sun, Jan 10, 2016, at 20:07, Rémi Denis-Courmont wrote:
> On Friday 08 January 2016 18:36:28 Thomas Guillem wrote:
> > 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))
>
> A symbolic link does not necessarily point to a regular file...
Indeed. As I use stat and not lstat, I don't need to check for
SYMLINK...
>
> > + 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);
> > +}
>
> --
> Rémi Denis-Courmont
> http://www.remlab.net/
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list