mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
f4b4872982
Prepare for changing vfs_streaminfo to do the same. Signed-off-by: Jeremy Allison <jra@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org> Autobuild-User(master): Ralph Böhme <slow@samba.org> Autobuild-Date(master): Mon Mar 7 21:12:56 CET 2016 on sn-devel-144
1983 lines
44 KiB
C
1983 lines
44 KiB
C
/*
|
|
* 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 struct smb_filename *smb_fname,
|
|
const char *mask,
|
|
uint32_t attr)
|
|
{
|
|
struct um_dirinfo_struct *dirInfo;
|
|
|
|
DEBUG(10, ("Entering with fname '%s'\n", smb_fname->base_name));
|
|
|
|
if (alloc_set_client_dirinfo(handle, smb_fname->base_name, &dirInfo)) {
|
|
goto err;
|
|
}
|
|
|
|
if (!dirInfo->isInMediaFiles) {
|
|
dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
|
|
handle, smb_fname, mask, attr);
|
|
} else {
|
|
struct smb_filename *client_smb_fname =
|
|
synthetic_smb_fname(talloc_tos(),
|
|
dirInfo->clientPath,
|
|
NULL,
|
|
NULL);
|
|
if (client_smb_fname == NULL) {
|
|
goto err;
|
|
}
|
|
|
|
dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
|
|
handle, client_smb_fname, mask, attr);
|
|
|
|
TALLOC_FREE(client_smb_fname);
|
|
}
|
|
|
|
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", smb_fname->base_name));
|
|
TALLOC_FREE(dirInfo);
|
|
return NULL;
|
|
}
|
|
|
|
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,
|
|
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 struct smb_filename *smb_fname,
|
|
mode_t mode)
|
|
{
|
|
int status;
|
|
const char *path = smb_fname->base_name;
|
|
struct smb_filename *client_fname = NULL;
|
|
|
|
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, smb_fname, mode);
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&client_fname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_MKDIR(handle, client_fname, mode);
|
|
err:
|
|
TALLOC_FREE(client_fname);
|
|
DEBUG(10, ("Leaving with path '%s'\n", path));
|
|
return status;
|
|
}
|
|
|
|
static int um_rmdir(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname)
|
|
{
|
|
int status;
|
|
const char *path = smb_fname->base_name;
|
|
struct smb_filename *client_fname = NULL;
|
|
|
|
DEBUG(10, ("Entering with path '%s'\n", path));
|
|
|
|
if (!is_in_media_files(path)) {
|
|
return SMB_VFS_NEXT_RMDIR(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_RMDIR(handle, client_fname);
|
|
err:
|
|
TALLOC_FREE(client_fname);
|
|
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 struct smb_filename *smb_fname,
|
|
mode_t mode)
|
|
{
|
|
int status;
|
|
struct smb_filename *client_fname = NULL;
|
|
|
|
DEBUG(10, ("Entering um_chmod\n"));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&client_fname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_CHMOD(handle, client_fname, mode);
|
|
|
|
err:
|
|
TALLOC_FREE(client_fname);
|
|
return status;
|
|
}
|
|
|
|
static int um_chown(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_chown\n"));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_CHOWN(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_CHOWN(handle, client_fname, uid, gid);
|
|
|
|
err:
|
|
TALLOC_FREE(client_fname);
|
|
return status;
|
|
}
|
|
|
|
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 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 struct smb_filename *smb_fname,
|
|
TALLOC_CTX *ctx,
|
|
unsigned int *num_streams,
|
|
struct stream_struct **streams)
|
|
{
|
|
NTSTATUS status;
|
|
int ret;
|
|
struct smb_filename *client_fname = NULL;
|
|
|
|
DEBUG(10, ("Entering um_streaminfo\n"));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname,
|
|
ctx, num_streams, streams);
|
|
}
|
|
|
|
ret = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&client_fname);
|
|
if (ret != 0) {
|
|
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_fname,
|
|
ctx, num_streams, streams);
|
|
err:
|
|
TALLOC_FREE(client_fname);
|
|
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 struct smb_filename *smb_fname,
|
|
uint32_t security_info,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct security_descriptor **ppdesc)
|
|
{
|
|
NTSTATUS status;
|
|
char *client_path = NULL;
|
|
struct smb_filename *client_smb_fname = NULL;
|
|
int ret;
|
|
|
|
DEBUG(10, ("Entering um_get_nt_acl\n"));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname,
|
|
security_info,
|
|
mem_ctx, ppdesc);
|
|
}
|
|
|
|
ret = alloc_get_client_path(handle, talloc_tos(),
|
|
smb_fname->base_name, &client_path);
|
|
if (ret != 0) {
|
|
status = map_nt_error_from_unix(errno);
|
|
goto err;
|
|
}
|
|
|
|
client_smb_fname = synthetic_smb_fname(talloc_tos(),
|
|
client_path,
|
|
NULL,
|
|
NULL);
|
|
if (client_smb_fname == NULL) {
|
|
TALLOC_FREE(client_path);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_GET_NT_ACL(handle, client_smb_fname,
|
|
security_info,
|
|
mem_ctx, ppdesc);
|
|
err:
|
|
TALLOC_FREE(client_smb_fname);
|
|
TALLOC_FREE(client_path);
|
|
return status;
|
|
}
|
|
|
|
static int um_chmod_acl(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname,
|
|
mode_t mode)
|
|
{
|
|
int status;
|
|
int saved_errno;
|
|
struct smb_filename *client_fname = NULL;
|
|
|
|
DEBUG(10, ("Entering um_chmod_acl\n"));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_CHMOD_ACL(handle, smb_fname, mode);
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&client_fname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
status = SMB_VFS_NEXT_CHMOD_ACL(handle, client_fname, mode);
|
|
|
|
err:
|
|
saved_errno = errno;
|
|
TALLOC_FREE(client_fname);
|
|
errno = saved_errno;
|
|
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;
|
|
}
|