media_harmony VFS module: Add and build by default.
authorAndrew Klaassen <clawsoon@yahoo.com>
Thu, 3 May 2012 17:39:53 +0000 (13:39 -0400)
committerBjoern Jacke <bj@sernet.de>
Tue, 7 Aug 2012 13:16:39 +0000 (15:16 +0200)
 - Supersedes previous patch.
 - Added various fixes for fake mtime functionality.
 - Now requires lp_cache_locked_write_times patch (bug 8912).
 - Removed various xattr functions to comply with recent VFS changes.
 - Changed SMB_STRUCT_DIR to DIR and SMB_STRUCT_DIRENT to struct dirent to comply with recent VFS changes.
 - Added manpage.
 - Added sample trigger_avid_update.py script.

Autobuild-User(master): Bj√∂rn Jacke <bj@sernet.de>
Autobuild-Date(master): Tue Aug  7 15:16:39 CEST 2012 on sn-devel-104

docs-xml/manpages-3/vfs_media_harmony.8.xml [new file with mode: 0644]
examples/scripts/vfs/media_harmony/trigger_avid_update.py [new file with mode: 0755]
source3/Makefile.in
source3/configure.in
source3/modules/vfs_media_harmony.c [new file with mode: 0644]
source3/wscript

diff --git a/docs-xml/manpages-3/vfs_media_harmony.8.xml b/docs-xml/manpages-3/vfs_media_harmony.8.xml
new file mode 100644 (file)
index 0000000..f24e700
--- /dev/null
@@ -0,0 +1,142 @@
+<?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_media_harmony</refentrytitle>
+       <manvolnum>8</manvolnum>
+       <refmiscinfo class="source">Samba</refmiscinfo>
+       <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+       <refmiscinfo class="version">3.6</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+       <refname>vfs_media_harmony</refname>
+       <refpurpose>Allow multiple Avid clients to share a network drive.</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>vfs objects = media_harmony</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>The <command>vfs_media_harmony</command> VFS module allows
+       Avid editorial workstations to share a network drive.  It does
+       this by:</para>
+       <orderedlist continuation="restarts" inheritnum="ignore" numeration="arabic">
+       <listitem><para>Giving each client their own copy of the Avid
+       msmMMOB.mdb and msmFMID.pmr files and Creating directories.</para></listitem>
+       <listitem><para>Allowing each client to explicitly control the
+       write time the Avid application sees on Avid media directories.</para></listitem>
+       </orderedlist>
+
+       <para>This module is stackable.</para>
+
+</refsect1>
+
+<refsect1>
+       <title>CONFIGURATION</title>
+
+       <para><command>vfs_media_harmony</command> automatically redirects
+       requests from clients for Avid database files or an Avid Creating
+       directory to a client-specific version of the file.  No
+       configuration beyond enabling the module is needed to get this
+       portion of its functionality working.</para>
+
+       <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>
+
+       <para>To allow each client to control when the Avid application
+       refreshes their Avid databases, create files for each client
+       and each Avid media directory with the name
+       [avid_dir_name]_[client_ip_address]_[client_username].
+       To trigger Avid database refreshes, update the write time on
+       those files.  See EXAMPLES.</para>
+
+       <para>It is also necessary for the <command>cache locked write times = no</command>
+       option to be set for clients to be able to control their Avid
+       media folder write times.</para>
+
+</refsect1>
+
+<refsect1>
+       <title>EXAMPLES</title>
+
+       <para>Enable media_harmony for Mac and Windows clients:</para>
+<programlisting>
+        <smbconfsection name="[avid_mac]"/>
+       <smbconfoption name="path">/avid</smbconfoption>
+       <smbconfoption name="vfs objects">media_harmony</smbconfoption>
+       <smbconfoption name="cache locked write times">no</smbconfoption>
+        <smbconfsection name="[avid_win]"/>
+       <smbconfoption name="path">/avid</smbconfoption>
+       <smbconfoption name="vfs objects">media_harmony</smbconfoption>
+       <smbconfoption name="cache locked write times">no</smbconfoption>
+       <smbconfoption name="veto files">/.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/</smbconfoption>
+       <smbconfoption name="delete veto files">yes</smbconfoption>
+</programlisting>
+
+       <para>Create the files that will allow users david and susan
+       to control their own Avid database refreshes:</para>
+<programlisting>
+       touch '/avid/OMFI MediaFiles_192.168.1.10_david' \
+               '/avid/OMFI MediaFiles_192.168.1.11_susan' \
+               '/avid/Avid MediaFiles/MXF/1_192.168.1.10_david' \
+               '/avid/Avid MediaFiles/MXF/1_192.168.1.11_susan'
+</programlisting>
+       <para>Trigger an Avid database refresh for user david:</para>
+<programlisting>
+       touch '/avid/OMFI MediaFiles_192.168.1.10_david' \
+               '/avid/Avid MediaFiles/MXF/1_192.168.1.10_david'
+</programlisting>
+
+       <para>If you have a large number of Avid media folders to manage,
+       you can give each editor a suitably modified version of
+       examples/scripts/vfs/media_harmony/trigger_avid_update.py to
+       create and update these files.</para>
+
+</refsect1>
+
+<refsect1>
+       <title>CAVEATS</title>
+
+       <para><command>vfs_media_harmony</command> is designed to work with
+       Avid editing applications that look in the Avid MediaFiles or
+       OMFI MediaFiles directories for media.  It is not designed to work
+       as expected in all circumstances for general use.  For example: It
+       is possible to open a client-specific file such as
+       msmMMOB.mdb_192.168.1.10_userx even though it doesn't show up
+       in a directory listing.</para>
+
+</refsect1>
+
+
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 3.2.7 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>
diff --git a/examples/scripts/vfs/media_harmony/trigger_avid_update.py b/examples/scripts/vfs/media_harmony/trigger_avid_update.py
new file mode 100755 (executable)
index 0000000..938b3d1
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+import os, socket, sys, stat
+
+######################################################################
+##
+##  trigger_avid_update.py for media_harmony VFS module.
+##
+##  Copyright (C) Andrew Klaassen      2012.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+##
+######################################################################
+
+
+#
+# Change avid_shares and ip_prefix as appropriate for your network.
+#
+
+avid_shares = (
+       '\\\\mediaharmony01\\project1\\',
+       '\\\\mediaharmony01\\project2\\',
+       '\\\\mediaharmony01\\project3\\',
+)
+
+ip_prefix = '192.168.1.'
+
+
+if __name__ == "__main__":
+       my_ips = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if ip[:len(ip_prefix)] == ip_prefix]
+       if not my_ips:
+               print 'No IP address found.  Aborting.'
+               dummy = raw_input("\nPress Enter to finish: ")
+               sys.exit()
+
+       my_ip = my_ips[0]
+       my_name = os.environ.get('USERNAME')
+
+       for avid_share in avid_shares:
+               media_dirs = []
+               omfi_dir = os.path.join(avid_share, 'OMFI MediaFiles')
+               if os.path.exists(omfi_dir):
+                       media_dirs.append(omfi_dir)
+               mxf_root = os.path.join(avid_share, 'Avid MediaFiles', 'MXF')
+               if os.path.exists(mxf_root):
+                       mxf_children = os.listdir(mxf_root)
+                       for child in mxf_children:
+                               fullpath = os.path.join(mxf_root, child)
+                               if os.path.isdir(fullpath):
+                                       media_dirs.append(fullpath)
+
+               for media_dir in media_dirs:
+
+                       print '\nChecking %s...' % media_dir
+
+                       fakepath = '%s_%s_%s' % (media_dir, my_ip, my_name)
+                       print '...fakepath: %s' % fakepath
+
+                       db = os.path.join(media_dir, 'msmMMOB.mdb')
+                       print '...Checking for %s' % db
+                       if os.path.exists(db):
+                               print '......found %s.' % db
+                               db_mtime = os.stat(db)[stat.ST_MTIME]
+                               newer_file = False
+                               for child in os.listdir(media_dir):
+                                       if child == 'msmMMOB.mdb' or child == 'msmFMID.pmr':
+                                               continue
+                                       child_mtime = os.stat(os.path.join(media_dir, child))[stat.ST_MTIME]
+                                       if child_mtime > db_mtime:
+                                               print '......found newer file %s' % child
+                                               newer_file = True
+                                               break
+                       else:
+                               print '......no %s.' % db
+                               newer_file = True
+
+                       if newer_file:
+                               utime = None # Sets to current time.
+                               print '...Setting fake mtime to NOW.  Will trigger re-index.'
+                       else:
+                               mtime = os.stat(media_dir)[stat.ST_MTIME]
+                               utime = (mtime, mtime)
+                               print '...Setting fake mtime to media_dir mtime.  No re-index.'
+
+                       if not os.path.exists(fakepath):
+                               tmp_fakepath = '%s.tmp' % fakepath
+                               open(tmp_fakepath, 'a').close()
+                               os.utime(tmp_fakepath, utime)
+                               os.rename(tmp_fakepath, fakepath)
+                       else:
+                               os.utime(fakepath, utime)
+
+       dummy = raw_input("\nPress Enter to finish: ")
index 1408614d164b2e115879154c78de742342f572da..ff0f1f4f407a658017efb2a85301b4e090fd2672 100644 (file)
@@ -902,6 +902,7 @@ VFS_SCANNEDONLY_OBJ = modules/vfs_scannedonly.o
 VFS_CROSSRENAME_OBJ = modules/vfs_crossrename.o
 VFS_LINUX_XFS_SGID_OBJ = modules/vfs_linux_xfs_sgid.o
 VFS_TIME_AUDIT_OBJ = modules/vfs_time_audit.o
