vfs_unityed_media: VFS module for sharing AVID projects
authorRalph Boehme <slow@samba.org>
Wed, 22 Oct 2014 17:17:12 +0000 (19:17 +0200)
committerJeremy Allison <jra@samba.org>
Sat, 10 Jan 2015 03:15:04 +0000 (04:15 +0100)
Based on <https://code.google.com/p/vfs-unityed-media/>.

The existing VFS module media_harmony has some problems relative to Avid
media sharing:

Avid looks at the modification time of the ingest directory. Since
media_harmony has everyone using the same directory, users (or client
systems) have to somehow create "fake" directories with special names
and then media_harmony returns the mod time of those fake directories
for the different clients rather than the actual mod time of the
communal ingest directory.

To make matters worse, users then have to have a special utility or
understand how to update the modtime on these specially named
directories. Otherwise, their client system will never update the
indexes to show new media.

To make it even worse than that, Avid creates new directories on the
fly, so you can't just set this up statically at the beginning. Avid
will silently create a new directory and your reindexing problems will
start all over until you create new fake directories.

With unityed_media:

* there are no reindexes between clients

* clients don't need to know which directories have been created for
  them, it's automatic.

* clients never have to reindex other systems directories.

* unityed_media let's each client have their own directories.

* unityed_media works much more like Avid's own ISIS servers work.

A module option controls which name is appended to client specific
paths: the username, the hostname (will not work with OS X) or the
client's IP.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Sat Jan 10 04:15:04 CET 2015 on sn-devel-104

docs-xml/manpages/vfs_unityed_media.8.xml [new file with mode: 0644]
docs-xml/wscript_build
source3/modules/vfs_unityed_media.c [new file with mode: 0644]
source3/modules/wscript_build
source3/wscript

