--- /dev/null
+/*
+ * 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;
+}