mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
5fc016f268
The Linux prototype for openat2 looks like this: long openat2(int dirfd, const char *pathname, struct open_how *how, size_t size); where "struct open_how" is defined in "linux/openat2.h". It is designed to be extensible with further flags. The "size" parameter is required because there is no type checking between userland and kernelspace, so the way for Linux to find which version of open_how is being passed in is looking at the size: "open_how" is expected to only every grow with additional fields, should a change be necessary in the future. Samba does not have this problem, we can typecheck the struct and pointers, we expect all VFS modules to be compiled against the current vfs.h. For now this adds no functionality, but it will make further patches much smaller. Pair-programmed-with: Stefan Metzmacher <metze@samba.org> Signed-off-by: Volker Lendecke <vl@samba.org> Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
1903 lines
43 KiB
C
1903 lines
43 KiB
C
/*
|
|
* $Id: media_harmony.c,v 1.1 2007/11/06 10:07:22 stuart_hc Exp $
|
|
*
|
|
* Samba VFS module supporting multiple AVID clients sharing media.
|
|
*
|
|
* Copyright (C) 2005 Philip de Nier <philipn@users.sourceforge.net>
|
|
* Copyright (C) 2012 Andrew Klaassen <clawsoon@yahoo.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Media Harmony is a Samba VFS module that allows multiple AVID
|
|
* clients to share media. Each client sees their own copy of the
|
|
* AVID msmMMOB.mdb and msmFMID.pmr files and Creating directories.
|
|
*
|
|
* Add this module to the vfs objects option in your Samba share
|
|
* configuration.
|
|
* eg.
|
|
*
|
|
* [avid_win]
|
|
* path = /video
|
|
* vfs objects = media_harmony
|
|
* ...
|
|
*
|
|
* It is recommended that you separate out Samba shares for Mac
|
|
* and Windows clients, and add the following options to the shares
|
|
* for Windows clients (NOTE: replace @ with *):
|
|
*
|
|
* veto files = /.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/
|
|
* delete veto files = yes
|
|
*
|
|
* This prevents hidden files from Mac clients interfering with Windows
|
|
* clients. If you find any more problem hidden files then add them to
|
|
* the list.
|
|
*
|
|
*
|
|
* Andrew Klaassen, 2012-03-14
|
|
* To prevent Avid clients from interrupting each other (via Avid's habit
|
|
* of launching a database refresh whenever it notices an mtime update
|
|
* on media directories, i.e. whenever one editor adds new material to a
|
|
* shared share), I've added code that causes stat information for anything
|
|
* directly under "Avid MediaFile/MXF" to be taken from
|
|
* dirname_clientaddr_clientuser if it exists. These files ~aren't~
|
|
* hidden, unlike the client-suffixed database files.
|
|
*
|
|
* For example, stat information for
|
|
* Avid MediaFiles/MXF/1
|
|
* will come from
|
|
* Avid MediaFiles/MXF/1_192.168.1.10_dave
|
|
* for dave working on 192.168.1.10, but will come from
|
|
* Avid MediaFile/MXF/1_192.168.1.11_susan
|
|
* for susan working on 192.168.1.11. If those alternate
|
|
* directories don't exist, the user will get the actual directory's stat
|
|
* info. When an editor wants to force a database refresh, they update
|
|
* the mtime on "their" file. This will cause Avid
|
|
* on that client to see an updated mtime for "Avid MediaFiles/MXF/1",
|
|
* which will trigger an Avid database refresh just for that editor.
|
|
*
|
|
*
|
|
* Notes:
|
|
* - This module is designed to work with AVID editing applications that
|
|
* look in the Avid MediaFiles or OMFI MediaFiles directory for media.
|
|
* It is not designed to work as expected in all circumstances for
|
|
* general use. For example: it is possibly to open client specific
|
|
* files such as msmMMOB.mdb_192.168.1.10_userx even though is doesn't
|
|
* show up in a directory listing.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "includes.h"
|
|
#include "system/filesys.h"
|
|
#include "smbd/smbd.h"
|
|
#include "../smbd/globals.h"
|
|
#include "auth.h"
|
|
#include "../lib/tsocket/tsocket.h"
|
|
|
|
#define MH_INFO_DEBUG 10
|
|
#define MH_ERR_DEBUG 0
|
|
|
|
static const char* MDB_FILENAME = "msmMMOB.mdb";
|
|
static const size_t MDB_FILENAME_LEN = 11;
|
|
static const char* PMR_FILENAME = "msmFMID.pmr";
|
|
static const size_t PMR_FILENAME_LEN = 11;
|
|
static const char* CREATING_DIRNAME = "Creating";
|
|
static const size_t CREATING_DIRNAME_LEN = 8;
|
|
static const char* AVID_MEDIAFILES_DIRNAME = "Avid MediaFiles";
|
|
static const size_t AVID_MEDIAFILES_DIRNAME_LEN = 15;
|
|
static const char* OMFI_MEDIAFILES_DIRNAME = "OMFI MediaFiles";
|
|
static const size_t OMFI_MEDIAFILES_DIRNAME_LEN = 15;
|
|
static const char* APPLE_DOUBLE_PREFIX = "._";
|
|
static const size_t APPLE_DOUBLE_PREFIX_LEN = 2;
|
|
static const char* AVID_MXF_DIRNAME = "Avid MediaFiles/MXF";
|
|
static const size_t AVID_MXF_DIRNAME_LEN = 19;
|
|
|
|
static int vfs_mh_debug_level = DBGC_VFS;
|
|
|
|
/* supplements the directory list stream */
|
|
typedef struct mh_dirinfo_struct
|
|
{
|
|
DIR* dirstream;
|
|
char *dirpath;
|
|
char *clientPath;
|
|
bool isInMediaFiles;
|
|
char *clientMDBFilename;
|
|
char *clientPMRFilename;
|
|
char *clientCreatingDirname;
|
|
} mh_dirinfo_struct;
|
|
|
|
|
|
/* Add "_<ip address>_<user name>" suffix to path or filename.
|
|
*
|
|
* Success: return 0
|
|
* Failure: set errno, path NULL, return -1
|
|
*/
|
|
static int alloc_append_client_suffix(vfs_handle_struct *handle,
|
|
char **path)
|
|
{
|
|
int status = 0;
|
|
char *raddr = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with *path '%s'\n", *path));
|
|
|
|
raddr = tsocket_address_inet_addr_string(
|
|
handle->conn->sconn->remote_address, talloc_tos());
|
|
if (raddr == NULL)
|
|
{
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
/* talloc_asprintf_append uses talloc_realloc, which
|
|
* frees original 'path' memory so we don't have to.
|
|
*/
|
|
*path = talloc_asprintf_append(*path, "_%s_%s",
|
|
raddr,
|
|
handle->conn->session_info->unix_info->sanitized_username);
|
|
if (*path == NULL)
|
|
{
|
|
DEBUG(MH_ERR_DEBUG, ("alloc_append_client_suffix "
|
|
"out of memory\n"));
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with *path '%s'\n", *path));
|
|
err:
|
|
TALLOC_FREE(raddr);
|
|
return status;
|
|
}
|
|
|
|
|
|
/* Returns True if the file or directory begins with the appledouble
|
|
* prefix.
|
|
*/
|
|
static bool is_apple_double(const char* fname)
|
|
{
|
|
bool ret = False;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with fname '%s'\n", fname));
|
|
|
|
if (strncmp(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)
|
|
== 0)
|
|
{
|
|
ret = True;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
|
|
ret == True ? "True" : "False"));
|
|
return ret;
|
|
}
|
|
|
|
static bool starts_with_media_dir(const char* media_dirname,
|
|
size_t media_dirname_len, const char* path)
|
|
{
|
|
bool ret = False;
|
|
const char *path_start;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with media_dirname '%s' "
|
|
"path '%s'\n", media_dirname, path));
|
|
|
|
/* Sometimes Samba gives us "./OMFI MediaFiles". */
|
|
if (strncmp(path, "./", 2) == 0)
|
|
{
|
|
path_start = &path[2];
|
|
}
|
|
else {
|
|
path_start = path;
|
|
}
|
|
|
|
if (strncmp(media_dirname, path_start, media_dirname_len) == 0
|
|
&&
|
|
(
|
|
path_start[media_dirname_len] == '\0'
|
|
||
|
|
path_start[media_dirname_len] == '/'
|
|
)
|
|
)
|
|
{
|
|
ret = True;
|
|
}
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
|
|
ret == True ? "True" : "False"));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Returns True if the file or directory referenced by the path is below
|
|
* the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME directory
|
|
* The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME are assumed to
|
|
* be in the root directory, which is generally a safe assumption
|
|
* in the fixed-path world of Avid.
|
|
*/
|
|
static bool is_in_media_files(const char* path)
|
|
{
|
|
bool ret = False;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
|
|
|
|
if (
|
|
starts_with_media_dir(AVID_MEDIAFILES_DIRNAME,
|
|
AVID_MEDIAFILES_DIRNAME_LEN, path)
|
|
||
|
|
starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
|
|
OMFI_MEDIAFILES_DIRNAME_LEN, path)
|
|
)
|
|
{
|
|
ret = True;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
|
|
ret == True ? "True" : "False"));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Returns depth of path under media directory. Deals with the
|
|
* occasional ..../. and ..../.. paths that get passed to stat.
|
|
*
|
|
* Assumes is_in_media_files has already been called and has returned
|
|
* true for the path; if it hasn't, this function will likely crash
|
|
* and burn.
|
|
*
|
|
* Not foolproof; something like "Avid MediaFiles/MXF/../foo/1"
|
|
* would fool it. Haven't seen paths like that getting to the
|
|
* stat function yet, so ignoring that possibility for now.
|
|
*/
|
|
static int depth_from_media_dir(const char* media_dirname,
|
|
size_t media_dirname_len, const char* path)
|
|
{
|
|
int transition_count = 0;
|
|
const char *path_start;
|
|
const char *pathPtr;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with media_dirname '%s' "
|
|
"path '%s'\n", media_dirname, path));
|
|
|
|
/* Sometimes Samba gives us "./OMFI MediaFiles". */
|
|
if (strncmp(path, "./", 2) == 0)
|
|
{
|
|
path_start = &path[2];
|
|
}
|
|
else {
|
|
path_start = path;
|
|
}
|
|
|
|
if (path_start[media_dirname_len] == '\0')
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
pathPtr = &path_start[media_dirname_len + 1];
|
|
|
|
while(1)
|
|
{
|
|
if (*pathPtr == '\0' || *pathPtr == '/')
|
|
{
|
|
if (
|
|
*(pathPtr - 1) == '.'
|
|
&&
|
|
*(pathPtr - 2) == '.'
|
|
&&
|
|
*(pathPtr - 3) == '/'
|
|
)
|
|
{
|
|
transition_count--;
|
|
}
|
|
else if (
|
|
!
|
|
(
|
|
*(pathPtr - 1) == '/'
|
|
||
|
|
(
|
|
*(pathPtr - 1) == '.'
|
|
&&
|
|
*(pathPtr - 2) == '/'
|
|
)
|
|
)
|
|
)
|
|
{
|
|
transition_count++;
|
|
}
|
|
}
|
|
if (*pathPtr == '\0')
|
|
{
|
|
break;
|
|
}
|
|
pathPtr++;
|
|
}
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with transition_count '%i'\n",
|
|
transition_count));
|
|
out:
|
|
return transition_count;
|
|
}
|
|
|
|
/* Identifies MDB and PMR files at end of path. */
|
|
static bool is_avid_database(
|
|
char *path,
|
|
size_t path_len,
|
|
const char *avid_db_filename,
|
|
const size_t avid_db_filename_len)
|
|
{
|
|
bool ret = False;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with path '%s', "
|
|
"avid_db_filename '%s', "
|
|
"path_len '%i', "
|
|
"avid_db_filename_len '%i'\n",
|
|
path, avid_db_filename,
|
|
(int)path_len, (int)avid_db_filename_len));
|
|
|
|
if (
|
|
path_len > avid_db_filename_len
|
|
&&
|
|
strcmp(&path[path_len - avid_db_filename_len],
|
|
avid_db_filename) == 0
|
|
&&
|
|
(
|
|
path[path_len - avid_db_filename_len - 1] == '/'
|
|
||
|
|
(path_len > avid_db_filename_len
|
|
+ APPLE_DOUBLE_PREFIX_LEN
|
|
&&
|
|
path[path_len - avid_db_filename_len
|
|
- APPLE_DOUBLE_PREFIX_LEN - 1] == '/'
|
|
&&
|
|
is_apple_double(&path[path_len
|
|
- avid_db_filename_len
|
|
- APPLE_DOUBLE_PREFIX_LEN]))
|
|
)
|
|
)
|
|
{
|
|
ret = True;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with ret '%s'\n",
|
|
ret == True ? "True" : "False"));
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Add client suffix to paths to MDB_FILENAME, PMR_FILENAME and
|
|
* CREATING_SUBDIRNAME.
|
|
*
|
|
* Caller must free newPath.
|
|
*
|
|
* Success: return 0
|
|
* Failure: set errno, newPath NULL, return -1
|
|
*/
|
|
static int alloc_get_client_path(vfs_handle_struct *handle,
|
|
TALLOC_CTX *ctx,
|
|
const char *path,
|
|
char **newPath)
|
|
{
|
|
/* replace /CREATING_DIRNAME/ or /._CREATING_DIRNAME/
|
|
* directory in path - potentially in middle of path
|
|
* - with suffixed name.
|
|
*/
|
|
int status = 0;
|
|
char* pathPtr;
|
|
size_t intermPathLen;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
|
|
|
|
*newPath = talloc_strdup(ctx, path);
|
|
if (*newPath == NULL)
|
|
{
|
|
DEBUG(MH_ERR_DEBUG, ("alloc_get_client_path ENOMEM #1\n"));
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto out;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #1 %s\n", *newPath));
|
|
if (
|
|
(pathPtr = strstr(path, CREATING_DIRNAME)) != NULL
|
|
&&
|
|
(
|
|
*(pathPtr + CREATING_DIRNAME_LEN) == '\0'
|
|
||
|
|
*(pathPtr + CREATING_DIRNAME_LEN) == '/'
|
|
)
|
|
&&
|
|
(
|
|
(pathPtr - path > 0
|
|
&&
|
|
*(pathPtr - 1) == '/')
|
|
||
|
|
(pathPtr - path > APPLE_DOUBLE_PREFIX_LEN
|
|
&&
|
|
*(pathPtr - APPLE_DOUBLE_PREFIX_LEN - 1) == '/'
|
|
&&
|
|
is_apple_double(pathPtr - APPLE_DOUBLE_PREFIX_LEN))
|
|
)
|
|
)
|
|
{
|
|
/* Insert client suffix into path. */
|
|
(*newPath)[pathPtr - path + CREATING_DIRNAME_LEN] = '\0';
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #2 %s\n", *newPath));
|
|
|
|
if ((status = alloc_append_client_suffix(handle, newPath)))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #3 %s\n", *newPath));
|
|
*newPath = talloc_strdup_append(*newPath,
|
|
pathPtr + CREATING_DIRNAME_LEN);
|
|
if (*newPath == NULL)
|
|
{
|
|
DEBUG(MH_ERR_DEBUG, ("alloc_get_client_path "
|
|
"ENOMEM #2\n"));
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto out;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #4 %s\n", *newPath));
|
|
}
|
|
|
|
/* replace /MDB_FILENAME or /PMR_FILENAME or /._MDB_FILENAME
|
|
* or /._PMR_FILENAME at newPath end with suffixed name.
|
|
*/
|
|
intermPathLen = strlen(*newPath);
|
|
if (
|
|
is_avid_database(*newPath, intermPathLen,
|
|
MDB_FILENAME, MDB_FILENAME_LEN)
|
|
||
|
|
is_avid_database(*newPath, intermPathLen,
|
|
PMR_FILENAME, PMR_FILENAME_LEN)
|
|
)
|
|
{
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #5 %s\n", *newPath));
|
|
if ((status = alloc_append_client_suffix(handle, newPath)))
|
|
{
|
|
goto out;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("newPath #6 %s\n", *newPath));
|
|
}
|
|
out:
|
|
/* newPath must be freed in caller. */
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with *newPath '%s'\n", *newPath));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
|
|
TALLOC_CTX *ctx,
|
|
const struct smb_filename *smb_fname,
|
|
struct smb_filename **clientFname)
|
|
{
|
|
int status = 0;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
|
|
*clientFname = cp_smb_filename(ctx, smb_fname);
|
|
if ((*clientFname) == NULL) {
|
|
DEBUG(MH_ERR_DEBUG, ("alloc_get_client_smb_fname "
|
|
"NTERR\n"));
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
if ((status = alloc_get_client_path(handle, ctx,
|
|
smb_fname->base_name,
|
|
&(*clientFname)->base_name)))
|
|
{
|
|
goto err;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with (*clientFname)->base_name "
|
|
"'%s'\n", (*clientFname)->base_name));
|
|
err:
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
|
|
TALLOC_CTX *ctx,
|
|
char **path,
|
|
const char *avid_db_filename)
|
|
{
|
|
int status = 0;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with avid_db_filename '%s'\n",
|
|
avid_db_filename));
|
|
|
|
if ((*path = talloc_strdup(ctx, avid_db_filename)) == NULL)
|
|
{
|
|
DEBUG(MH_ERR_DEBUG, ("alloc_set_client_dirinfo_path "
|
|
"ENOMEM\n"));
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
if ((status = alloc_append_client_suffix(handle, path)))
|
|
{
|
|
goto err;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with *path '%s'\n", *path));
|
|
err:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Replace mtime on clientFname with mtime from client-suffixed
|
|
* equivalent, if it exists.
|
|
*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int set_fake_mtime(vfs_handle_struct *handle,
|
|
TALLOC_CTX *ctx,
|
|
struct smb_filename **clientFname,
|
|
int (*statFn)(const char *, SMB_STRUCT_STAT *, bool))
|
|
{
|
|
int status = 0;
|
|
char *statPath;
|
|
SMB_STRUCT_STAT fakeStat;
|
|
int copy_len;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with (*clientFname)->base_name "
|
|
"'%s', (*clientFname)->st.st_ex_mtime %s",
|
|
(*clientFname)->base_name,
|
|
ctime(&((*clientFname)->st.st_ex_mtime.tv_sec))));
|
|
|
|
if (
|
|
depth_from_media_dir(AVID_MXF_DIRNAME,
|
|
AVID_MXF_DIRNAME_LEN,
|
|
(*clientFname)->base_name)
|
|
!= 1
|
|
&&
|
|
depth_from_media_dir(OMFI_MEDIAFILES_DIRNAME,
|
|
OMFI_MEDIAFILES_DIRNAME_LEN,
|
|
(*clientFname)->base_name)
|
|
!= 0
|
|
)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
copy_len = strlen((*clientFname)->base_name);
|
|
|
|
/* Hack to deal with occasional "Avid MediaFiles/MXF/1/." paths.
|
|
* We know we're under a media dir, so paths are at least 2 chars
|
|
* long.
|
|
*/
|
|
if ((*clientFname)->base_name[copy_len - 1] == '.' &&
|
|
(*clientFname)->base_name[copy_len - 2] == '/')
|
|
{
|
|
copy_len -= 2;
|
|
}
|
|
|
|
if (((statPath = talloc_strndup(ctx,
|
|
(*clientFname)->base_name, copy_len)) == NULL))
|
|
{
|
|
errno = ENOMEM;
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
if ((status = alloc_append_client_suffix(handle, &statPath)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Fake stat'ing '%s'\n", statPath));
|
|
if (statFn(statPath, &fakeStat,
|
|
lp_fake_directory_create_times(SNUM(handle->conn))))
|
|
{
|
|
/* This can fail for legitimate reasons - i.e. the
|
|
* fakeStat directory doesn't exist, which is okay
|
|
* - so we don't set status. But if it does fail,
|
|
* we need to skip over the mtime assignment.
|
|
*/
|
|
goto err;
|
|
}
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Setting fake mtime from '%s'\n", statPath));
|
|
(*clientFname)->st.st_ex_mtime = fakeStat.st_ex_mtime;
|
|
err:
|
|
TALLOC_FREE(statPath);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with (*clientFname)->base_name "
|
|
"'%s', (*clientFname)->st.st_ex_mtime %s",
|
|
(*clientFname)->base_name,
|
|
ctime(&((*clientFname)->st.st_ex_mtime.tv_sec))));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_statvfs(struct vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname,
|
|
struct vfs_statvfs_struct *statbuf)
|
|
{
|
|
int status;
|
|
struct smb_filename *clientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n",
|
|
smb_fname->base_name));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_STATVFS(handle, smb_fname, statbuf);
|
|
goto out;
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&clientFname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_STATVFS(handle, clientFname, statbuf);
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with path '%s'\n",
|
|
smb_fname->base_name));
|
|
return status;
|
|
}
|
|
|
|
static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
|
|
const char *fname,
|
|
struct mh_dirinfo_struct **dirInfo)
|
|
{
|
|
int status = 0;
|
|
char *clientPath;
|
|
TALLOC_CTX *ctx;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with fname '%s'\n", fname));
|
|
|
|
*dirInfo = talloc(NULL, struct mh_dirinfo_struct);
|
|
if (*dirInfo == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
(*dirInfo)->dirpath = talloc_strdup(*dirInfo, fname);
|
|
if ((*dirInfo)->dirpath == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if (!is_in_media_files(fname))
|
|
{
|
|
(*dirInfo)->clientPath = NULL;
|
|
(*dirInfo)->clientMDBFilename = NULL;
|
|
(*dirInfo)->clientPMRFilename = NULL;
|
|
(*dirInfo)->clientCreatingDirname = NULL;
|
|
(*dirInfo)->isInMediaFiles = False;
|
|
goto out;
|
|
}
|
|
|
|
(*dirInfo)->isInMediaFiles = True;
|
|
|
|
if (alloc_set_client_dirinfo_path(handle,
|
|
*dirInfo,
|
|
&((*dirInfo)->clientMDBFilename),
|
|
MDB_FILENAME))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if (alloc_set_client_dirinfo_path(handle,
|
|
*dirInfo,
|
|
&((*dirInfo)->clientPMRFilename),
|
|
PMR_FILENAME))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if (alloc_set_client_dirinfo_path(handle,
|
|
*dirInfo,
|
|
&((*dirInfo)->clientCreatingDirname),
|
|
CREATING_DIRNAME))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
clientPath = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
if (alloc_get_client_path(handle, ctx,
|
|
fname,
|
|
&clientPath))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
(*dirInfo)->clientPath = talloc_strdup(*dirInfo, clientPath);
|
|
if ((*dirInfo)->clientPath == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
TALLOC_FREE(clientPath);
|
|
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with (*dirInfo)->dirpath '%s', "
|
|
"(*dirInfo)->clientPath '%s'\n",
|
|
(*dirInfo)->dirpath,
|
|
(*dirInfo)->clientPath));
|
|
return status;
|
|
|
|
err:
|
|
DEBUG(MH_ERR_DEBUG, ("Failing with fname '%s'\n", fname));
|
|
TALLOC_FREE(*dirInfo);
|
|
status = -1;
|
|
errno = ENOMEM;
|
|
return status;
|
|
}
|
|
|
|
static DIR *mh_fdopendir(vfs_handle_struct *handle,
|
|
files_struct *fsp,
|
|
const char *mask,
|
|
uint32_t attr)
|
|
{
|
|
struct mh_dirinfo_struct *dirInfo = NULL;
|
|
DIR *dirstream;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("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) {
|
|
goto out;
|
|
}
|
|
|
|
if (set_fake_mtime(handle, fsp, &(fsp->fsp_name), sys_stat))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with dirInfo->dirpath '%s', "
|
|
"dirInfo->clientPath '%s', "
|
|
"fsp->fsp_name->st.st_ex_mtime %s",
|
|
dirInfo->dirpath,
|
|
dirInfo->clientPath,
|
|
ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
|
|
/* Success is freed in closedir. */
|
|
return (DIR *) dirInfo;
|
|
err:
|
|
/* Failure is freed here. */
|
|
DEBUG(MH_ERR_DEBUG, ("Failing with fsp->fsp_name->base_name '%s'\n",
|
|
fsp->fsp_name->base_name));
|
|
TALLOC_FREE(dirInfo);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* skip MDB_FILENAME and PMR_FILENAME filenames and CREATING_DIRNAME
|
|
* directory, skip other client's suffixed MDB_FILENAME and PMR_FILENAME
|
|
* filenames and CREATING_DIRNAME directory, replace this client's
|
|
* suffixed MDB_FILENAME and PMR_FILENAME filenames and CREATING_DIRNAME
|
|
* directory with non suffixed.
|
|
*
|
|
* Success: return dirent
|
|
* End of data: return NULL
|
|
* Failure: set errno, return NULL
|
|
*/
|
|
static struct dirent *mh_readdir(vfs_handle_struct *handle,
|
|
struct files_struct *dirfsp,
|
|
DIR *dirp,
|
|
SMB_STRUCT_STAT *sbuf)
|
|
{
|
|
mh_dirinfo_struct* dirInfo = (mh_dirinfo_struct*)dirp;
|
|
struct dirent *d = NULL;
|
|
int skip;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_readdir\n"));
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("dirInfo->dirpath '%s', "
|
|
"dirInfo->clientPath '%s', "
|
|
"dirInfo->isInMediaFiles '%s', "
|
|
"dirInfo->clientMDBFilename '%s', "
|
|
"dirInfo->clientPMRFilename '%s', "
|
|
"dirInfo->clientCreatingDirname '%s'\n",
|
|
dirInfo->dirpath,
|
|
dirInfo->clientPath,
|
|
dirInfo->isInMediaFiles ? "True" : "False",
|
|
dirInfo->clientMDBFilename,
|
|
dirInfo->clientPMRFilename,
|
|
dirInfo->clientCreatingDirname));
|
|
|
|
if (! dirInfo->isInMediaFiles)
|
|
{
|
|
d = SMB_VFS_NEXT_READDIR(handle, dirfsp, dirInfo->dirstream, sbuf);
|
|
goto out;
|
|
}
|
|
|
|
do
|
|
{
|
|
const char* dname;
|
|
bool isAppleDouble;
|
|
|
|
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;
|
|
}
|
|
|
|
/* skip Avid-special files with no client suffix */
|
|
if (
|
|
strcmp(dname, MDB_FILENAME) == 0
|
|
||
|
|
strcmp(dname, PMR_FILENAME) == 0
|
|
||
|
|
strcmp(dname, CREATING_DIRNAME) == 0
|
|
)
|
|
{
|
|
skip = True;
|
|
}
|
|
/* chop client suffix off this client's suffixed files */
|
|
else if (strcmp(dname, dirInfo->clientMDBFilename) == 0)
|
|
{
|
|
if (isAppleDouble)
|
|
{
|
|
d->d_name[MDB_FILENAME_LEN
|
|
+ APPLE_DOUBLE_PREFIX_LEN] = '\0';
|
|
}
|
|
else
|
|
{
|
|
d->d_name[MDB_FILENAME_LEN] = '\0';
|
|
}
|
|
}
|
|
else if (strcmp(dname, dirInfo->clientPMRFilename) == 0)
|
|
{
|
|
if (isAppleDouble)
|
|
{
|
|
d->d_name[PMR_FILENAME_LEN
|
|
+ APPLE_DOUBLE_PREFIX_LEN] = '\0';
|
|
}
|
|
else
|
|
{
|
|
d->d_name[PMR_FILENAME_LEN] = '\0';
|
|
}
|
|
}
|
|
else if (strcmp(dname, dirInfo->clientCreatingDirname)
|
|
== 0)
|
|
{
|
|
if (isAppleDouble)
|
|
{
|
|
d->d_name[CREATING_DIRNAME_LEN
|
|
+ APPLE_DOUBLE_PREFIX_LEN] = '\0';
|
|
}
|
|
else
|
|
{
|
|
d->d_name[CREATING_DIRNAME_LEN] = '\0';
|
|
}
|
|
}
|
|
/*
|
|
* Anything that starts as an Avid-special file
|
|
* that's made it this far should be skipped. This
|
|
* is different from the original behaviour, which
|
|
* only skipped other client's suffixed files.
|
|
*/
|
|
else if (
|
|
strncmp(MDB_FILENAME, dname,
|
|
MDB_FILENAME_LEN) == 0
|
|
||
|
|
strncmp(PMR_FILENAME, dname,
|
|
PMR_FILENAME_LEN) == 0
|
|
||
|
|
strncmp(CREATING_DIRNAME, dname,
|
|
CREATING_DIRNAME_LEN) == 0
|
|
)
|
|
{
|
|
skip = True;
|
|
}
|
|
}
|
|
while (skip);
|
|
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving mh_readdir\n"));
|
|
return d;
|
|
}
|
|
|
|
/*
|
|
* Success: no success result defined.
|
|
* Failure: no failure result defined.
|
|
*/
|
|
static void mh_seekdir(vfs_handle_struct *handle,
|
|
DIR *dirp,
|
|
long offset)
|
|
{
|
|
DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_seekdir\n"));
|
|
SMB_VFS_NEXT_SEEKDIR(handle,
|
|
((mh_dirinfo_struct*)dirp)->dirstream, offset);
|
|
}
|
|
|
|
/*
|
|
* Success: return long
|
|
* Failure: no failure result defined.
|
|
*/
|
|
static long mh_telldir(vfs_handle_struct *handle,
|
|
DIR *dirp)
|
|
{
|
|
DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_telldir\n"));
|
|
return SMB_VFS_NEXT_TELLDIR(handle,
|
|
((mh_dirinfo_struct*)dirp)->dirstream);
|
|
}
|
|
|
|
/*
|
|
* Success: no success result defined.
|
|
* Failure: no failure result defined.
|
|
*/
|
|
static void mh_rewinddir(vfs_handle_struct *handle,
|
|
DIR *dirp)
|
|
{
|
|
DEBUG(MH_INFO_DEBUG, ("Entering and leaving mh_rewinddir\n"));
|
|
SMB_VFS_NEXT_REWINDDIR(handle,
|
|
((mh_dirinfo_struct*)dirp)->dirstream);
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_mkdirat(vfs_handle_struct *handle,
|
|
struct files_struct *dirfsp,
|
|
const struct smb_filename *smb_fname,
|
|
mode_t mode)
|
|
{
|
|
int status;
|
|
struct smb_filename *clientFname = NULL;
|
|
const char *path = smb_fname->base_name;
|
|
struct smb_filename *full_fname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with path '%s'\n", path));
|
|
|
|
if (!is_in_media_files(path)) {
|
|
status = SMB_VFS_NEXT_MKDIRAT(handle,
|
|
dirfsp,
|
|
smb_fname,
|
|
mode);
|
|
goto out;
|
|
}
|
|
|
|
full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dirfsp,
|
|
smb_fname);
|
|
if (full_fname == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
full_fname,
|
|
&clientFname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_MKDIRAT(handle,
|
|
handle->conn->cwd_fsp,
|
|
clientFname,
|
|
mode);
|
|
err:
|
|
TALLOC_FREE(full_fname);
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with path '%s'\n", path));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_closedir(vfs_handle_struct *handle,
|
|
DIR *dirp)
|
|
{
|
|
DIR *realdirp = ((mh_dirinfo_struct*)dirp)->dirstream;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_closedir\n"));
|
|
// Will this talloc_free destroy realdirp?
|
|
TALLOC_FREE(dirp);
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving mh_closedir\n"));
|
|
return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
|
|
}
|
|
|
|
/*
|
|
* Success: return non-negative file descriptor
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_openat(struct vfs_handle_struct *handle,
|
|
const struct files_struct *dirfsp,
|
|
const struct smb_filename *smb_fname,
|
|
files_struct *fsp,
|
|
const struct vfs_open_how *how)
|
|
{
|
|
int ret;
|
|
struct smb_filename *clientFname;
|
|
TALLOC_CTX *ctx;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
ret = SMB_VFS_NEXT_OPENAT(handle,
|
|
dirfsp,
|
|
smb_fname,
|
|
fsp,
|
|
how);
|
|
goto out;
|
|
}
|
|
|
|
clientFname = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
if (alloc_get_client_smb_fname(handle, ctx, smb_fname, &clientFname)) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* What about fsp->fsp_name? We also have to get correct stat info into
|
|
* fsp and smb_fname for DB files, don't we?
|
|
*/
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->base_name '%s' "
|
|
"smb_fname->st.st_ex_mtime %s"
|
|
" fsp->fsp_name->st.st_ex_mtime %s",
|
|
smb_fname->base_name,
|
|
ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
|
|
ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
|
|
|
|
ret = SMB_VFS_NEXT_OPENAT(handle, dirfsp, clientFname, fsp, how);
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Success: return non-negative file descriptor
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static NTSTATUS mh_create_file(vfs_handle_struct *handle,
|
|
struct smb_request *req,
|
|
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 *clientFname;
|
|
TALLOC_CTX *ctx;
|
|
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
if (!is_in_media_files(smb_fname->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_CREATE_FILE(
|
|
handle,
|
|
req,
|
|
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);
|
|
goto out;
|
|
}
|
|
|
|
clientFname = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
if (alloc_get_client_smb_fname(handle, ctx,
|
|
smb_fname,
|
|
&clientFname))
|
|
{
|
|
status = map_nt_error_from_unix(errno);
|
|
goto err;
|
|
}
|
|
|
|
/* This only creates files, so we don't have to worry about
|
|
* our fake directory stat'ing here.
|
|
*/
|
|
// But we still need to route stat calls for DB files
|
|
// properly, right?
|
|
status = SMB_VFS_NEXT_CREATE_FILE(
|
|
handle,
|
|
req,
|
|
dirfsp,
|
|
clientFname,
|
|
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(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->base_name '%s'"
|
|
"smb_fname->st.st_ex_mtime %s"
|
|
" fsp->fsp_name->st.st_ex_mtime %s",
|
|
smb_fname->base_name,
|
|
ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
|
|
(*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
|
|
ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
|
|
"No fsp time\n"));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_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 = -1;
|
|
struct smb_filename *full_fname_src = NULL;
|
|
struct smb_filename *full_fname_dst = NULL;
|
|
struct smb_filename *srcClientFname = NULL;
|
|
struct smb_filename *dstClientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with "
|
|
"smb_fname_src->base_name '%s', "
|
|
"smb_fname_dst->base_name '%s'\n",
|
|
smb_fname_src->base_name,
|
|
smb_fname_dst->base_name));
|
|
|
|
if (!is_in_media_files(smb_fname_src->base_name)
|
|
&&
|
|
!is_in_media_files(smb_fname_dst->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_RENAMEAT(handle,
|
|
srcfsp,
|
|
smb_fname_src,
|
|
dstfsp,
|
|
smb_fname_dst);
|
|
goto out;
|
|
}
|
|
|
|
full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
|
|
srcfsp,
|
|
smb_fname_src);
|
|
if (full_fname_src == NULL) {
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dstfsp,
|
|
smb_fname_dst);
|
|
if (full_fname_dst == NULL) {
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
full_fname_src,
|
|
&srcClientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
full_fname_dst,
|
|
&dstClientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_RENAMEAT(handle,
|
|
srcfsp->conn->cwd_fsp,
|
|
srcClientFname,
|
|
dstfsp->conn->cwd_fsp,
|
|
dstClientFname);
|
|
err:
|
|
TALLOC_FREE(full_fname_src);
|
|
TALLOC_FREE(full_fname_dst);
|
|
TALLOC_FREE(dstClientFname);
|
|
TALLOC_FREE(srcClientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname_src->base_name '%s',"
|
|
" smb_fname_dst->base_name '%s'\n",
|
|
smb_fname_src->base_name,
|
|
smb_fname_dst->base_name));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_stat(vfs_handle_struct *handle,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
int status = 0;
|
|
struct smb_filename *clientFname;
|
|
TALLOC_CTX *ctx;
|
|
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_STAT(handle, smb_fname);
|
|
goto out;
|
|
}
|
|
|
|
clientFname = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, ctx,
|
|
smb_fname,
|
|
&clientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
DEBUG(MH_INFO_DEBUG, ("Stat'ing clientFname->base_name '%s'\n",
|
|
clientFname->base_name));
|
|
if ((status = SMB_VFS_NEXT_STAT(handle, clientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
if ((status = set_fake_mtime(handle, ctx, &clientFname, sys_stat)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
/* Unlike functions with const smb_filename, we have to
|
|
* modify smb_fname itself to pass our info back up.
|
|
*/
|
|
DEBUG(MH_INFO_DEBUG, ("Setting smb_fname '%s' stat "
|
|
"from clientFname '%s'\n",
|
|
smb_fname->base_name,
|
|
clientFname->base_name));
|
|
smb_fname->st = clientFname->st;
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->st.st_ex_mtime %s",
|
|
ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_lstat(vfs_handle_struct *handle,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
int status = 0;
|
|
struct smb_filename *clientFname;
|
|
TALLOC_CTX *ctx;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with smb_fname->base_name '%s'\n",
|
|
smb_fname->base_name));
|
|
|
|
if (!is_in_media_files(smb_fname->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
|
|
goto out;
|
|
}
|
|
|
|
clientFname = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, ctx,
|
|
smb_fname,
|
|
&clientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
if ((status = SMB_VFS_NEXT_LSTAT(handle, clientFname)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if ((status = set_fake_mtime(handle, ctx, &clientFname, sys_lstat)))
|
|
{
|
|
goto err;
|
|
}
|
|
/* Unlike functions with const smb_filename, we have to
|
|
* modify smb_fname itself to pass our info back up.
|
|
*/
|
|
smb_fname->st = clientFname->st;
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with smb_fname->st.st_ex_mtime %s",
|
|
ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_fstat(vfs_handle_struct *handle,
|
|
files_struct *fsp, SMB_STRUCT_STAT *sbuf)
|
|
{
|
|
int status = 0;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering with fsp->fsp_name->base_name "
|
|
"'%s'\n", fsp_str_dbg(fsp)));
|
|
|
|
if ((status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf)))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
if (fsp->fsp_name == NULL
|
|
|| !is_in_media_files(fsp->fsp_name->base_name))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
if ((status = mh_stat(handle, fsp->fsp_name)))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
*sbuf = fsp->fsp_name->st;
|
|
out:
|
|
DEBUG(MH_INFO_DEBUG, ("Leaving with fsp->fsp_name->st.st_ex_mtime "
|
|
"%s",
|
|
fsp->fsp_name != NULL ?
|
|
ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) :
|
|
"0"));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_unlinkat(vfs_handle_struct *handle,
|
|
struct files_struct *dirfsp,
|
|
const struct smb_filename *smb_fname,
|
|
int flags)
|
|
{
|
|
int status;
|
|
struct smb_filename *full_fname = NULL;
|
|
struct smb_filename *clientFname;
|
|
TALLOC_CTX *ctx;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_unlinkat\n"));
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
status = SMB_VFS_NEXT_UNLINKAT(handle,
|
|
dirfsp,
|
|
smb_fname,
|
|
flags);
|
|
goto out;
|
|
}
|
|
|
|
clientFname = NULL;
|
|
ctx = talloc_tos();
|
|
|
|
full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dirfsp,
|
|
smb_fname);
|
|
if (full_fname == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, ctx,
|
|
full_fname,
|
|
&clientFname))) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_UNLINKAT(handle,
|
|
dirfsp->conn->cwd_fsp,
|
|
clientFname,
|
|
flags);
|
|
err:
|
|
TALLOC_FREE(full_fname);
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_lchown(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname,
|
|
uid_t uid,
|
|
gid_t gid)
|
|
{
|
|
int status;
|
|
struct smb_filename *clientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_lchown\n"));
|
|
if (!is_in_media_files(smb_fname->base_name))
|
|
{
|
|
status = SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid);
|
|
goto out;
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&clientFname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_LCHOWN(handle, clientFname, uid, gid);
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_chdir(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname)
|
|
{
|
|
int status;
|
|
struct smb_filename *clientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_chdir\n"));
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
status = SMB_VFS_NEXT_CHDIR(handle, smb_fname);
|
|
goto out;
|
|
}
|
|
|
|
status = alloc_get_client_smb_fname(handle,
|
|
talloc_tos(),
|
|
smb_fname,
|
|
&clientFname);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_CHDIR(handle, clientFname);
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
|
|
static int mh_symlinkat(vfs_handle_struct *handle,
|
|
const struct smb_filename *link_contents,
|
|
struct files_struct *dirfsp,
|
|
const struct smb_filename *new_smb_fname)
|
|
{
|
|
int status = -1;
|
|
struct smb_filename *full_fname = NULL;
|
|
struct smb_filename *new_link_target = NULL;
|
|
struct smb_filename *newclientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_symlinkat\n"));
|
|
|
|
full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dirfsp,
|
|
new_smb_fname);
|
|
if (full_fname == NULL) {
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
if (!is_in_media_files(link_contents->base_name) &&
|
|
!is_in_media_files(full_fname->base_name)) {
|
|
status = SMB_VFS_NEXT_SYMLINKAT(handle,
|
|
link_contents,
|
|
dirfsp,
|
|
new_smb_fname);
|
|
goto out;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, talloc_tos(),
|
|
link_contents,
|
|
&new_link_target))) {
|
|
goto err;
|
|
}
|
|
if ((status = alloc_get_client_smb_fname(handle, talloc_tos(),
|
|
full_fname,
|
|
&newclientFname))) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_SYMLINKAT(handle,
|
|
new_link_target,
|
|
handle->conn->cwd_fsp,
|
|
newclientFname);
|
|
err:
|
|
TALLOC_FREE(new_link_target);
|
|
TALLOC_FREE(newclientFname);
|
|
out:
|
|
TALLOC_FREE(full_fname);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return byte count
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_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 *full_fname = NULL;
|
|
struct smb_filename *clientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_readlinkat\n"));
|
|
full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dirfsp,
|
|
smb_fname);
|
|
if (full_fname == NULL) {
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
if (!is_in_media_files(full_fname->base_name)) {
|
|
status = SMB_VFS_NEXT_READLINKAT(handle,
|
|
dirfsp,
|
|
smb_fname,
|
|
buf,
|
|
bufsiz);
|
|
goto out;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, talloc_tos(),
|
|
full_fname,
|
|
&clientFname))) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_READLINKAT(handle,
|
|
handle->conn->cwd_fsp,
|
|
clientFname,
|
|
buf,
|
|
bufsiz);
|
|
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
TALLOC_FREE(full_fname);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_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 *oldclientFname = NULL;
|
|
struct smb_filename *new_full_fname = NULL;
|
|
struct smb_filename *newclientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_linkat\n"));
|
|
|
|
old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
srcfsp,
|
|
old_smb_fname);
|
|
if (old_full_fname == NULL) {
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dstfsp,
|
|
new_smb_fname);
|
|
if (new_full_fname == NULL) {
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
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);
|
|
|
|
status = SMB_VFS_NEXT_LINKAT(handle,
|
|
srcfsp,
|
|
old_smb_fname,
|
|
dstfsp,
|
|
new_smb_fname,
|
|
flags);
|
|
goto out;
|
|
}
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, talloc_tos(),
|
|
old_full_fname,
|
|
&oldclientFname))) {
|
|
goto err;
|
|
}
|
|
if ((status = alloc_get_client_smb_fname(handle, talloc_tos(),
|
|
new_full_fname,
|
|
&newclientFname))) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_LINKAT(handle,
|
|
handle->conn->cwd_fsp,
|
|
oldclientFname,
|
|
handle->conn->cwd_fsp,
|
|
newclientFname,
|
|
flags);
|
|
|
|
err:
|
|
TALLOC_FREE(old_full_fname);
|
|
TALLOC_FREE(new_full_fname);
|
|
TALLOC_FREE(newclientFname);
|
|
TALLOC_FREE(oldclientFname);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
*/
|
|
static int mh_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 *full_fname = NULL;
|
|
struct smb_filename *clientFname = NULL;
|
|
TALLOC_CTX *ctx;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_mknodat\n"));
|
|
|
|
full_fname = full_path_from_dirfsp_atname(talloc_tos(),
|
|
dirfsp,
|
|
smb_fname);
|
|
if (full_fname == NULL) {
|
|
status = -1;
|
|
goto err;
|
|
}
|
|
|
|
if (!is_in_media_files(full_fname->base_name)) {
|
|
status = SMB_VFS_NEXT_MKNODAT(handle,
|
|
dirfsp,
|
|
smb_fname,
|
|
mode,
|
|
dev);
|
|
goto out;
|
|
}
|
|
|
|
ctx = talloc_tos();
|
|
|
|
if ((status = alloc_get_client_smb_fname(handle, ctx,
|
|
full_fname,
|
|
&clientFname))) {
|
|
goto err;
|
|
}
|
|
|
|
status = SMB_VFS_NEXT_MKNODAT(handle,
|
|
handle->conn->cwd_fsp,
|
|
clientFname,
|
|
mode,
|
|
dev);
|
|
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
out:
|
|
TALLOC_FREE(full_fname);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Success: return path pointer
|
|
* Failure: set errno, return NULL pointer
|
|
*/
|
|
static struct smb_filename *mh_realpath(vfs_handle_struct *handle,
|
|
TALLOC_CTX *ctx,
|
|
const struct smb_filename *smb_fname)
|
|
{
|
|
struct smb_filename *result_fname = NULL;
|
|
struct smb_filename *clientFname = NULL;
|
|
|
|
DEBUG(MH_INFO_DEBUG, ("Entering mh_realpath\n"));
|
|
if (!is_in_media_files(smb_fname->base_name)) {
|
|
return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname);
|
|
}
|
|
|
|
if (alloc_get_client_smb_fname(handle, ctx,
|
|
smb_fname,
|
|
&clientFname) != 0) {
|
|
goto err;
|
|
}
|
|
|
|
result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, clientFname);
|
|
err:
|
|
TALLOC_FREE(clientFname);
|
|
return result_fname;
|
|
}
|
|
|
|
/* Ignoring get_real_filename function because the default
|
|
* doesn't do anything.
|
|
*/
|
|
|
|
/*
|
|
* Success: return 0
|
|
* Failure: set errno, return -1
|
|
* In this case, "name" is an attr name.
|
|
*/
|
|
|
|
/* VFS operations structure */
|
|
|
|
static struct vfs_fn_pointers vfs_mh_fns = {
|
|
/* Disk operations */
|
|
|
|
.statvfs_fn = mh_statvfs,
|
|
|
|
/* Directory operations */
|
|
|
|
.fdopendir_fn = mh_fdopendir,
|
|
.readdir_fn = mh_readdir,
|
|
.seekdir_fn = mh_seekdir,
|
|
.telldir_fn = mh_telldir,
|
|
.rewind_dir_fn = mh_rewinddir,
|
|
.mkdirat_fn = mh_mkdirat,
|
|
.closedir_fn = mh_closedir,
|
|
|
|
/* File operations */
|
|
|
|
.openat_fn = mh_openat,
|
|
.create_file_fn = mh_create_file,
|
|
.renameat_fn = mh_renameat,
|
|
.stat_fn = mh_stat,
|
|
.lstat_fn = mh_lstat,
|
|
.fstat_fn = mh_fstat,
|
|
.unlinkat_fn = mh_unlinkat,
|
|
.lchown_fn = mh_lchown,
|
|
.chdir_fn = mh_chdir,
|
|
.symlinkat_fn = mh_symlinkat,
|
|
.readlinkat_fn = mh_readlinkat,
|
|
.linkat_fn = mh_linkat,
|
|
.mknodat_fn = mh_mknodat,
|
|
.realpath_fn = mh_realpath,
|
|
|
|
/* EA operations. */
|
|
.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
|
|
.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
|
|
|
|
/* aio operations */
|
|
};
|
|
|
|
static_decl_vfs;
|
|
NTSTATUS vfs_media_harmony_init(TALLOC_CTX *ctx)
|
|
{
|
|
NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
|
|
"media_harmony", &vfs_mh_fns);
|
|
if (!NT_STATUS_IS_OK(ret))
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
vfs_mh_debug_level = debug_add_class("media_harmony");
|
|
|
|
if (vfs_mh_debug_level == -1) {
|
|
vfs_mh_debug_level = DBGC_VFS;
|
|
DEBUG(1, ("media_harmony_init: Couldn't register custom "
|
|
"debugging class.\n"));
|
|
} else {
|
|
DEBUG(3, ("media_harmony_init: Debug class number of "
|
|
"'media_harmony': %d\n",
|
|
vfs_mh_debug_level));
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|