+VFS_MEDIA_HARMONY_OBJ = modules/vfs_media_harmony.o
 
 PAM_ERRORS_OBJ = ../libcli/auth/pam_errors.o
 PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o $(PAM_ERRORS_OBJ)
@@ -2957,6 +2958,10 @@ bin/time_audit.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_TIME_AUDIT_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_TIME_AUDIT_OBJ)
 
+bin/media_harmony.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_MEDIA_HARMONY_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(VFS_MEDIA_HARMONY_OBJ)
+
 #########################################################
 ## IdMap NSS plugins
 
index bd21db916dbd0fb4fe00eab5bb6ad69f796d2ecc..44fd38dd9c55ca9bdb6639c6e59cd2d990b06d4c 100644 (file)
@@ -466,6 +466,7 @@ default_shared_modules="$default_shared_modules vfs_scannedonly"
 default_shared_modules="$default_shared_modules vfs_crossrename"
 default_shared_modules="$default_shared_modules vfs_linux_xfs_sgid"
 default_shared_modules="$default_shared_modules vfs_time_audit"
+default_shared_modules="$default_shared_modules vfs_media_harmony"
 default_shared_modules="$default_shared_modules idmap_autorid"
 default_shared_modules="$default_shared_modules idmap_tdb2"
 default_shared_modules="$default_shared_modules idmap_rid"
