diff options
Diffstat (limited to 'source3/modules/vfs_unityed_media.c')
-rw-r--r-- | source3/modules/vfs_unityed_media.c | 1565 |
1 files changed, 1565 insertions, 0 deletions
diff --git a/source3/modules/vfs_unityed_media.c b/source3/modules/vfs_unityed_media.c new file mode 100644 index 0000000..3c1a263 --- /dev/null +++ b/source3/modules/vfs_unityed_media.c @@ -0,0 +1,1565 @@ +/* + * 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 "lib/util/smb_strtox.h" +#include <libgen.h> +#include "source3/lib/substitute.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; + codepoint_t cp; + size_t size; + int error = 0; + + 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)smb_strtoul(p, + NULL, + 10, + &error, + SMB_STR_STANDARD); + if (error != 0) { + return false; + } + 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 struct smb_filename *smb_fname, + struct vfs_statvfs_struct *statbuf) +{ + int status; + struct smb_filename *client_fname = NULL; + + DEBUG(10, ("Entering with path '%s'\n", smb_fname->base_name)); + + if (!is_in_media_files(smb_fname->base_name)) { + return SMB_VFS_NEXT_STATVFS(handle, smb_fname, statbuf); + } + + status = alloc_get_client_smb_fname(handle, + talloc_tos(), + smb_fname, + &client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_STATVFS(handle, client_fname, statbuf); +err: + TALLOC_FREE(client_fname); + DEBUG(10, ("Leaving with path '%s'\n", smb_fname->base_name)); + return status; +} + +static DIR *um_fdopendir(vfs_handle_struct *handle, + files_struct *fsp, + const char *mask, + uint32_t 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, + struct files_struct *dirfsp, + 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, dirfsp, 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, dirfsp, 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_mkdirat(vfs_handle_struct *handle, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + mode_t mode) +{ + int status; + const char *path = NULL; + struct smb_filename *client_fname = NULL; + struct smb_filename *full_fname = NULL; + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { + return -1; + } + + path = full_fname->base_name; + DEBUG(10, ("Entering with path '%s'\n", path)); + + if (!is_in_media_files(path) || !is_in_media_dir(path)) { + TALLOC_FREE(full_fname); + return SMB_VFS_NEXT_MKDIRAT(handle, + dirfsp, + smb_fname, + mode); + } + + status = alloc_get_client_smb_fname(handle, + talloc_tos(), + full_fname, + &client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_MKDIRAT(handle, + handle->conn->cwd_fsp, + client_fname, + mode); +err: + DEBUG(10, ("Leaving with path '%s'\n", path)); + TALLOC_FREE(client_fname); + TALLOC_FREE(full_fname); + 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 int um_openat(struct vfs_handle_struct *handle, + const struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + struct files_struct *fsp, + const struct vfs_open_how *how) +{ + struct smb_filename *client_fname = NULL; + int ret; + + DBG_DEBUG("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_OPENAT(handle, + dirfsp, + smb_fname, + fsp, + how); + } + + 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_OPENAT(handle, + dirfsp, + client_fname, + fsp, + how); +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, + struct files_struct *dirfsp, + 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, + const 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, + dirfsp, + 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, + dirfsp, + 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_renameat(vfs_handle_struct *handle, + files_struct *srcfsp, + const struct smb_filename *smb_fname_src, + files_struct *dstfsp, + const struct smb_filename *smb_fname_dst) +{ + int status; + struct smb_filename *src_full_fname = NULL; + struct smb_filename *dst_full_fname = NULL; + struct smb_filename *src_client_fname = NULL; + struct smb_filename *dst_client_fname = NULL; + + src_full_fname = full_path_from_dirfsp_atname(talloc_tos(), + srcfsp, + smb_fname_src); + if (src_full_fname == NULL) { + errno = ENOMEM; + return -1; + } + dst_full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dstfsp, + smb_fname_dst); + if (dst_full_fname == NULL) { + TALLOC_FREE(src_full_fname); + errno = ENOMEM; + return -1; + } + + DBG_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); + + if (!is_in_media_files(src_full_fname->base_name) + && + !is_in_media_files(dst_full_fname->base_name)) { + TALLOC_FREE(src_full_fname); + TALLOC_FREE(dst_full_fname); + return SMB_VFS_NEXT_RENAMEAT(handle, + srcfsp, + smb_fname_src, + dstfsp, + smb_fname_dst); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + src_full_fname, + &src_client_fname); + if (status != 0) { + goto err; + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + dst_full_fname, + &dst_client_fname); + + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_RENAMEAT(handle, + handle->conn->cwd_fsp, + src_client_fname, + handle->conn->cwd_fsp, + dst_client_fname); + +err: + TALLOC_FREE(dst_client_fname); + TALLOC_FREE(src_client_fname); + TALLOC_FREE(src_full_fname); + TALLOC_FREE(dst_full_fname); + DBG_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 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_unlinkat(vfs_handle_struct *handle, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + int flags) +{ + int ret; + struct smb_filename *full_fname = NULL; + struct smb_filename *client_fname = NULL; + + DEBUG(10, ("Entering um_unlinkat\n")); + + if (!is_in_media_files(smb_fname->base_name)) { + return SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp, + smb_fname, + flags); + } + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { + return -1; + } + + ret = alloc_get_client_smb_fname(handle, talloc_tos(), + full_fname, + &client_fname); + if (ret != 0) { + goto err; + } + + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp->conn->cwd_fsp, + client_fname, + flags); + +err: + TALLOC_FREE(full_fname); + TALLOC_FREE(client_fname); + return ret; +} + +static int um_lchown(vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + uid_t uid, + gid_t gid) +{ + int status; + struct smb_filename *client_fname = NULL; + + DEBUG(10, ("Entering um_lchown\n")); + if (!is_in_media_files(smb_fname->base_name)) { + return SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid); + } + + status = alloc_get_client_smb_fname(handle, + talloc_tos(), + smb_fname, + &client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid); + +err: + TALLOC_FREE(client_fname); + return status; +} + +static int um_chdir(vfs_handle_struct *handle, + const struct smb_filename *smb_fname) +{ + int status; + struct smb_filename *client_fname = NULL; + + DEBUG(10, ("Entering um_chdir\n")); + + if (!is_in_media_files(smb_fname->base_name)) { + return SMB_VFS_NEXT_CHDIR(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_CHDIR(handle, client_fname); + +err: + TALLOC_FREE(client_fname); + return status; +} + +static int um_symlinkat(vfs_handle_struct *handle, + const struct smb_filename *link_contents, + struct files_struct *dirfsp, + const struct smb_filename *new_smb_fname) +{ + int status; + struct smb_filename *new_link_target = NULL; + struct smb_filename *new_client_fname = NULL; + struct smb_filename *full_fname = NULL; + + DEBUG(10, ("Entering um_symlinkat\n")); + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + new_smb_fname); + if (full_fname == NULL) { + return -1; + } + + if (!is_in_media_files(link_contents->base_name) && + !is_in_media_files(full_fname->base_name)) { + TALLOC_FREE(full_fname); + return SMB_VFS_NEXT_SYMLINKAT(handle, + link_contents, + dirfsp, + new_smb_fname); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + link_contents, &new_link_target); + if (status != 0) { + goto err; + } + status = alloc_get_client_smb_fname(handle, talloc_tos(), + full_fname, &new_client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_SYMLINKAT(handle, + new_link_target, + handle->conn->cwd_fsp, + new_client_fname); + +err: + TALLOC_FREE(new_link_target); + TALLOC_FREE(new_client_fname); + TALLOC_FREE(full_fname); + return status; +} + +static int um_readlinkat(vfs_handle_struct *handle, + const struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + char *buf, + size_t bufsiz) +{ + int status; + struct smb_filename *client_fname = NULL; + struct smb_filename *full_fname = NULL; + + DEBUG(10, ("Entering um_readlinkat\n")); + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { + return -1; + } + + if (!is_in_media_files(full_fname->base_name)) { + TALLOC_FREE(full_fname); + return SMB_VFS_NEXT_READLINKAT(handle, + dirfsp, + smb_fname, + buf, + bufsiz); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + full_fname, &client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_READLINKAT(handle, + handle->conn->cwd_fsp, + client_fname, + buf, + bufsiz); + +err: + TALLOC_FREE(full_fname); + TALLOC_FREE(client_fname); + return status; +} + +static int um_linkat(vfs_handle_struct *handle, + files_struct *srcfsp, + const struct smb_filename *old_smb_fname, + files_struct *dstfsp, + const struct smb_filename *new_smb_fname, + int flags) +{ + int status; + struct smb_filename *old_full_fname = NULL; + struct smb_filename *new_full_fname = NULL; + struct smb_filename *old_client_fname = NULL; + struct smb_filename *new_client_fname = NULL; + + old_full_fname = full_path_from_dirfsp_atname(talloc_tos(), + srcfsp, + old_smb_fname); + if (old_full_fname == NULL) { + return -1; + } + new_full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dstfsp, + new_smb_fname); + if (new_full_fname == NULL) { + TALLOC_FREE(old_full_fname); + return -1; + } + + DEBUG(10, ("Entering um_linkat\n")); + if (!is_in_media_files(old_full_fname->base_name) && + !is_in_media_files(new_full_fname->base_name)) { + TALLOC_FREE(old_full_fname); + TALLOC_FREE(new_full_fname); + return SMB_VFS_NEXT_LINKAT(handle, + srcfsp, + old_smb_fname, + dstfsp, + new_smb_fname, + flags); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + old_full_fname, &old_client_fname); + if (status != 0) { + goto err; + } + status = alloc_get_client_smb_fname(handle, talloc_tos(), + new_full_fname, &new_client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_LINKAT(handle, + handle->conn->cwd_fsp, + old_client_fname, + handle->conn->cwd_fsp, + new_client_fname, + flags); + +err: + TALLOC_FREE(old_full_fname); + TALLOC_FREE(new_full_fname); + TALLOC_FREE(old_client_fname); + TALLOC_FREE(new_client_fname); + return status; +} + +static int um_mknodat(vfs_handle_struct *handle, + files_struct *dirfsp, + const struct smb_filename *smb_fname, + mode_t mode, + SMB_DEV_T dev) +{ + int status; + struct smb_filename *client_fname = NULL; + struct smb_filename *full_fname = NULL; + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { + return -1; + } + + DEBUG(10, ("Entering um_mknodat\n")); + if (!is_in_media_files(full_fname->base_name)) { + TALLOC_FREE(full_fname); + return SMB_VFS_NEXT_MKNODAT(handle, + dirfsp, + smb_fname, + mode, + dev); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + full_fname, &client_fname); + if (status != 0) { + goto err; + } + + status = SMB_VFS_NEXT_MKNODAT(handle, + handle->conn->cwd_fsp, + client_fname, + mode, + dev); + +err: + TALLOC_FREE(client_fname); + TALLOC_FREE(full_fname); + return status; +} + +static struct smb_filename *um_realpath(vfs_handle_struct *handle, + TALLOC_CTX *ctx, + const struct smb_filename *smb_fname) +{ + struct smb_filename *client_fname = NULL; + struct smb_filename *result_fname = NULL; + int status; + + DEBUG(10, ("Entering um_realpath\n")); + + if (!is_in_media_files(smb_fname->base_name)) { + return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname); + } + + status = alloc_get_client_smb_fname(handle, talloc_tos(), + smb_fname, &client_fname); + if (status != 0) { + goto err; + } + + result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, client_fname); + +err: + TALLOC_FREE(client_fname); + return result_fname; +} + +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 */ + + .fdopendir_fn = um_fdopendir, + .readdir_fn = um_readdir, + .seekdir_fn = um_seekdir, + .telldir_fn = um_telldir, + .rewind_dir_fn = um_rewinddir, + .mkdirat_fn = um_mkdirat, + .closedir_fn = um_closedir, + + /* File operations */ + + .openat_fn = um_openat, + .create_file_fn = um_create_file, + .renameat_fn = um_renameat, + .stat_fn = um_stat, + .lstat_fn = um_lstat, + .fstat_fn = um_fstat, + .unlinkat_fn = um_unlinkat, + .lchown_fn = um_lchown, + .chdir_fn = um_chdir, + .symlinkat_fn = um_symlinkat, + .readlinkat_fn = um_readlinkat, + .linkat_fn = um_linkat, + .mknodat_fn = um_mknodat, + .realpath_fn = um_realpath, + + /* EA operations. */ + .getxattrat_send_fn = vfs_not_implemented_getxattrat_send, + .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv, +}; + +static_decl_vfs; +NTSTATUS vfs_unityed_media_init(TALLOC_CTX *ctx) +{ + 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; +} |