diff --git a/docs-xml/manpages/vfs_unityed_media.8.xml b/docs-xml/manpages/vfs_unityed_media.8.xml
new file mode 100644 (file)
index 0000000..0d061c6
--- /dev/null
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="vfs_media_harmony.8">
+
+<refmeta>
+       <refentrytitle>vfs_unityed_media</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">4.2</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>vfs_unityed_media</refname>
+       <refpurpose>Allow multiple Avid clients to share a network drive.</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>vfs objects = unityed_media</command>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>This VFS module is part of the
+       <citerefentry><refentrytitle>samba</refentrytitle>
+       <manvolnum>7</manvolnum></citerefentry> suite.</para>
+
+       <para>Unityed Media is related with Media Harmony VFS, the
+       main difference between Unityed Media and Media Harmony is
+       that Unityed Media doesn't need manual refreshing of media
+       directories. Unityed Media handles your media files in a similar
+       way to the way Unity, ISIS, EditShare or another dedicated solution
+       does. Without client-side application and on hardware of your
+       choice.</para>
+
+       <para>This module is stackable.</para>
+</refsect1>
+
+<refsect1>
+       <title>CONFIGURATION</title>
+
+       <para>If Mac and Windows Avid clients will be accessing the same
+       folder, they should be given separate share definitions, with
+       hidden Mac files vetoed on the Windows share.  See EXAMPLES.</para>
+</refsect1>
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+
+         <varlistentry>
+           <term>unityed_media:clientid = user | hostname | ip </term>
+           <listitem>
+             <para>Controls what client related identifier is
+             appended to user specific paths:</para>
+
+             <itemizedlist>
+               <listitem><para><command>user (default)</command> -
+               use the username.  </para></listitem>
+
+               <listitem><para><command>hostname</command> - use the
+               hostname. Note this will not work with OS X clients as
+               these always send a generic string ("workstation") to
+               the server..</para></listitem>
+
+               <listitem><para><command>ip</command> - use the
+               client's IP address.  NOTE: this is untested and may
+               not work at all.</para></listitem>
+             </itemizedlist>
+
+           </listitem>
+         </varlistentry>
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>EXAMPLES</title>
+
+       <para>Enable unityed_media for Mac and Windows clients:</para>
+<programlisting>
+        <smbconfsection name="[avid_mac]"/>
+       <smbconfoption name="path">/avid</smbconfoption>
+       <smbconfoption name="vfs objects">unityed_media</smbconfoption>
+        <smbconfsection name="[avid_win]"/>
+       <smbconfoption name="path">/avid</smbconfoption>
+       <smbconfoption name="vfs objects">unityed_media</smbconfoption>
+       <smbconfoption name="veto files">/.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/</smbconfoption>
+       <smbconfoption name="delete veto files">yes</smbconfoption>
+</programlisting>
+
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.2 of the Samba suite.
+       </para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para>The original Samba software and related utilities
+       were created by Andrew Tridgell. Samba is now developed
+       by the Samba Team as an Open Source project similar
+       to the way the Linux kernel is developed.</para>
+
+</refsect1>
+
+</refentry>
index 0bc3f54..4060021 100644 (file)
@@ -83,6 +83,7 @@ manpages='''
          manpages/vfs_syncops.8
          manpages/vfs_time_audit.8
          manpages/vfs_tsmsm.8
+         manpages/vfs_unityed_media.8
          manpages/vfs_worm.8
          manpages/vfs_xattr_tdb.8
          manpages/vfstest.1
diff --git a/source3/modules/vfs_unityed_media.c b/source3/modules/vfs_unityed_media.c
new file mode 100644 (file)
index 0000000..7dc187f
--- /dev/null
@@ -0,0 +1,1953 @@
+/*
+ * Samba VFS module supporting multiple AVID clients sharing media.
+ *
+ * Copyright (C) 2005  Philip de Nier <philipn@users.sourceforge.net>
+ * Copyright (C) 2012  Andrew Klaassen <clawsoon@yahoo.com>
+ * Copyright (C) 2013  Milos Lukacek
+ * Copyright (C) 2013  Ralph Boehme <slow@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+/*
+ * Unityed Media is a Samba VFS module that allows multiple AVID
+ * clients to share media.
+ *
+ * Add this module to the vfs objects option in your Samba share
+ * configuration.
+ * eg.
+ *
+ *   [avid_win]
+ *     path = /video
+ *     vfs objects = unityed_media
+ *     ...
+ *
+ * It is recommended that you separate out Samba shares for Mac
+ * and Windows clients, and add the following options to the shares
+ * for Windows clients (NOTE: replace @ with *):
+ *
+ *     veto files = /.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/
+ *     delete veto files = yes
+ *
+ * This prevents hidden files from Mac clients interfering with Windows
+ * clients. If you find any more problem hidden files then add them to
+ * the list.
+ *
+ * Notes:
+ * This module is designed to work with AVID editing applications that
+ * look in the Avid MediaFiles or OMFI MediaFiles directory for media.
+ * It is not designed to work as expected in all circumstances for
+ * general use.
+ */
+
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
+#include "../smbd/globals.h"
+#include "auth.h"
+#include "../lib/tsocket/tsocket.h"
+#include <libgen.h>
+
+#define UM_PARAM_TYPE_NAME "unityed_media"
+
+static const char *AVID_MXF_DIRNAME = "Avid MediaFiles/MXF";
+static const size_t AVID_MXF_DIRNAME_LEN = 19;
+static const char *OMFI_MEDIAFILES_DIRNAME = "OMFI MediaFiles";
+static const size_t OMFI_MEDIAFILES_DIRNAME_LEN = 15;
+static const char *APPLE_DOUBLE_PREFIX = "._";
+static const size_t APPLE_DOUBLE_PREFIX_LEN = 2;
+static int vfs_um_debug_level = DBGC_VFS;
+
+enum um_clientid {UM_CLIENTID_NAME, UM_CLIENTID_IP, UM_CLIENTID_HOSTNAME};
+
+struct um_config_data {
+       enum um_clientid clientid;
+};
+
+static const struct enum_list um_clientid[] = {
+       {UM_CLIENTID_NAME, "user"},
+       {UM_CLIENTID_IP, "ip"},
+       {UM_CLIENTID_HOSTNAME, "hostname"},
+       {-1, NULL}
+};
+
+/* supplements the directory list stream */
+typedef struct um_dirinfo_struct {
+       DIR* dirstream;
+       char *dirpath;
+       char *clientPath;
+       bool isInMediaFiles;
+       char *clientSubDirname;
+} um_dirinfo_struct;
+
+/**
+ * Returns true and first group of digits in path, false and 0 otherwise
+ **/
+static bool get_digit_group(const char *path, uintmax_t *digit)
+{
+       const char *p = path;
+       char *endp = NULL;
+       codepoint_t cp;
+       size_t size;
+
+       DEBUG(10, ("get_digit_group entering with path '%s'\n",
+                  path));
+
+       /*
+        * Delibiretly initialize to 0 because callers use this result
+        * even though the string doesn't contain any number and we
+        * returned false
+        */
+       *digit = 0;
+
+       while (*p) {
+               cp = next_codepoint(p, &size);
+               if (cp == -1) {
+                       return false;
+               }
+               if ((size == 1) && (isdigit(cp))) {
+                       *digit = (uintmax_t)strtoul(p, &endp, 10);
+                       DEBUG(10, ("num_suffix = '%ju'\n",
+                                  *digit));
+                       return true;
+               }
+               p += size;
+       }
+
+       return false;
+}
+
+/* Add "_<remote_name>.<number>" suffix to path or filename.
+ *
+ * Success: return 0
+ * Failure: set errno, path NULL, return -1
+ */
+
+static int alloc_append_client_suffix(vfs_handle_struct *handle,
+                                     char **path)
+{
+       int status = 0;
+       uintmax_t number;
+       const char *clientid;
+       struct um_config_data *config;
+
+       DEBUG(10, ("Entering with path '%s'\n", *path));
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct um_config_data,
+                               return -1);
+
+       (void)get_digit_group(*path, &number);
+
+       switch (config->clientid) {
+
+       case UM_CLIENTID_IP:
+               clientid = tsocket_address_inet_addr_string(
+                       handle->conn->sconn->remote_address, talloc_tos());
+               if (clientid == NULL) {
+                       errno = ENOMEM;
+                       status = -1;
+                       goto err;
+               }
+               break;
+
+       case UM_CLIENTID_HOSTNAME:
+               clientid = get_remote_machine_name();
+               break;
+
+       case UM_CLIENTID_NAME:
+       default:
+               clientid = get_current_username();
+               break;
+       }
+
+       *path = talloc_asprintf_append(*path, "_%s.%ju",
+                                      clientid, number);
+       if (*path == NULL) {
+               DEBUG(1, ("alloc_append_client_suffix "
+                                    "out of memory\n"));
+               errno = ENOMEM;
+               status = -1;
+               goto err;
+       }
+       DEBUG(10, ("Leaving with *path '%s'\n", *path));
+err:
+       return status;
+}
+
+/* Returns true if the file or directory begins with the appledouble
+ * prefix.
+ */
+static bool is_apple_double(const char* fname)
+{
+       bool ret = false;
+
+       DEBUG(10, ("Entering with fname '%s'\n", fname));
+
+       if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
+               ret = true;
+       }
+       DEBUG(10, ("Leaving with ret '%s'\n",
+                             ret == true ? "true" : "false"));
+       return ret;
+}
+
+static bool starts_with_media_dir(const char* media_dirname,
+                                 size_t media_dirname_len,
+                                 const char *path)
+{
+       bool ret = false;
+       const char *path_start = path;
+
+       DEBUG(10, ("Entering with media_dirname '%s' "
+                             "path '%s'\n", media_dirname, path));
+
+       /* Sometimes Samba gives us "./OMFI MediaFiles". */
+       if (strnequal(path, "./", 2)) {
+               path_start += 2;
+       }
+
+       if (strnequal(media_dirname, path_start, media_dirname_len)
+           &&
+           ((path_start[media_dirname_len] == '\0') ||
+            (path_start[media_dirname_len] == '/'))) {
+               ret = true;
+       }
+
+       DEBUG(10, ("Leaving with ret '%s'\n",
+                             ret == true ? "true" : "false"));
+       return ret;
+}
+
+/*
+ * Returns true if the file or directory referenced by the path is ONE
+ * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
+ * directory
+ */
+static bool is_in_media_dir(const char *path)
+{
+       int transition_count = 0;
+       const char *path_start = path;
+       const char *p;
+       const char *media_dirname;
+       size_t media_dirname_len;
+
+       DEBUG(10, ("Entering with path'%s' ", path));
+
+       /* Sometimes Samba gives us "./OMFI MediaFiles". */
+       if (strnequal(path, "./", 2)) {
+               path_start += 2;
+       }
+
+       if (strnequal(path_start, AVID_MXF_DIRNAME, AVID_MXF_DIRNAME_LEN)) {
+               media_dirname = AVID_MXF_DIRNAME;
+               media_dirname_len = AVID_MXF_DIRNAME_LEN;
+       } else if (strnequal(path_start,
+                            OMFI_MEDIAFILES_DIRNAME,
+                            OMFI_MEDIAFILES_DIRNAME_LEN)) {
+               media_dirname = OMFI_MEDIAFILES_DIRNAME;
+               media_dirname_len = OMFI_MEDIAFILES_DIRNAME_LEN;
+       } else {
+               return false;
+       }
+
+       if (path_start[media_dirname_len] == '\0') {
+               goto out;
+       }
+
+       p = path_start + media_dirname_len + 1;
+
+       while (true) {
+               if (*p == '\0' || *p == '/') {
+                       if (strnequal(p - 3, "/..", 3)) {
+                               transition_count--;
+                       } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
+                               transition_count++;
+                       }
+               }
+               if (*p == '\0') {
+                       break;
+               }
+               p++;
+       }
+
+out:
+       DEBUG(10, ("Going out with transition_count '%i'\n",
+                             transition_count));
+       if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
+           ||
+           ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
+               return true;
+       }
+       else return false;
+}
+
+/*
+ * Returns true if the file or directory referenced by the path is
+ * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
+ * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
+ * are assumed to be in the root directory, which is generally a safe
+ * assumption in the fixed-path world of Avid.
+ */
+static bool is_in_media_files(const char *path)
+{
+       bool ret = false;
+
+       DEBUG(10, ("Entering with path '%s'\n", path));
+
+       if (starts_with_media_dir(AVID_MXF_DIRNAME,
+                                 AVID_MXF_DIRNAME_LEN, path) ||
+           starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
+                                 OMFI_MEDIAFILES_DIRNAME_LEN, path)) {
+               ret = true;
+       }
+       DEBUG(10, ("Leaving with ret '%s'\n",
+                             ret == true ? "true" : "false"));
+       return ret;
+}
+
+
+/* Add client suffix to "pure-number" path.
+ *
+ * Caller must free newPath.
+ *
+ * Success: return 0
+ * Failure: set errno, newPath NULL, return -1
+ */
+static int alloc_get_client_path(vfs_handle_struct *handle,
+                                TALLOC_CTX *ctx,
+                                const char *path_in,
+                                char **path_out)
+{
+       int status = 0;
+       char *p;
+       char *digits;
+       size_t digits_len;
+       uintmax_t number;
+
+       *path_out = talloc_strdup(ctx, path_in);
+        if (*path_out == NULL) {
+               DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
+               return -1;
+       }
+
+       (void)get_digit_group(*path_out, &number);
+
+       digits = talloc_asprintf(NULL, "%ju", number);
+        if (digits == NULL) {
+               DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
+               return -1;
+       }
+       digits_len = strlen(digits);
+
+       p = strstr_m(path_in, digits);
+       if ((p)
+           &&
+           ((p[digits_len] == '\0') || (p[digits_len] == '/'))
+           &&
+           (((p - path_in > 0) && (p[-1] == '/'))
+            ||
+            (((p - path_in) > APPLE_DOUBLE_PREFIX_LEN)
+             &&
+             is_apple_double(p - APPLE_DOUBLE_PREFIX_LEN)
+             &&
+             (p[-(APPLE_DOUBLE_PREFIX_LEN + 1)] == '/'))))
+       {
+               (*path_out)[p - path_in + digits_len] = '\0';
+
+               status = alloc_append_client_suffix(handle, path_out);
+               if (status != 0) {
+                       goto out;
+               }
+
+                *path_out = talloc_strdup_append(*path_out, p + digits_len);
+               if (*path_out == NULL) {
+                       DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
+                       status = -1;
+                       goto out;
+               }
+       }
+out:
+       /* path_out must be freed in caller. */
+       DEBUG(10, ("Result:'%s'\n", *path_out));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
+                                     TALLOC_CTX *ctx,
+                                     const struct smb_filename *smb_fname,
+                                     struct smb_filename **client_fname)
+{
+       int status ;
+
+       DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
+                  smb_fname->base_name));
+
+       *client_fname = cp_smb_filename(ctx, smb_fname);
+       if (*client_fname == NULL) {
+               DEBUG(1, ("cp_smb_filename returned NULL\n"));
+               return -1;
+       }
+       status = alloc_get_client_path(handle, ctx,
+                                      smb_fname->base_name,
+                                      &(*client_fname)->base_name);
+       if (status != 0) {
+               return -1;
+       }
+
+       DEBUG(10, ("Leaving with (*client_fname)->base_name "
+                  "'%s'\n", (*client_fname)->base_name));
+
+       return 0;
+}
+
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
+                                        TALLOC_CTX *ctx,
+                                        char **path,
+                                        const char *suffix_number)
+{
+       int status;
+
+       DEBUG(10, ("Entering with suffix_number '%s'\n",
+                  suffix_number));
+
+       *path = talloc_strdup(ctx, suffix_number);
+       if (*path == NULL) {
+               DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
+               return -1;
+       }
+       status = alloc_append_client_suffix(handle, path);
+       if (status != 0) {
+               return -1;
+       }
+
+       DEBUG(10, ("Leaving with *path '%s'\n", *path));
+
+       return 0;
+}
+
+static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
+                                   const char *fname,
+                                   struct um_dirinfo_struct **di_result)
+{
+       int status = 0;
+       char *digits;
+       uintmax_t number;
+       struct um_dirinfo_struct *dip;
+
+       DEBUG(10, ("Entering with fname '%s'\n", fname));
+
+       *di_result = talloc(NULL, struct um_dirinfo_struct);
+       if (*di_result == NULL) {
+               goto err;
+       }
+       dip = *di_result;
+
+       dip->dirpath = talloc_strdup(dip, fname);
+       if (dip->dirpath == NULL) {
+               goto err;
+       }
+
+       if (!is_in_media_files(fname)) {
+               dip->isInMediaFiles = false;
+               dip->clientPath = NULL;
+               dip->clientSubDirname = NULL;
+               goto out;
+       }
+
+       dip->isInMediaFiles = true;
+
+       (void)get_digit_group(fname, &number);
+       digits = talloc_asprintf(talloc_tos(), "%ju", number);
+       if (digits == NULL) {
+               goto err;
+       }
+
+       status = alloc_set_client_dirinfo_path(handle, dip,
+                                              &dip->clientSubDirname,
+                                              digits);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = alloc_get_client_path(handle, dip, fname,
+                                      &dip->clientPath);
+       if (status != 0 || dip->clientPath == NULL) {
+               goto err;
+       }
+
+out:
+       DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
+                             "(*dirInfo)->clientPath '%s'\n",
+                             dip->dirpath, dip->clientPath));
+       return status;
+
+err:
+       DEBUG(1, ("Failing with fname '%s'\n", fname));
+       TALLOC_FREE(*di_result);
+       status = -1;
+       errno = ENOMEM;
+       return status;
+}
+
+/**********************************************************************
+ * VFS functions
+ **********************************************************************/
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int um_statvfs(struct vfs_handle_struct *handle,
+                     const char *path,
+                     struct vfs_statvfs_struct *statbuf)
+{
+       int status;
+       char *clientPath = NULL;
+
+       DEBUG(10, ("Entering with path '%s'\n", path));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &clientPath);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_STATVFS(handle, clientPath, statbuf);
+err:
+       TALLOC_FREE(clientPath);
+       DEBUG(10, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+/* Success: return a um_dirinfo_struct cast as a DIR
+ * Failure: set errno, return NULL
+ */
+static DIR *um_opendir(vfs_handle_struct *handle,
+                      const char *fname,
+                      const char *mask,
+                      uint32 attr)
+{
+       struct um_dirinfo_struct *dirInfo;
+
+       DEBUG(10, ("Entering with fname '%s'\n", fname));
+
+       if (alloc_set_client_dirinfo(handle, fname, &dirInfo)) {
+               goto err;
+       }
+
+       if (!dirInfo->isInMediaFiles) {
+               dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
+                       handle, fname, mask, attr);
+       } else {
+               dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
+                       handle, dirInfo->clientPath, mask, attr);
+       }
+
+       if (dirInfo->dirstream == NULL) {
+               goto err;
+       }
+
+       DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
+                             "dirInfo->clientPath '%s'\n",
+                             dirInfo->dirpath,
+                             dirInfo->clientPath));
+       return (DIR*)dirInfo;
+
+err:
+       DEBUG(1, ("Failing with fname '%s'\n", fname));
+       TALLOC_FREE(dirInfo);
+       return NULL;
+}
+
+static DIR *um_fdopendir(vfs_handle_struct *handle,
+                        files_struct *fsp,
+                        const char *mask,
+                        uint32 attr)
+{
+       struct um_dirinfo_struct *dirInfo = NULL;
+       DIR *dirstream;
+
+       DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
+                  fsp->fsp_name->base_name));
+
+       dirstream = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
+       if (!dirstream) {
+               goto err;
+       }
+
+       if (alloc_set_client_dirinfo(handle,
+                                    fsp->fsp_name->base_name,
+                                    &dirInfo)) {
+               goto err;
+       }
+
+       dirInfo->dirstream = dirstream;
+
+       if (!dirInfo->isInMediaFiles) {
+               /*
+                * FIXME: this is the original code, something must be
+                * missing here, but what? -slow
+                */
+               goto out;
+       }
+
+out:
+       DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
+                  "dirInfo->clientPath '%s', "
+                  "fsp->fsp_name->st.st_ex_mtime %s",
+                  dirInfo->dirpath,
+                  dirInfo->clientPath,
+                  ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
+       return (DIR *) dirInfo;
+
+err:
+       DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
+                 fsp->fsp_name->base_name));
+       TALLOC_FREE(dirInfo);
+       return NULL;
+}
+
+/*
+ * skip own suffixed directory
+ * replace own suffixed directory with non suffixed.
+ *
+ * Success: return dirent
+ * End of data: return NULL
+ * Failure: set errno, return NULL
+ */
+static struct dirent *um_readdir(vfs_handle_struct *handle,
+                                DIR *dirp,
+                                SMB_STRUCT_STAT *sbuf)
+{
+       um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
+       struct dirent *d = NULL;
+       int skip;
+
+       DEBUG(10, ("dirInfo->dirpath '%s', "
+                  "dirInfo->clientPath '%s', "
+                  "dirInfo->isInMediaFiles '%s', "
+                  "dirInfo->clientSubDirname '%s'\n",
+                  dirInfo->dirpath,
+                  dirInfo->clientPath,
+                  dirInfo->isInMediaFiles ? "true" : "false",
+                  dirInfo->clientSubDirname));
+
+       if (!dirInfo->isInMediaFiles) {
+               return SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
+       }
+
+       do {
+               const char* dname;
+               bool isAppleDouble;
+               char *digits;
+               size_t digits_len;
+               uintmax_t number;
+
+               skip = false;
+               d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
+
+               if (d == NULL) {
+                       break;
+               }
+
+               /* ignore apple double prefix for logic below */
+               if (is_apple_double(d->d_name)) {
+                       dname = &d->d_name[APPLE_DOUBLE_PREFIX_LEN];
+                       isAppleDouble = true;
+               } else {
+                       dname = d->d_name;
+                       isAppleDouble = false;
+               }
+
+               DEBUG(10, ("dname = '%s'\n", dname));
+
+               (void)get_digit_group(dname, &number);
+               digits = talloc_asprintf(talloc_tos(), "%ju", number);
+               if (digits == NULL) {
+                       DEBUG(1, ("out of memory"));
+                       goto err;
+               }
+               digits_len = strlen(digits);
+
+               if (alloc_set_client_dirinfo_path(handle,
+                                                 dirInfo,
+                                                 &((dirInfo)->clientSubDirname),
+                                                 digits)) {
+                       goto err;
+               }
+
+               /*
+                * If set to "true", vfs shows digits-only
+                * non-suffixed subdirectories.  Normally, such
+                * subdirectories can exists only in non-media
+                * directories, so we set it to "false".  Otherwise,
+                * if we have such subdirectories (probably created
+                * over not "unityed" connection), it can be little
+                * bit confusing.
+                */
+               if (strequal(dname, digits)) {
+                       skip = false;
+               } else if (strequal(dname, dirInfo->clientSubDirname)) {
+                       /*
+                        * Remove suffix of this client's suffixed
+                        * subdirectories
+                        */
+                       if (isAppleDouble) {
+                               d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
+                       } else {
+                               d->d_name[digits_len] = '\0';
+                       }
+               } else if (strnequal(digits, dname, digits_len)) {
+                       /*
+                        * Set to false to see another clients subdirectories
+                        */
+                       skip = false;
+               }
+       } while (skip);
+
+       DEBUG(10, ("Leaving um_readdir\n"));
+       return d;
+err:
+       TALLOC_FREE(dirInfo);
+       return NULL;
+}
+
+static void um_seekdir(vfs_handle_struct *handle,
+                      DIR *dirp,
+                      long offset)
+{
+       DEBUG(10, ("Entering and leaving um_seekdir\n"));
+       SMB_VFS_NEXT_SEEKDIR(handle,
+                            ((um_dirinfo_struct*)dirp)->dirstream, offset);
+}
+
+static long um_telldir(vfs_handle_struct *handle,
+                      DIR *dirp)
+{
+       DEBUG(10, ("Entering and leaving um_telldir\n"));
+       return SMB_VFS_NEXT_TELLDIR(handle,
+                                   ((um_dirinfo_struct*)dirp)->dirstream);
+}
+
+static void um_rewinddir(vfs_handle_struct *handle,
+                        DIR *dirp)
+{
+       DEBUG(10, ("Entering and leaving um_rewinddir\n"));
+       SMB_VFS_NEXT_REWINDDIR(handle,
+                              ((um_dirinfo_struct*)dirp)->dirstream);
+}
+
+static int um_mkdir(vfs_handle_struct *handle,
+                   const char *path,
+                   mode_t mode)
+{
+       int status;
+       char *clientPath;
+       TALLOC_CTX *ctx;
+
+
+       DEBUG(10, ("Entering with path '%s'\n", path));
+
+       if (!is_in_media_files(path) || !is_in_media_dir(path)) {
+               return SMB_VFS_NEXT_MKDIR(handle, path, mode);
+       }
+
+       clientPath = NULL;
+       ctx = talloc_tos();
+
+       if ((status = alloc_get_client_path(handle, ctx,
+                                           path,
+                                           &clientPath))) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_MKDIR(handle, clientPath, mode);
+err:
+       TALLOC_FREE(clientPath);
+       DEBUG(10, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+static int um_rmdir(vfs_handle_struct *handle,
+                   const char *path)
+{
+       int status;
+       char *clientPath;
+       TALLOC_CTX *ctx;
+
+
+       DEBUG(10, ("Entering with path '%s'\n", path));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_RMDIR(handle, path);
+       }
+
+       clientPath = NULL;
+       ctx = talloc_tos();
+
+       if ((status = alloc_get_client_path(handle, ctx,
+                                           path,
+                                           &clientPath))) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_RMDIR(handle, clientPath);
+err:
+       TALLOC_FREE(clientPath);
+       DEBUG(10, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+static int um_closedir(vfs_handle_struct *handle,
+                      DIR *dirp)
+{
+       DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
+
+       TALLOC_FREE(dirp);
+
+       return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
+}
+
+static void um_init_search_op(vfs_handle_struct *handle,
+                             DIR *dirp)
+{
+       DEBUG(10, ("Entering and leaving um_init_search_op\n"));
+
+       SMB_VFS_NEXT_INIT_SEARCH_OP(handle,
+                                   ((um_dirinfo_struct*)dirp)->dirstream);
+}
+
+static int um_open(vfs_handle_struct *handle,
+                  struct smb_filename *smb_fname,
+                  files_struct *fsp,
+                  int flags,
+                  mode_t mode)
+{
+       int ret;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
+                             smb_fname->base_name));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       }
+
+       if (alloc_get_client_smb_fname(handle, talloc_tos(),
+                                      smb_fname,
+                                      &client_fname)) {
+               ret = -1;
+               goto err;
+       }
+
+       /*
+        * FIXME:
+        * What about fsp->fsp_name?  We also have to get correct stat
+        * info into fsp and smb_fname for DB files, don't we?
+        */
+
+       DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
+                  "smb_fname->st.st_ex_mtime %s"
+                  "fsp->fsp_name->st.st_ex_mtime %s",
+                             smb_fname->base_name,
+                             ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
+                             ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
+
+       ret = SMB_VFS_NEXT_OPEN(handle, client_fname, fsp, flags, mode);
+err:
+       TALLOC_FREE(client_fname);
+       DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
+                             smb_fname->base_name));
+       return ret;
+}
+
+static NTSTATUS um_create_file(vfs_handle_struct *handle,
+                              struct smb_request *req,
+                              uint16_t root_dir_fid,
+                              struct smb_filename *smb_fname,
+                              uint32_t access_mask,
+                              uint32_t share_access,
+                              uint32_t create_disposition,
+                              uint32_t create_options,
+                              uint32_t file_attributes,
+                              uint32_t oplock_request,
+                              struct smb2_lease *lease,
+                              uint64_t allocation_size,
+                              uint32_t private_flags,
+                              struct security_descriptor *sd,
+                              struct ea_list *ea_list,
+                              files_struct **result_fsp,
+                              int *pinfo,
+                              const struct smb2_create_blobs *in_context_blobs,
+                              struct smb2_create_blobs *out_context_blobs)
+{
+       NTSTATUS status;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
+                  smb_fname->base_name));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_CREATE_FILE(
+                       handle,
+                       req,
+                       root_dir_fid,
+                       smb_fname,
+                       access_mask,
+                       share_access,
+                       create_disposition,
+                       create_options,
+                       file_attributes,
+                       oplock_request,
+                       lease,
+                       allocation_size,
+                       private_flags,
+                       sd,
+                       ea_list,
+                       result_fsp,
+                       pinfo,
+                       in_context_blobs,
+                       out_context_blobs);
+       }
+
+       if (alloc_get_client_smb_fname(handle, talloc_tos(),
+                                      smb_fname,
+                                      &client_fname)) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /*
+        * FIXME:
+        * This only creates files, so we don't have to worry about
+        * our fake directory stat'ing here.  But we still need to
+        * route stat calls for DB files properly, right?
+        */
+       status = SMB_VFS_NEXT_CREATE_FILE(
+               handle,
+               req,
+               root_dir_fid,
+               client_fname,
+               access_mask,
+               share_access,
+               create_disposition,
+               create_options,
+               file_attributes,
+               oplock_request,
+               lease,
+               allocation_size,
+               private_flags,
+               sd,
+               ea_list,
+               result_fsp,
+               pinfo,
+               in_context_blobs,
+               out_context_blobs);
+err:
+       TALLOC_FREE(client_fname);
+       DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
+                  "smb_fname->st.st_ex_mtime %s"
+                  " fsp->fsp_name->st.st_ex_mtime %s",
+                  smb_fname->base_name,
+                  ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
+                  (*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
+                  ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
+                  "No fsp time\n"));
+       return status;
+}
+
+static int um_rename(vfs_handle_struct *handle,
+                    const struct smb_filename *smb_fname_src,
+                    const struct smb_filename *smb_fname_dst)
+{
+       int status;
+       struct smb_filename *src_client_fname = NULL;
+       struct smb_filename *dst_client_fname = NULL;
+
+       DEBUG(10, ("Entering with "
+                  "smb_fname_src->base_name '%s', "
+                  "smb_fname_dst->base_name '%s'\n",
+                  smb_fname_src->base_name,
+                  smb_fname_dst->base_name));
+
+       if (!is_in_media_files(smb_fname_src->base_name)
+           &&
+           !is_in_media_files(smb_fname_dst->base_name)) {
+               return SMB_VFS_NEXT_RENAME(handle, smb_fname_src,
+                                          smb_fname_dst);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname_src,
+                                           &src_client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname_dst,
+                                           &dst_client_fname);
+
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_RENAME(handle, src_client_fname,
+                                    dst_client_fname);
+err:
+       TALLOC_FREE(dst_client_fname);
+       TALLOC_FREE(src_client_fname);
+       DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
+                  " smb_fname_dst->base_name '%s'\n",
+                  smb_fname_src->base_name,
+                  smb_fname_dst->base_name));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int um_stat(vfs_handle_struct *handle,
+                  struct smb_filename *smb_fname)
+{
+       int status = 0;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
+                  smb_fname->base_name));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_STAT(handle, smb_fname);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname,
+                                           &client_fname);
+       if (status != 0) {
+               goto err;
+       }
+       DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
+                  client_fname->base_name));
+
+       status = SMB_VFS_NEXT_STAT(handle, client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       /*
+        * Unlike functions with const smb_filename, we have to modify
+        * smb_fname itself to pass our info back up.
+        */
+       DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
+                  smb_fname->base_name, client_fname->base_name));
+       smb_fname->st = client_fname->st;
+
+err:
+       TALLOC_FREE(client_fname);
+       DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
+                  ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+static int um_lstat(vfs_handle_struct *handle,
+                   struct smb_filename *smb_fname)
+{
+       int status = 0;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
+                  smb_fname->base_name));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+       }
+
+       client_fname = NULL;
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname,
+                                           &client_fname);
+       if (status != 0) {
+               goto err;
+       }
+       status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       smb_fname->st = client_fname->st;
+
+err:
+       TALLOC_FREE(client_fname);
+       DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
+                  ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+static int um_fstat(vfs_handle_struct *handle,
+                   files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+       int status = 0;
+
+       DEBUG(10, ("Entering with fsp->fsp_name->base_name "
+                  "'%s'\n", fsp_str_dbg(fsp)));
+
+       status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+       if (status != 0) {
+               goto out;
+       }
+
+       if ((fsp->fsp_name == NULL) ||
+           !is_in_media_files(fsp->fsp_name->base_name)) {
+               goto out;
+       }
+
+       status = um_stat(handle, fsp->fsp_name);
+       if (status != 0) {
+               goto out;
+       }
+
+       *sbuf = fsp->fsp_name->st;
+
+out:
+       DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
+                  fsp->fsp_name != NULL ?
+                  ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) : "0"));
+       return status;
+}
+
+static int um_unlink(vfs_handle_struct *handle,
+                    const struct smb_filename *smb_fname)
+{
+       int status;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering um_unlink\n"));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname,
+                                           &client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_UNLINK(handle, client_fname);
+
+err:
+       TALLOC_FREE(client_fname);
+       return status;
+}
+
+static int um_chmod(vfs_handle_struct *handle,
+                   const char *path,
+                   mode_t mode)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_chmod\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHMOD(handle, client_path, mode);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_chown(vfs_handle_struct *handle,
+                   const char *path,
+                   uid_t uid,
+                   gid_t gid)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_chown\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHOWN(handle, client_path, uid, gid);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_lchown(vfs_handle_struct *handle,
+                    const char *path,
+                    uid_t uid,
+                    gid_t gid)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_lchown\n"));
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_LCHOWN(handle, path, uid, gid);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_LCHOWN(handle, client_path, uid, gid);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_chdir(vfs_handle_struct *handle,
+                   const char *path)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_chdir\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_CHDIR(handle, path);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHDIR(handle, client_path);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_ntimes(vfs_handle_struct *handle,
+                    const struct smb_filename *smb_fname,
+                    struct smb_file_time *ft)
+{
+       int status;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering um_ntimes\n"));
+
+       if (!is_in_media_files(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           smb_fname, &client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_NTIMES(handle, client_fname, ft);
+
+err:
+       TALLOC_FREE(client_fname);
+       return status;
+}
+
+static int um_symlink(vfs_handle_struct *handle,
+                     const char *oldpath,
+                     const char *newpath)
+{
+       int status;
+       char *old_client_path = NULL;
+       char *new_client_path = NULL;
+
+       DEBUG(10, ("Entering um_symlink\n"));
+
+       if (!is_in_media_files(oldpath) && !is_in_media_files(newpath)) {
+               return SMB_VFS_NEXT_SYMLINK(handle, oldpath, newpath);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      oldpath, &old_client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      newpath, &new_client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYMLINK(handle,
+                                     old_client_path,
+                                     new_client_path);
+
+err:
+       TALLOC_FREE(new_client_path);
+       TALLOC_FREE(old_client_path);
+       return status;
+}
+
+static int um_readlink(vfs_handle_struct *handle,
+                      const char *path,
+                      char *buf,
+                      size_t bufsiz)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_readlink\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_READLINK(handle, path, buf, bufsiz);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_READLINK(handle, client_path, buf, bufsiz);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_link(vfs_handle_struct *handle,
+                  const char *oldpath,
+                  const char *newpath)
+{
+       int status;
+       char *old_client_path = NULL;
+       char *new_client_path = NULL;
+
+       DEBUG(10, ("Entering um_link\n"));
+       if (!is_in_media_files(oldpath) && !is_in_media_files(newpath)) {
+               return SMB_VFS_NEXT_LINK(handle, oldpath, newpath);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      oldpath, &old_client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      newpath, &new_client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_LINK(handle, old_client_path, new_client_path);
+
+err:
+       TALLOC_FREE(new_client_path);
+       TALLOC_FREE(old_client_path);
+       return status;
+}
+
+static int um_mknod(vfs_handle_struct *handle,
+                   const char *pathname,
+                   mode_t mode,
+                   SMB_DEV_T dev)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_mknod\n"));
+       if (!is_in_media_files(pathname)) {
+               return SMB_VFS_NEXT_MKNOD(handle, pathname, mode, dev);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      pathname, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_MKNOD(handle, client_path, mode, dev);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static char *um_realpath(vfs_handle_struct *handle,
+                        const char *path)
+{
+       char *buf = NULL;
+       char *client_path = NULL;
+       int status;
+
+       DEBUG(10, ("Entering um_realpath\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_REALPATH(handle, path);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       buf = SMB_VFS_NEXT_REALPATH(handle, client_path);
+
+err:
+       TALLOC_FREE(client_path);
+       return buf;
+}
+
+static int um_chflags(vfs_handle_struct *handle,
+                     const char *path,
+                     unsigned int flags)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_chflags\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_CHFLAGS(handle, path, flags);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHFLAGS(handle, client_path, flags);
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static NTSTATUS um_streaminfo(struct vfs_handle_struct *handle,
+                             struct files_struct *fsp,
+                             const char *fname,
+                             TALLOC_CTX *ctx,
+                             unsigned int *num_streams,
+                             struct stream_struct **streams)
+{
+       NTSTATUS status;
+       char *client_path = NULL;
+       int ret;
+
+       DEBUG(10, ("Entering um_streaminfo\n"));
+
+       if (!is_in_media_files(fname)) {
+               return SMB_VFS_NEXT_STREAMINFO(handle, fsp, fname,
+                                              ctx, num_streams, streams);
+       }
+
+       ret = alloc_get_client_path(handle, talloc_tos(),
+                                   fname, &client_path);
+       if (ret != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /*
+        * This only works on files, so we don't have to worry about
+        * our fake directory stat'ing here.  But what does this
+        * function do, exactly?  Does it need extra modifications for
+        * the Avid stuff?
+        */
+       status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, client_path,
+                                        ctx, num_streams, streams);
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+/*
+ * Ignoring get_real_filename function because the default doesn't do
+ * anything.
+ */
+
+static NTSTATUS um_get_nt_acl(vfs_handle_struct *handle,
+                             const char *name,
+                             uint32 security_info,
+                             TALLOC_CTX *mem_ctx,
+                             struct security_descriptor **ppdesc)
+{
+       NTSTATUS status;
+       char *client_path = NULL;
+       int ret;
+
+       DEBUG(10, ("Entering um_get_nt_acl\n"));
+
+       if (!is_in_media_files(name)) {
+               return SMB_VFS_NEXT_GET_NT_ACL(handle, name,
+                                              security_info,
+                                              mem_ctx, ppdesc);
+       }
+
+       ret = alloc_get_client_path(handle, talloc_tos(),
+                                   name, &client_path);
+       if (ret != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_GET_NT_ACL(handle, client_path,
+                                        security_info,
+                                        mem_ctx, ppdesc);
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_chmod_acl(vfs_handle_struct *handle,
+                       const char *path,
+                       mode_t mode)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_chmod_acl\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_CHMOD_ACL(handle, path, mode);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHMOD_ACL(handle, client_path, mode);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static SMB_ACL_T um_sys_acl_get_file(vfs_handle_struct *handle,
+                                    const char *path_p,
+                                    SMB_ACL_TYPE_T type,
+                                    TALLOC_CTX *mem_ctx)
+{
+       SMB_ACL_T ret;
+       char *client_path = NULL;
+       int status;
+
+       DEBUG(10, ("Entering um_sys_acl_get_file\n"));
+
+       if (!is_in_media_files(path_p)) {
+               return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p,
+                                                    type, mem_ctx);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path_p, &client_path);
+       if (status != 0) {
+               ret = NULL;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, client_path, type, mem_ctx);
+
+err:
+       TALLOC_FREE(client_path);
+       return ret;
+}
+
+static int um_sys_acl_set_file(vfs_handle_struct *handle,
+                              const char *name,
+                              SMB_ACL_TYPE_T acltype,
+                              SMB_ACL_T theacl)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_sys_acl_set_file\n"));
+
+       if (!is_in_media_files(name)) {
+               return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name,
+                                                    acltype, theacl);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      name, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, client_path,
+                                              acltype, theacl);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_sys_acl_delete_def_file(vfs_handle_struct *handle,
+                                     const char *path)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, path);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                           path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, client_path);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static ssize_t um_getxattr(struct vfs_handle_struct *handle,
+                          const char *path,
+                          const char *name,
+                          void *value,
+                          size_t size)
+{
+       ssize_t ret;
+       char *client_path = NULL;
+       int status;
+
+       DEBUG(10, ("Entering um_getxattr\n"));
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_GETXATTR(handle, path, name, value, size);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               ret = -1;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_GETXATTR(handle, client_path, name, value, size);
+err:
+       TALLOC_FREE(client_path);
+       return ret;
+}
+
+static ssize_t um_listxattr(struct vfs_handle_struct *handle,
+                           const char *path,
+                           char *list,
+                           size_t size)
+{
+       ssize_t ret;
+       char *client_path = NULL;
+       int status;
+
+       DEBUG(10, ("Entering um_listxattr\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_LISTXATTR(handle, path, list, size);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               ret = -1;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_LISTXATTR(handle, client_path, list, size);
+
+err:
+       TALLOC_FREE(client_path);
+       return ret;
+}
+
+static int um_removexattr(struct vfs_handle_struct *handle,
+                         const char *path,
+                         const char *name)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_removexattr\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_REMOVEXATTR(handle, path, name);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_REMOVEXATTR(handle, client_path, name);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static int um_setxattr(struct vfs_handle_struct *handle,
+                      const char *path,
+                      const char *name,
+                      const void *value,
+                      size_t size,
+                      int flags)
+{
+       int status;
+       char *client_path = NULL;
+
+       DEBUG(10, ("Entering um_setxattr\n"));
+
+       if (!is_in_media_files(path)) {
+               return SMB_VFS_NEXT_SETXATTR(handle, path, name, value,
+                                            size, flags);
+       }
+
+       status = alloc_get_client_path(handle, talloc_tos(),
+                                      path, &client_path);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SETXATTR(handle, client_path, name, value,
+                                      size, flags);
+
+err:
+       TALLOC_FREE(client_path);
+       return status;
+}
+
+static bool um_is_offline(struct vfs_handle_struct *handle,
+                         const struct smb_filename *fname,
+                         SMB_STRUCT_STAT *sbuf)
+{
+       bool ret;
+       struct smb_filename *client_fname = NULL;
+       int status;
+
+       DEBUG(10, ("Entering um_is_offline\n"));
+
+       if (!is_in_media_files(fname->base_name)) {
+               return SMB_VFS_NEXT_IS_OFFLINE(handle, fname, sbuf);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           fname, &client_fname);
+       if (status != 0) {
+               ret = false;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_IS_OFFLINE(handle, client_fname, sbuf);
+
+err:
+       TALLOC_FREE(client_fname);
+       return ret;
+}
+
+static int um_set_offline(struct vfs_handle_struct *handle,
+                         const struct smb_filename *fname)
+{
+       int status;
+       struct smb_filename *client_fname = NULL;
+
+       DEBUG(10, ("Entering um_set_offline\n"));
+
+       if (!is_in_media_files(fname->base_name)) {
+               return SMB_VFS_NEXT_SET_OFFLINE(handle, fname);
+       }
+
+       status = alloc_get_client_smb_fname(handle, talloc_tos(),
+                                           fname, &client_fname);
+       if (status != 0) {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SET_OFFLINE(handle, client_fname);
+
+err:
+       TALLOC_FREE(client_fname);
+       return status;
+}
+
+static int um_connect(vfs_handle_struct *handle,
+                        const char *service,
+                        const char *user)
+{
+       int rc;
+       struct um_config_data *config;
+       int enumval;
+
+       rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (rc != 0) {
+               return rc;
+       }
+
+       config = talloc_zero(handle->conn, struct um_config_data);
+       if (!config) {
+               DEBUG(1, ("talloc_zero() failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       enumval = lp_parm_enum(SNUM(handle->conn), UM_PARAM_TYPE_NAME,
+                              "clientid", um_clientid, UM_CLIENTID_NAME);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: type unknown\n",
+                         UM_PARAM_TYPE_NAME));
+               return -1;
+       }
+       config->clientid = (enum um_clientid)enumval;
+
+       SMB_VFS_HANDLE_SET_DATA(handle, config,
+                               NULL, struct um_config_data,
+                               return -1);
+
+       return 0;
+}
+
+/* VFS operations structure */
+
+static struct vfs_fn_pointers vfs_um_fns = {
+       .connect_fn = um_connect,
+
+       /* Disk operations */
+
+       .statvfs_fn = um_statvfs,
+
+       /* Directory operations */
+
+       .opendir_fn = um_opendir,
+       .fdopendir_fn = um_fdopendir,
+       .readdir_fn = um_readdir,
+       .seekdir_fn = um_seekdir,
+       .telldir_fn = um_telldir,
+       .rewind_dir_fn = um_rewinddir,
+       .mkdir_fn = um_mkdir,
+       .rmdir_fn = um_rmdir,
+       .closedir_fn = um_closedir,
+       .init_search_op_fn = um_init_search_op,
+
+       /* File operations */
+
+       .open_fn = um_open,
+       .create_file_fn = um_create_file,
+       .rename_fn = um_rename,
+       .stat_fn = um_stat,
+       .lstat_fn = um_lstat,
+       .fstat_fn = um_fstat,
+       .unlink_fn = um_unlink,
+       .chmod_fn = um_chmod,
+       .chown_fn = um_chown,
+       .lchown_fn = um_lchown,
+       .chdir_fn = um_chdir,
+       .ntimes_fn = um_ntimes,
+       .symlink_fn = um_symlink,
+       .readlink_fn = um_readlink,
+       .link_fn = um_link,
+       .mknod_fn = um_mknod,
+       .realpath_fn = um_realpath,
+       .chflags_fn = um_chflags,
+       .streaminfo_fn = um_streaminfo,
+
+       /* NT ACL operations. */
+
+       .get_nt_acl_fn = um_get_nt_acl,
+
+       /* POSIX ACL operations. */
+
+       .chmod_acl_fn = um_chmod_acl,
+
+       .sys_acl_get_file_fn = um_sys_acl_get_file,
+       .sys_acl_set_file_fn = um_sys_acl_set_file,
+       .sys_acl_delete_def_file_fn = um_sys_acl_delete_def_file,
+
+       /* EA operations. */
+       .getxattr_fn = um_getxattr,
+       .listxattr_fn = um_listxattr,
+       .removexattr_fn = um_removexattr,
+       .setxattr_fn = um_setxattr,
+
+       /* aio operations */
+
+       /* offline operations */
+       .is_offline_fn = um_is_offline,
+       .set_offline_fn = um_set_offline
+};
+
+NTSTATUS vfs_unityed_media_init(void);
+NTSTATUS vfs_unityed_media_init(void)
+{
+       NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+                                       "unityed_media", &vfs_um_fns);
+       if (!NT_STATUS_IS_OK(ret)) {
+               return ret;
+       }
+
+       vfs_um_debug_level = debug_add_class("unityed_media");
+
+       if (vfs_um_debug_level == -1) {
+               vfs_um_debug_level = DBGC_VFS;
+               DEBUG(1, ("unityed_media_init: Couldn't register custom "
+                         "debugging class.\n"));
+       }
+
+       return ret;
+}
index de4947b..e2d4fde 100644 (file)
@@ -443,6 +443,15 @@ bld.SAMBA3_MODULE('vfs_media_harmony',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_media_harmony'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_media_harmony'))
 
+bld.SAMBA3_MODULE('vfs_unityed_media',
+                 subsystem='vfs',
+                 source='vfs_unityed_media.c',
+                 allow_warnings=True,
+                 deps='samba-util',
+                 init_function='',
+                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_unityed_media'),
+                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_unityed_media'))
+
 bld.SAMBA3_MODULE('vfs_dfs_samba4',
                  subsystem='vfs',
                  source='vfs_dfs_samba4.c',
index 8e04c9e..b7e7dcc 100644 (file)
@@ -1572,7 +1572,7 @@ main() {
                                       auth_script vfs_readahead vfs_xattr_tdb vfs_posix_eadb
                                       vfs_streams_xattr vfs_streams_depot vfs_acl_xattr vfs_acl_tdb
                                       vfs_smb_traffic_analyzer vfs_preopen vfs_catia vfs_scannedonly
-                                     vfs_media_harmony vfs_fruit
+                                     vfs_media_harmony vfs_unityed_media vfs_fruit
                                      vfs_commit
                                      vfs_worm
                                       vfs_crossrename vfs_linux_xfs_sgid