@@ -6575,6 +6576,7 @@ SMB_MODULE(vfs_scannedonly, \$(VFS_SCANNEDONLY_OBJ), "bin/scannedonly.$SHLIBEXT"
 SMB_MODULE(vfs_crossrename, \$(VFS_CROSSRENAME_OBJ), "bin/crossrename.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_linux_xfs_sgid, \$(VFS_LINUX_XFS_SGID_OBJ), "bin/linux_xfs_sgid.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_time_audit, \$(VFS_TIME_AUDIT_OBJ), "bin/time_audit.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_media_harmony, \$(VFS_MEDIA_HARMONY_OBJ), "bin/media_harmony.$SHLIBEXT", VFS)
 
 SMB_SUBSYSTEM(VFS,smbd/vfs.o)
 
diff --git a/source3/modules/vfs_media_harmony.c b/source3/modules/vfs_media_harmony.c
new file mode 100644 (file)
index 0000000..82beccc
--- /dev/null
@@ -0,0 +1,2438 @@
+/*
+ * $Id: media_harmony.c,v 1.1 2007/11/06 10:07:22 stuart_hc Exp $
+ *
+ * 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>
+ *
+ * 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.
+ */
+
+
+/*
+ * Media Harmony is a Samba VFS module that allows multiple AVID
+ * clients to share media. Each client sees their own copy of the
+ * AVID msmMMOB.mdb and msmFMID.pmr files and Creating directories.
+ *
+ * Add this module to the vfs objects option in your Samba share
+ * configuration.
+ * eg.
+ *
+ *   [avid_win]
+ *     path = /video
+ *     vfs objects = media_harmony
+ *     ...
+ *
+ * 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.
+ *
+ *
+ * Andrew Klaassen, 2012-03-14
+ * To prevent Avid clients from interrupting each other (via Avid's habit
+ * of launching a database refresh whenever it notices an mtime update
+ * on media directories, i.e. whenever one editor adds new material to a
+ * shared share), I've added code that causes stat information for anything
+ * directly under "Avid MediaFile/MXF" to be taken from
+ * dirname_clientaddr_clientuser if it exists.  These files ~aren't~
+ * hidden, unlike the client-suffixed database files.
+ *
+ * For example, stat information for
+ *     Avid MediaFiles/MXF/1
+ * will come from
+ *     Avid MediaFiles/MXF/1_192.168.1.10_dave
+ * for dave working on 192.168.1.10, but will come from
+ *     Avid MediaFile/MXF/1_192.168.1.11_susan
+ * for susan working on 192.168.1.11.  If those alternate
+ * directories don't exist, the user will get the actual directory's stat
+ * info.  When an editor wants to force a database refresh, they update
+ * the mtime on "their" file.  This will cause Avid
+ * on that client to see an updated mtime for "Avid MediaFiles/MXF/1",
+ * which will trigger an Avid database refresh just for that editor.
+ *
+ *
+ * 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. For example: it is possibly to open client specific
+ * files such as msmMMOB.mdb_192.168.1.10_userx even though is doesn't
+ * show up in a directory listing.
+ *
+ */
+
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
+#include "../smbd/globals.h"
+#include "auth.h"
+#include "../lib/tsocket/tsocket.h"
+
+#define MH_INFO_DEBUG 10
+#define MH_ERR_DEBUG 0
+
+static const char* MH_MODULE_NAME = "media_harmony";
+static const char* MDB_FILENAME = "msmMMOB.mdb";
+static const size_t MDB_FILENAME_LEN = 11;
+static const char* PMR_FILENAME = "msmFMID.pmr";
+static const size_t PMR_FILENAME_LEN = 11;
+static const char* CREATING_DIRNAME = "Creating";
+static const size_t CREATING_DIRNAME_LEN = 8;
+static const char* AVID_MEDIAFILES_DIRNAME = "Avid MediaFiles";
+static const size_t AVID_MEDIAFILES_DIRNAME_LEN = 15;
+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 const char* AVID_MXF_DIRNAME = "Avid MediaFiles/MXF";
+static const size_t AVID_MXF_DIRNAME_LEN = 19;
+
+static int vfs_mh_debug_level = DBGC_VFS;
+
+/* supplements the directory list stream */
+typedef struct mh_dirinfo_struct
+{
+       DIR* dirstream;
+       char *dirpath;
+       char *clientPath;
+       bool isInMediaFiles;
+       char *clientMDBFilename;
+       char *clientPMRFilename;
+       char *clientCreatingDirname;
+} mh_dirinfo_struct;
+
+
+/* Add "_<ip address>_<user name>" 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)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with *path '%s'\n", *path));
+
+       int status = 0;
+       char *raddr = NULL;
+
+       raddr = tsocket_address_inet_addr_string(
+                       handle->conn->sconn->remote_address, talloc_tos());
+       if (raddr == NULL)
+       {
+               errno = ENOMEM;
+               status = -1;
+               goto err;
+       }
+
+       /* talloc_asprintf_append uses talloc_realloc, which
+        * frees original 'path' memory so we don't have to.
+        */
+       *path = talloc_asprintf_append(*path, "_%s_%s",
+               raddr,
+               handle->conn->session_info->unix_info->sanitized_username);
+       if (*path == NULL)
+       {
+               DEBUG(MH_ERR_DEBUG, ("alloc_append_client_suffix "
+                                       "out of memory\n"));
+               errno = ENOMEM;
+               status = -1;
+               goto err;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Leaving with *path '%s'\n", *path));
+err:
+       TALLOC_FREE(raddr);
+       return status;
+}
+
+
+/* Returns True if the file or directory begins with the appledouble
+ * prefix.
+ */
+static bool is_apple_double(const char* fname)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with fname '%s'\n", fname));
+
+       bool ret = False;
+
+       if (strncmp(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)
+                       == 0)
+       {
+               ret = True;
+       }
+       DEBUG(MH_INFO_DEBUG, ("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)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with media_dirname '%s' "
+                               "path '%s'\n", media_dirname, path));
+
+       bool ret = False;
+       char* path_start;
+
+       /* Sometimes Samba gives us "./OMFI MediaFiles". */
+       if (strncmp(path, "./", 2) == 0)
+       {
+               path_start = &path[2];
+       }
+       else {
+               path_start = path;
+       }
+
+       if (strncmp(media_dirname, path_start, media_dirname_len) == 0
+                       &&
+               (
+                       path_start[media_dirname_len] == '\0'
+                       ||
+                       path_start[media_dirname_len] == '/'
+               )
+       )
+       {
+               ret = True;
+       }
+
+       DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
+                               ret == True ? "True" : "False"));
+       return ret;
+}
+
+/*
+ * 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)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
+
+       bool ret = False;
+
+       if (
+               starts_with_media_dir(AVID_MEDIAFILES_DIRNAME,
+                               AVID_MEDIAFILES_DIRNAME_LEN, path)
+               ||
+               starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
+                               OMFI_MEDIAFILES_DIRNAME_LEN, path)
+       )
+       {
+               ret = True;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
+                               ret == True ? "True" : "False"));
+       return ret;
+}
+
+/*
+ * Returns depth of path under media directory.  Deals with the
+ * occasional ..../. and ..../.. paths that get passed to stat.
+ *
+ * Assumes is_in_media_files has already been called and has returned
+ * true for the path; if it hasn't, this function will likely crash
+ * and burn.
+ *
+ * Not foolproof; something like "Avid MediaFiles/MXF/../foo/1"
+ * would fool it.  Haven't seen paths like that getting to the
+ * stat function yet, so ignoring that possibility for now.
+ */
+static int depth_from_media_dir(const char* media_dirname,
+               size_t media_dirname_len, const char* path)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with media_dirname '%s' "
+                               "path '%s'\n", media_dirname, path));
+       int transition_count = 0;
+       char* path_start;
+       char* pathPtr;
+
+       /* Sometimes Samba gives us "./OMFI MediaFiles". */
+       if (strncmp(path, "./", 2) == 0)
+       {
+               path_start = &path[2];
+       }
+       else {
+               path_start = path;
+       }
+
+       if (path_start[media_dirname_len] == '\0')
+       {
+               goto out;
+       }
+
+       pathPtr = &path_start[media_dirname_len + 1];
+
+       while(1)
+       {
+               if (*pathPtr == '\0' || *pathPtr == '/')
+               {
+                       if (
+                               *(pathPtr - 1) == '.'
+                                       &&
+                               *(pathPtr - 2) == '.'
+                                       &&
+                               *(pathPtr - 3) == '/'
+                       )
+                       {
+                               transition_count--;
+                       }
+                       else if (
+                               !
+                               (
+                                       *(pathPtr - 1) == '/'
+                                       ||
+                                       (
+                                               *(pathPtr - 1) == '.'
+                                                       &&
+                                               *(pathPtr - 2) == '/'
+                                       )
+                               )
+                       )
+                       {
+                               transition_count++;
+                       }
+               }
+               if (*pathPtr == '\0')
+               {
+                       break;
+               }
+               pathPtr++;
+       }
+
+       DEBUG(MH_INFO_DEBUG, ("Leaving with transition_count '%i'\n",
+                               transition_count));
+out:
+       return transition_count;
+}
+
+/* Identifies MDB and PMR files at end of path. */
+static bool is_avid_database(
+               char *path,
+               size_t path_len,
+               const char *avid_db_filename,
+               const size_t avid_db_filename_len)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s', "
+                               "avid_db_filename '%s', "
+                               "path_len '%i', "
+                               "avid_db_filename_len '%i'\n",
+                               path, avid_db_filename,
+                               path_len, avid_db_filename_len));
+
+       bool ret = False;
+
+       if (
+               path_len > avid_db_filename_len
+                       &&
+               strcmp(&path[path_len - avid_db_filename_len],
+                               avid_db_filename) == 0
+                       &&
+               (
+                       path[path_len - avid_db_filename_len - 1] == '/'
+                       ||
+                       path_len > avid_db_filename_len
+                               + APPLE_DOUBLE_PREFIX_LEN
+                               &&
+                       path[path_len - avid_db_filename_len
+                               - APPLE_DOUBLE_PREFIX_LEN - 1] == '/'
+                               &&
+                       is_apple_double(&path[path_len
+                               - avid_db_filename_len
+                               - APPLE_DOUBLE_PREFIX_LEN])
+               )
+       )
+       {
+               ret = True;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
+                               ret == True ? "True" : "False"));
+       return ret;
+}
+
+
+/* Add client suffix to paths to MDB_FILENAME, PMR_FILENAME and
+ * CREATING_SUBDIRNAME.
+ *
+ * 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,
+               char **newPath)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
+
+       /* replace /CREATING_DIRNAME/ or /._CREATING_DIRNAME/
+        * directory in path - potentially in middle of path
+        * - with suffixed name.
+        */
+       int status = 0;
+       char* pathPtr;
+       *newPath = talloc_strdup(ctx, path);
+       if (*newPath == NULL)
+       {
+               DEBUG(MH_ERR_DEBUG, ("alloc_get_client_path ENOMEM #1\n"));
+               errno = ENOMEM;
+               status = -1;
+               goto out;
+       }
+       DEBUG(MH_INFO_DEBUG, ("newPath #1 %s\n", *newPath));
+       if (
+               (pathPtr = strstr(path, CREATING_DIRNAME)) != NULL
+                       &&
+               (
+                       *(pathPtr + CREATING_DIRNAME_LEN) == '\0'
+                       ||
+                       *(pathPtr + CREATING_DIRNAME_LEN) == '/'
+               )
+                       &&
+               (
+                       pathPtr - path > 0
+                               &&
+                       *(pathPtr - 1) == '/'
+                       ||
+                       pathPtr - path > APPLE_DOUBLE_PREFIX_LEN
+                               &&
+                       *(pathPtr - APPLE_DOUBLE_PREFIX_LEN - 1) == '/'
+                               &&
+                       is_apple_double(pathPtr - APPLE_DOUBLE_PREFIX_LEN)
+               )
+       )
+       {
+               /* Insert client suffix into path. */
+               (*newPath)[pathPtr - path + CREATING_DIRNAME_LEN] = '\0';
+               DEBUG(MH_INFO_DEBUG, ("newPath #2 %s\n", *newPath));
+
+               if (status = alloc_append_client_suffix(handle, newPath))
+               {
+                       goto out;
+               }
+
+               DEBUG(MH_INFO_DEBUG, ("newPath #3 %s\n", *newPath));
+               *newPath = talloc_strdup_append(*newPath,
+                               pathPtr + CREATING_DIRNAME_LEN);
+               if (*newPath == NULL)
+               {
+                       DEBUG(MH_ERR_DEBUG, ("alloc_get_client_path "
+                                               "ENOMEM #2\n"));
+                       errno = ENOMEM;
+                       status = -1;
+                       goto out;
+               }
+               DEBUG(MH_INFO_DEBUG, ("newPath #4 %s\n", *newPath));
+       }
+
+       /* replace /MDB_FILENAME or /PMR_FILENAME or /._MDB_FILENAME
+        * or /._PMR_FILENAME at newPath end with suffixed name.
+        */
+       size_t intermPathLen = strlen(*newPath);
+       if (
+               is_avid_database(*newPath, intermPathLen,
+                       MDB_FILENAME, MDB_FILENAME_LEN)
+               ||
+               is_avid_database(*newPath, intermPathLen,
+                       PMR_FILENAME, PMR_FILENAME_LEN)
+       )
+       {
+               DEBUG(MH_INFO_DEBUG, ("newPath #5 %s\n", *newPath));
+               if (status = alloc_append_client_suffix(handle, newPath))
+               {
+                       goto out;
+               }
+               DEBUG(MH_INFO_DEBUG, ("newPath #6 %s\n", *newPath));
+       }
+out:
+       /* newPath must be freed in caller. */
+       DEBUG(MH_INFO_DEBUG, ("Leaving with *newPath '%s'\n", *newPath));
+       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 **clientFname)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+
+       int status = 0;
+       NTSTATUS copystatus;
+
+       copystatus = copy_smb_filename(ctx, smb_fname, clientFname);
+       if (!NT_STATUS_IS_OK(copystatus))
+       {
+               DEBUG(MH_ERR_DEBUG, ("alloc_get_client_smb_fname "
+                                       "NTERR\n"));
+               errno = map_errno_from_nt_status(copystatus);
+               status = -1;
+               goto err;
+       }
+       if (status = alloc_get_client_path(handle, ctx,
+                               smb_fname->base_name,
+                               &(*clientFname)->base_name))
+       {
+               goto err;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Leaving with (*clientFname)->base_name "
+                               "'%s'\n", (*clientFname)->base_name));
+err:
+       return status;
+}
+
+
+/*
+ * 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 *avid_db_filename)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with avid_db_filename '%s'\n",
+                               avid_db_filename));
+
+       int status = 0;
+
+       if ((*path = talloc_strdup(ctx, avid_db_filename)) == NULL)
+       {
+               DEBUG(MH_ERR_DEBUG, ("alloc_set_client_dirinfo_path "
+                                       "ENOMEM\n"));
+               errno = ENOMEM;
+               status = -1;
+               goto err;
+       }
+       if (status = alloc_append_client_suffix(handle, path))
+       {
+               goto err;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Leaving with *path '%s'\n", *path));
+err:
+       return status;
+}
+
+/*
+ * Replace mtime on clientFname with mtime from client-suffixed
+ * equivalent, if it exists.
+ *
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int set_fake_mtime(vfs_handle_struct *handle,
+               TALLOC_CTX *ctx,
+               struct smb_filename **clientFname,
+               int (*statFn)(const char *, SMB_STRUCT_STAT *, bool))
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with (*clientFname)->base_name "
+                       "'%s', (*clientFname)->st.st_ex_mtime %s",
+                       (*clientFname)->base_name,
+                       ctime(&((*clientFname)->st.st_ex_mtime.tv_sec))));
+
+       int status = 0;
+
+       if (
+               depth_from_media_dir(AVID_MXF_DIRNAME,
+                               AVID_MXF_DIRNAME_LEN,
+                               (*clientFname)->base_name)
+                       != 1
+                       &&
+               depth_from_media_dir(OMFI_MEDIAFILES_DIRNAME,
+                               OMFI_MEDIAFILES_DIRNAME_LEN,
+                               (*clientFname)->base_name)
+                       != 0
+       )
+       {
+               goto out;
+       }
+
+       char *statPath;
+       SMB_STRUCT_STAT fakeStat;
+       int copy_len = strlen((*clientFname)->base_name);
+
+       /* Hack to deal with occasional "Avid MediaFiles/MXF/1/." paths.
+        * We know we're under a media dir, so paths are at least 2 chars
+        * long.
+        */
+       if ((*clientFname)->base_name[copy_len - 1] == '.' &&
+                       (*clientFname)->base_name[copy_len - 2] == '/')
+       {
+               copy_len -= 2;
+       }
+
+       if ((statPath = talloc_strndup(ctx,
+                       (*clientFname)->base_name, copy_len)) == NULL)
+       {
+               errno = ENOMEM;
+               status = -1;
+               goto err;
+       }
+       if (status = alloc_append_client_suffix(handle, &statPath))
+       {
+               goto err;
+       }
+
+       DEBUG(MH_INFO_DEBUG, ("Fake stat'ing '%s'\n", statPath));
+       if (statFn(statPath, &fakeStat,
+                       lp_fake_dir_create_times(SNUM(handle->conn))))
+       {
+               /* This can fail for legitimate reasons - i.e. the
+                * fakeStat directory doesn't exist, which is okay
+                * - so we don't set status.  But if it does fail,
+                * we need to skip over the mtime assignment.
+                */
+               goto err;
+       }
+
+       DEBUG(MH_INFO_DEBUG, ("Setting fake mtime from '%s'\n", statPath));
+       (*clientFname)->st.st_ex_mtime = fakeStat.st_ex_mtime;
+err:
+       TALLOC_FREE(statPath);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with (*clientFname)->base_name "
+                       "'%s', (*clientFname)->st.st_ex_mtime %s",
+                       (*clientFname)->base_name,
+                       ctime(&((*clientFname)->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_statvfs(struct vfs_handle_struct *handle,
+               const char *path,
+               struct vfs_statvfs_struct *statbuf)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
+
+       int status;
+
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_STATVFS(handle, clientPath, statbuf);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
+               const char *fname,
+               struct mh_dirinfo_struct **dirInfo)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with fname '%s'\n", fname));
+
+       int status = 0;
+
+       *dirInfo = talloc(NULL, struct mh_dirinfo_struct);
+       if (*dirInfo == NULL)
+       {
+               goto err;
+       }
+
+       (*dirInfo)->dirpath = talloc_strdup(*dirInfo, fname);
+       if ((*dirInfo)->dirpath == NULL)
+       {
+               goto err;
+       }
+
+       if (!is_in_media_files(fname))
+       {
+               (*dirInfo)->clientPath = NULL;
+               (*dirInfo)->clientMDBFilename = NULL;
+               (*dirInfo)->clientPMRFilename = NULL;
+               (*dirInfo)->clientCreatingDirname = NULL;
+               (*dirInfo)->isInMediaFiles = False;
+               goto out;
+       }
+
+       (*dirInfo)->isInMediaFiles = True;
+
+       if (alloc_set_client_dirinfo_path(handle,
+                               *dirInfo,
+                               &((*dirInfo)->clientMDBFilename),
+                               MDB_FILENAME))
+       {
+               goto err;
+       }
+
+       if (alloc_set_client_dirinfo_path(handle,
+                               *dirInfo,
+                               &((*dirInfo)->clientPMRFilename),
+                               PMR_FILENAME))
+       {
+               goto err;
+       }
+
+       if (alloc_set_client_dirinfo_path(handle,
+                               *dirInfo,
+                               &((*dirInfo)->clientCreatingDirname),
+                               CREATING_DIRNAME))
+       {
+               goto err;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               fname,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       (*dirInfo)->clientPath = talloc_strdup(*dirInfo, clientPath);
+       if ((*dirInfo)->clientPath == NULL)
+       {
+               goto err;
+       }
+
+       TALLOC_FREE(clientPath);
+
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with (*dirInfo)->dirpath '%s', "
+                               "(*dirInfo)->clientPath '%s'\n",
+                               (*dirInfo)->dirpath,
+                               (*dirInfo)->clientPath));
+       return status;
+
+err:
+       DEBUG(MH_ERR_DEBUG, ("Failing with fname '%s'\n", fname));
+       TALLOC_FREE(*dirInfo);
+       status = -1;
+       errno = ENOMEM;
+       return status;
+}
+
+/* Success: return a mh_dirinfo_struct cast as a DIR
+ * Failure: set errno, return NULL
+ */
+static DIR *mh_opendir(vfs_handle_struct *handle,
+               const char *fname,
+               const char *mask,
+               uint32 attr)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with fname '%s'\n", fname));
+
+       struct mh_dirinfo_struct *dirInfo;
+
+       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;
+       }
+
+out:
+       /* Success is freed in closedir. */
+       DEBUG(MH_INFO_DEBUG, ("Leaving with dirInfo->dirpath '%s', "
+                               "dirInfo->clientPath '%s'\n",
+                               dirInfo->dirpath,
+                               dirInfo->clientPath));
+       return (DIR*)dirInfo;
+err:
+       /* Failure is freed here. */
+       DEBUG(MH_ERR_DEBUG, ("Failing with fname '%s'\n", fname));
+       TALLOC_FREE(dirInfo);
+       return NULL;
+}
+
+static DIR *mh_fdopendir(vfs_handle_struct *handle,
+               files_struct *fsp,
+               const char *mask,
+               uint32 attr)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with fsp->fsp_name->base_name '%s'\n",
+                       fsp->fsp_name->base_name));
+
+       struct mh_dirinfo_struct *dirInfo;
+       DIR *dirstream;
+
+       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) {
+               goto out;
+       }
+
+       if (set_fake_mtime(handle, fsp, &(fsp->fsp_name), sys_stat))
+       {
+               goto err;
+       }
+
+out:
+       DEBUG(MH_INFO_DEBUG, ("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))));
+       /* Success is freed in closedir. */
+       return (DIR *) dirInfo;
+err:
+       /* Failure is freed here. */
+       DEBUG(MH_ERR_DEBUG, ("Failing with fsp->fsp_name->base_name '%s'\n",
+                       fsp->fsp_name->base_name));
+       TALLOC_FREE(dirInfo);
+       return NULL;
+}
+
+/*
+ * skip MDB_FILENAME and PMR_FILENAME filenames and CREATING_DIRNAME
+ * directory, skip other client's suffixed MDB_FILENAME and PMR_FILENAME
+ * filenames and CREATING_DIRNAME directory, replace this client's
+ * suffixed MDB_FILENAME and PMR_FILENAME filenames and CREATING_DIRNAME
+ * directory with non suffixed.
+ *
+ * Success: return dirent
+ * End of data: return NULL
+ * Failure: set errno, return NULL
+ */
+static struct dirent *mh_readdir(vfs_handle_struct *handle,
+               DIR *dirp,
+               SMB_STRUCT_STAT *sbuf)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_readdir\n"));
+
+       mh_dirinfo_struct* dirInfo = (mh_dirinfo_struct*)dirp;
+
+       DEBUG(MH_INFO_DEBUG, ("dirInfo->dirpath '%s', "
+                               "dirInfo->clientPath '%s', "
+                               "dirInfo->isInMediaFiles '%s', "
+                               "dirInfo->clientMDBFilename '%s', "
+                               "dirInfo->clientPMRFilename '%s', "
+                               "dirInfo->clientCreatingDirname '%s'\n",
+                               dirInfo->dirpath,
+                               dirInfo->clientPath,
+                               dirInfo->isInMediaFiles ? "True" : "False",
+                               dirInfo->clientMDBFilename,
+                               dirInfo->clientPMRFilename,
+                               dirInfo->clientCreatingDirname));
+
+       struct dirent *d = NULL;
+
+       if (! dirInfo->isInMediaFiles)
+       {
+               d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
+               goto out;
+       }
+
+       int skip;
+       do
+       {
+               skip = False;
+               d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
+
+               if (d == NULL)
+               {
+                       break;
+               }
+
+               const char* dname;
+               bool isAppleDouble;
+
+               /* 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;
+               }
+
+               /* skip Avid-special files with no client suffix */
+               if (
+                       strcmp(dname, MDB_FILENAME) == 0
+                       ||
+                       strcmp(dname, PMR_FILENAME) == 0
+                       ||
+                       strcmp(dname, CREATING_DIRNAME) == 0
+               )
+               {
+                       skip = True;
+               }
+               /* chop client suffix off this client's suffixed files */
+               else if (strcmp(dname, dirInfo->clientMDBFilename) == 0)
+               {
+                       if (isAppleDouble)
+                       {
+                               d->d_name[MDB_FILENAME_LEN
+                                       + APPLE_DOUBLE_PREFIX_LEN] = '\0';
+                       }
+                       else
+                       {
+                               d->d_name[MDB_FILENAME_LEN] = '\0';
+                       }
+               }
+               else if (strcmp(dname, dirInfo->clientPMRFilename) == 0)
+               {
+                       if (isAppleDouble)
+                       {
+                               d->d_name[PMR_FILENAME_LEN
+                                       + APPLE_DOUBLE_PREFIX_LEN] = '\0';
+                       }
+                       else
+                       {
+                               d->d_name[PMR_FILENAME_LEN] = '\0';
+                       }
+               }
+               else if (strcmp(dname, dirInfo->clientCreatingDirname)
+                               == 0)
+               {
+                       if (isAppleDouble)
+                       {
+                               d->d_name[CREATING_DIRNAME_LEN
+                                       + APPLE_DOUBLE_PREFIX_LEN] = '\0';
+                       }
+                       else
+                       {
+                               d->d_name[CREATING_DIRNAME_LEN] = '\0';
+                       }
+               }
+               /*
+                * Anything that starts as an Avid-special file
+                * that's made it this far should be skipped.  This
+                * is different from the original behaviour, which
+                * only skipped other client's suffixed files.
+                */
+               else if (
+                       strncmp(MDB_FILENAME, dname,
+                               MDB_FILENAME_LEN) == 0
+                       ||
+                       strncmp(PMR_FILENAME, dname,
+                               PMR_FILENAME_LEN) == 0
+                       ||
+                       strncmp(CREATING_DIRNAME, dname,
+                               CREATING_DIRNAME_LEN) == 0
+               )
+               {
+                       skip = True;
+               }
+       }
+       while (skip);
+
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving mh_readdir\n"));
+       return d;
+}
+
+/*
+ * Success: no success result defined.
+ * Failure: no failure result defined.
+ */
+static void mh_seekdir(vfs_handle_struct *handle,
+               DIR *dirp,
+               long offset)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_seekdir\n"));
+       return SMB_VFS_NEXT_SEEKDIR(handle,
+                       ((mh_dirinfo_struct*)dirp)->dirstream, offset);
+}
+
+/*
+ * Success: return long
+ * Failure: no failure result defined.
+ */
+static long mh_telldir(vfs_handle_struct *handle,
+               DIR *dirp)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_telldir\n"));
+       return SMB_VFS_NEXT_TELLDIR(handle,
+                       ((mh_dirinfo_struct*)dirp)->dirstream);
+}
+
+/*
+ * Success: no success result defined.
+ * Failure: no failure result defined.
+ */
+static void mh_rewinddir(vfs_handle_struct *handle,
+               DIR *dirp)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_rewinddir\n"));
+       return SMB_VFS_NEXT_REWINDDIR(handle,
+                       ((mh_dirinfo_struct*)dirp)->dirstream);
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_mkdir(vfs_handle_struct *handle,
+               const char *path,
+               mode_t mode)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
+
+       int status;
+
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_MKDIR(handle, path, mode);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *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);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_rmdir(vfs_handle_struct *handle,
+               const char *path)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
+
+       int status;
+
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_RMDIR(handle, path);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *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);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with path '%s'\n", path));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_closedir(vfs_handle_struct *handle,
+               DIR *dirp)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_closedir\n"));
+       DIR *realdirp = ((mh_dirinfo_struct*)dirp)->dirstream;
+       // Will this talloc_free destroy realdirp?
+       TALLOC_FREE(dirp);
+
+       DEBUG(MH_INFO_DEBUG, ("Leaving mh_closedir\n"));
+       return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
+}
+
+/*
+ * Success: no success result defined.
+ * Failure: no failure result defined.
+ */
+static void mh_init_search_op(vfs_handle_struct *handle,
+               DIR *dirp)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_init_search_op\n"));
+       return SMB_VFS_NEXT_INIT_SEARCH_OP(handle,
+                       ((mh_dirinfo_struct*)dirp)->dirstream);
+}
+
+/*
+ * Success: return non-negative file descriptor
+ * Failure: set errno, return -1
+ */
+static int mh_open(vfs_handle_struct *handle,
+               struct smb_filename *smb_fname,
+               files_struct *fsp,
+               int flags,
+               mode_t mode)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+
+       int ret;
+
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags,
+                               mode);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if(alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               ret = -1;
+               goto err;
+       }
+
+       // 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(MH_INFO_DEBUG, ("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, clientFname, fsp, flags, mode);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+       return ret;
+}
+
+/*
+ * Success: return non-negative file descriptor
+ * Failure: set errno, return -1
+ */
+static NTSTATUS mh_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,
+               uint64_t allocation_size,
+               uint32_t private_flags,
+               struct security_descriptor *sd,
+               struct ea_list *ea_list,
+               files_struct **result_fsp,
+               int *pinfo)
+{
+       NTSTATUS status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               status = SMB_VFS_NEXT_CREATE_FILE(
+                       handle,
+                       req,
+                       root_dir_fid,
+                       smb_fname,
+                       access_mask,
+                       share_access,
+                       create_disposition,
+                       create_options,
+                       file_attributes,
+                       oplock_request,
+                       allocation_size,
+                       private_flags,
+                       sd,
+                       ea_list,
+                       result_fsp,
+                       pinfo);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /* 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,
+               clientFname,
+               access_mask,
+               share_access,
+               create_disposition,
+               create_options,
+               file_attributes,
+               oplock_request,
+               allocation_size,
+               private_flags,
+               sd,
+               ea_list,
+               result_fsp,
+               pinfo);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       DEBUG(MH_INFO_DEBUG, ("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;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_rename(vfs_handle_struct *handle,
+               const struct smb_filename *smb_fname_src,
+               const struct smb_filename *smb_fname_dst)
+{
+       DEBUG(MH_INFO_DEBUG, ("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));
+
+       int status;
+
+       if (!is_in_media_files(smb_fname_src->base_name)
+                               &&
+                       !is_in_media_files(smb_fname_dst->base_name))
+       {
+               status = SMB_VFS_NEXT_RENAME(handle, smb_fname_src,
+                               smb_fname_dst);
+               goto out;
+       }
+
+       struct smb_filename *srcClientFname = NULL;
+       struct smb_filename *dstClientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname_src,
+                               &srcClientFname))
+       {
+               goto err;
+       }
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname_dst,
+                               &dstClientFname))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_RENAME(handle, srcClientFname,
+                               dstClientFname);
+err:
+       TALLOC_FREE(dstClientFname);
+       TALLOC_FREE(srcClientFname);
+out:
+       DEBUG(MH_INFO_DEBUG, ("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 mh_stat(vfs_handle_struct *handle,
+               struct smb_filename *smb_fname)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+
+       int status = 0;
+
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               status = SMB_VFS_NEXT_STAT(handle, smb_fname);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               goto err;
+       }
+       DEBUG(MH_INFO_DEBUG, ("Stat'ing clientFname->base_name '%s'\n",
+                               clientFname->base_name));
+       if (status = SMB_VFS_NEXT_STAT(handle, clientFname))
+       {
+               goto err;
+       }
+       if (status = set_fake_mtime(handle, ctx, &clientFname, sys_stat))
+       {
+               goto err;
+       }
+
+       /* Unlike functions with const smb_filename, we have to
+        * modify smb_fname itself to pass our info back up.
+        */
+       DEBUG(MH_INFO_DEBUG, ("Setting smb_fname '%s' stat "
+                               "from clientFname '%s'\n",
+                               smb_fname->base_name,
+                               clientFname->base_name));
+       smb_fname->st = clientFname->st;
+err:
+       TALLOC_FREE(clientFname);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->st.st_ex_mtime %s",
+                       ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_lstat(vfs_handle_struct *handle,
+               struct smb_filename *smb_fname)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
+                               smb_fname->base_name));
+
+       int status = 0;
+
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               status = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               goto err;
+       }
+       if (status = SMB_VFS_NEXT_LSTAT(handle, clientFname))
+       {
+               goto err;
+       }
+
+       if (status = set_fake_mtime(handle, ctx, &clientFname, sys_lstat))
+       {
+               goto err;
+       }
+       /* Unlike functions with const smb_filename, we have to
+        * modify smb_fname itself to pass our info back up.
+        */
+       smb_fname->st = clientFname->st;
+err:
+       TALLOC_FREE(clientFname);
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->st.st_ex_mtime %s",
+                       ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_fstat(vfs_handle_struct *handle,
+               files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+       DEBUG(MH_INFO_DEBUG, ("Entering with fsp->fsp_name->base_name "
+                               "'%s'\n", fsp_str_dbg(fsp)));
+
+       int status = 0;
+
+       if (status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf))
+       {
+               goto out;
+       }
+
+       if (fsp->fsp_name == NULL
+                       || !is_in_media_files(fsp->fsp_name->base_name))
+       {
+               goto out;
+       }
+
+       if (status = mh_stat(handle, fsp->fsp_name))
+       {
+               goto out;
+       }
+
+       *sbuf = fsp->fsp_name->st;
+out:
+       DEBUG(MH_INFO_DEBUG, ("Leaving with fsp->fsp_name->st.st_ex_mtime "
+                       "%s",
+                       ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_unlink(vfs_handle_struct *handle,
+               const struct smb_filename *smb_fname)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_unlink\n"));
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               status = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_UNLINK(handle, clientFname);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_chmod(vfs_handle_struct *handle,
+               const char *path,
+               mode_t mode)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_chmod\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_CHMOD(handle, path, mode);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHMOD(handle, clientPath, mode);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_chown(vfs_handle_struct *handle,
+               const char *path,
+               uid_t uid,
+               gid_t gid)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_chown\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHOWN(handle, clientPath, uid, gid);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_lchown(vfs_handle_struct *handle,
+               const char *path,
+               uid_t uid,
+               gid_t gid)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_lchown\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_LCHOWN(handle, path, uid, gid);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_LCHOWN(handle, clientPath, uid, gid);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_chdir(vfs_handle_struct *handle,
+               const char *path)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_chdir\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_CHDIR(handle, path);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHDIR(handle, clientPath);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_ntimes(vfs_handle_struct *handle,
+               const struct smb_filename *smb_fname,
+               struct smb_file_time *ft)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_ntimes\n"));
+       if (!is_in_media_files(smb_fname->base_name))
+       {
+               status = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               smb_fname,
+                               &clientFname))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_NTIMES(handle, clientFname, ft);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_symlink(vfs_handle_struct *handle,
+               const char *oldpath,
+               const char *newpath)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_symlink\n"));
+       if (!is_in_media_files(oldpath) && !is_in_media_files(newpath))
+       {
+               status = SMB_VFS_NEXT_SYMLINK(handle, oldpath, newpath);
+               goto out;
+       }
+
+       char *oldClientPath = NULL;
+       char *newClientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               oldpath,
+                               &oldClientPath))
+       {
+               goto err;
+       }
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               newpath,
+                               &newClientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYMLINK(handle,
+                       oldClientPath,
+                       newClientPath);
+
+err:
+       TALLOC_FREE(newClientPath);
+       TALLOC_FREE(oldClientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return byte count
+ * Failure: set errno, return -1
+ */
+static int mh_readlink(vfs_handle_struct *handle,
+               const char *path,
+               char *buf,
+               size_t bufsiz)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_readlink\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_READLINK(handle, path, buf, bufsiz);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_READLINK(handle, clientPath, buf, bufsiz);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_link(vfs_handle_struct *handle,
+               const char *oldpath,
+               const char *newpath)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_link\n"));
+       if (!is_in_media_files(oldpath) && !is_in_media_files(newpath))
+       {
+               status = SMB_VFS_NEXT_LINK(handle, oldpath, newpath);
+               goto out;
+       }
+
+       char *oldClientPath = NULL;
+       char *newClientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               oldpath,
+                               &oldClientPath))
+       {
+               goto err;
+       }
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               newpath,
+                               &newClientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_LINK(handle, oldClientPath, newClientPath);
+err:
+       TALLOC_FREE(newClientPath);
+       TALLOC_FREE(oldClientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_mknod(vfs_handle_struct *handle,
+               const char *pathname,
+               mode_t mode,
+               SMB_DEV_T dev)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_mknod\n"));
+       if (!is_in_media_files(pathname))
+       {
+               status = SMB_VFS_NEXT_MKNOD(handle, pathname, mode, dev);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               pathname,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_MKNOD(handle, clientPath, mode, dev);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return path pointer
+ * Failure: set errno, return NULL pointer
+ */
+static char *mh_realpath(vfs_handle_struct *handle,
+               const char *path)
+{
+       char *buf;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_realpath\n"));
+       if (!is_in_media_files(path))
+       {
+               buf = SMB_VFS_NEXT_REALPATH(handle, path);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               buf = NULL;
+               goto err;
+       }
+
+       buf = SMB_VFS_NEXT_REALPATH(handle, clientPath);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return buf;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_chflags(vfs_handle_struct *handle,
+               const char *path,
+               unsigned int flags)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_chflags\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_CHFLAGS(handle, path, flags);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHFLAGS(handle, clientPath, flags);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return NT_STATUS_OK
+ * Failure: return NT status error
+ */
+static NTSTATUS mh_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;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_streaminfo\n"));
+       if (!is_in_media_files(fname))
+       {
+               status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, fname,
+                               ctx, num_streams, streams);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *mem_ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, mem_ctx,
+                               fname,
+                               &clientPath))
+       {
+               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, clientPath,
+                               ctx, num_streams, streams);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/* Ignoring get_real_filename function because the default
+ * doesn't do anything.
+ */
+
+/*
+ * Success: return NT_STATUS_OK
+ * Failure: return NT status error
+ * In this case, "name" is a path.
+ */
+static NTSTATUS mh_get_nt_acl(vfs_handle_struct *handle,
+               const char *name,
+               uint32 security_info,
+               struct security_descriptor **ppdesc)
+{
+       NTSTATUS status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_get_nt_acl\n"));
+       if (!is_in_media_files(name))
+       {
+               status = SMB_VFS_NEXT_GET_NT_ACL(handle, name,
+                               security_info, ppdesc);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               name,
+                               &clientPath))
+       {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_GET_NT_ACL(handle, clientPath,
+                       security_info, ppdesc);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_chmod_acl(vfs_handle_struct *handle,
+               const char *path,
+               mode_t mode)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_chmod_acl\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_CHMOD_ACL(handle, path, mode);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_CHMOD_ACL(handle, clientPath, mode);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return acl pointer
+ * Failure: set errno, return NULL
+ */
+static SMB_ACL_T mh_sys_acl_get_file(vfs_handle_struct *handle,
+               const char *path_p,
+               SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T ret;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_sys_acl_get_file\n"));
+       if (!is_in_media_files(path_p))
+       {
+               ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p, type);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               path_p,
+                               &clientPath))
+       {
+               ret = NULL;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, clientPath, type);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return ret;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ * In this case, "name" is a path.
+ */
+static int mh_sys_acl_set_file(vfs_handle_struct *handle,
+               const char *name,
+               SMB_ACL_TYPE_T acltype,
+               SMB_ACL_T theacl)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_sys_acl_set_file\n"));
+       if (!is_in_media_files(name))
+       {
+               status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name,
+                               acltype, theacl);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               name,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, clientPath,
+                       acltype, theacl);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ */
+static int mh_sys_acl_delete_def_file(vfs_handle_struct *handle,
+               const char *path)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_sys_acl_delete_def_file\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle,
+                               path);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, clientPath);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return positive number
+ * Failure: set errno, return -1
+ * In this case, "name" is an attr name.
+ */
+static ssize_t mh_getxattr(struct vfs_handle_struct *handle,
+               const char *path,
+               const char *name,
+               void *value,
+               size_t size)
+{
+       ssize_t ret;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_getxattr\n"));
+       if (!is_in_media_files(path))
+       {
+               ret = SMB_VFS_NEXT_GETXATTR(handle, path, name, value,
+                               size);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               ret = -1;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_GETXATTR(handle, clientPath, name, value, size);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return ret;
+}
+
+/*
+ * Success: return positive number
+ * Failure: set errno, return -1
+ */
+static ssize_t mh_listxattr(struct vfs_handle_struct *handle,
+               const char *path,
+               char *list,
+               size_t size)
+{
+       ssize_t ret;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_listxattr\n"));
+       if (!is_in_media_files(path))
+       {
+               ret = SMB_VFS_NEXT_LISTXATTR(handle, path, list, size);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               ret = -1;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_LISTXATTR(handle, clientPath, list, size);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return ret;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ * In this case, "name" is an attr name.
+ */
+static int mh_removexattr(struct vfs_handle_struct *handle,
+               const char *path,
+               const char *name)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_removexattr\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_REMOVEXATTR(handle, path, name);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_REMOVEXATTR(handle, clientPath, name);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return 0
+ * Failure: set errno, return -1
+ * In this case, "name" is an attr name.
+ */
+static int mh_setxattr(struct vfs_handle_struct *handle,
+               const char *path,
+               const char *name,
+               const void *value,
+               size_t size,
+               int flags)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_setxattr\n"));
+       if (!is_in_media_files(path))
+       {
+               status = SMB_VFS_NEXT_SETXATTR(handle, path, name, value,
+                               size, flags);
+               goto out;
+       }
+
+       char *clientPath = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_path(handle, ctx,
+                               path,
+                               &clientPath))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SETXATTR(handle, clientPath, name, value,
+                       size, flags);
+err:
+       TALLOC_FREE(clientPath);
+out:
+       return status;
+}
+
+/*
+ * Success: return true
+ * Failure: set errno, return false
+ */
+static bool mh_is_offline(struct vfs_handle_struct *handle,
+               const struct smb_filename *fname,
+               SMB_STRUCT_STAT *sbuf)
+{
+       // check if sbuf is modified further down the chain.
+       bool ret;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_is_offline\n"));
+       if (!is_in_media_files(fname->base_name))
+       {
+               ret = SMB_VFS_NEXT_IS_OFFLINE(handle, fname, sbuf);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if(alloc_get_client_smb_fname(handle, ctx,
+                               fname,
+                               &clientFname))
+       {
+               ret = -1;
+               goto err;
+       }
+
+       ret = SMB_VFS_NEXT_IS_OFFLINE(handle, clientFname, sbuf);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       return ret;
+}
+
+/*
+ * Success: return 0 (?)
+ * Failure: set errno, return -1
+ */
+static int mh_set_offline(struct vfs_handle_struct *handle,
+               const struct smb_filename *fname)
+{
+       int status;
+
+       DEBUG(MH_INFO_DEBUG, ("Entering mh_set_offline\n"));
+       if (!is_in_media_files(fname->base_name))
+       {
+               status = SMB_VFS_NEXT_SET_OFFLINE(handle, fname);
+               goto out;
+       }
+
+       struct smb_filename *clientFname = NULL;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (status = alloc_get_client_smb_fname(handle, ctx,
+                               fname,
+                               &clientFname))
+       {
+               goto err;
+       }
+
+       status = SMB_VFS_NEXT_SET_OFFLINE(handle, clientFname);
+err:
+       TALLOC_FREE(clientFname);
+out:
+       return status;
+}
+
+/* VFS operations structure */
+
+static struct vfs_fn_pointers vfs_mh_fns = {
+       /* Disk operations */
+
+       .statvfs_fn = mh_statvfs,
+
+       /* Directory operations */
+
+       .opendir_fn = mh_opendir,
+       .fdopendir_fn = mh_fdopendir,
+       .readdir_fn = mh_readdir,
+       .seekdir_fn = mh_seekdir,
+       .telldir_fn = mh_telldir,
+       .rewind_dir_fn = mh_rewinddir,
+       .mkdir_fn = mh_mkdir,
+       .rmdir_fn = mh_rmdir,
+       .closedir_fn = mh_closedir,
+       .init_search_op_fn = mh_init_search_op,
+
+       /* File operations */
+
+       .open_fn = mh_open,
+       .create_file_fn = mh_create_file,
+       .rename_fn = mh_rename,
+       .stat_fn = mh_stat,
+       .lstat_fn = mh_lstat,
+       .fstat_fn = mh_fstat,
+       .unlink_fn = mh_unlink,
+       .chmod_fn = mh_chmod,
+       .chown_fn = mh_chown,
+       .lchown_fn = mh_lchown,
+       .chdir_fn = mh_chdir,
+       .ntimes_fn = mh_ntimes,
+       .symlink_fn = mh_symlink,
+       .readlink_fn = mh_readlink,
+       .link_fn = mh_link,
+       .mknod_fn = mh_mknod,
+       .realpath_fn = mh_realpath,
+       .chflags_fn = mh_chflags,
+       .streaminfo_fn = mh_streaminfo,
+
+       /* NT ACL operations. */
+
+       .get_nt_acl_fn = mh_get_nt_acl,
+
+       /* POSIX ACL operations. */
+
+       .chmod_acl_fn = mh_chmod_acl,
+
+       .sys_acl_get_file_fn = mh_sys_acl_get_file,
+       .sys_acl_set_file_fn = mh_sys_acl_set_file,
+       .sys_acl_delete_def_file_fn = mh_sys_acl_delete_def_file,
+
+       /* EA operations. */
+       .getxattr_fn = mh_getxattr,
+       .listxattr_fn = mh_listxattr,
+       .removexattr_fn = mh_removexattr,
+       .setxattr_fn = mh_setxattr,
+
+       /* aio operations */
+
+       /* offline operations */
+       .is_offline_fn = mh_is_offline,
+       .set_offline_fn = mh_set_offline
+};
+
+NTSTATUS vfs_media_harmony_init(void);
+NTSTATUS vfs_media_harmony_init(void)
+{
+       NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+                               "media_harmony", &vfs_mh_fns);
+       if (!NT_STATUS_IS_OK(ret))
+       {
+               goto out;
+       }
+
+       vfs_mh_debug_level = debug_add_class("media_harmony");
+
+       if (vfs_mh_debug_level == -1) {
+               vfs_mh_debug_level = DBGC_VFS;
+               DEBUG(1, ("media_harmony_init: Couldn't register custom "
+                               "debugging class.\n"));
+       } else {
+               DEBUG(3, ("media_harmony_init: Debug class number of "
+                               "'media_harmony': %d\n",
+                               vfs_mh_debug_level));
+       }
+
+out:
+       return ret;
+}
index 7b45116d9d6589f7c65cbe3ea51100e103f57597..ee95b23066717f50dd855875b060319781efca76 100755 (executable)
@@ -1458,6 +1458,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_crossrename vfs_linux_xfs_sgid
                                       vfs_time_audit idmap_autorid idmap_tdb2
                                       idmap_rid idmap_hash''')