2 * Samba VFS module supporting multiple AVID clients sharing media.
4 * Copyright (C) 2005 Philip de Nier <philipn@users.sourceforge.net>
5 * Copyright (C) 2012 Andrew Klaassen <clawsoon@yahoo.com>
6 * Copyright (C) 2013 Milos Lukacek
7 * Copyright (C) 2013 Ralph Boehme <slow@samba.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 * Unityed Media is a Samba VFS module that allows multiple AVID
27 * clients to share media.
29 * Add this module to the vfs objects option in your Samba share
35 * vfs objects = unityed_media
38 * It is recommended that you separate out Samba shares for Mac
39 * and Windows clients, and add the following options to the shares
40 * for Windows clients (NOTE: replace @ with *):
42 * veto files = /.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/
43 * delete veto files = yes
45 * This prevents hidden files from Mac clients interfering with Windows
46 * clients. If you find any more problem hidden files then add them to
50 * This module is designed to work with AVID editing applications that
51 * look in the Avid MediaFiles or OMFI MediaFiles directory for media.
52 * It is not designed to work as expected in all circumstances for
58 #include "system/filesys.h"
59 #include "smbd/smbd.h"
60 #include "../smbd/globals.h"
62 #include "../lib/tsocket/tsocket.h"
63 #include "lib/util/smb_strtox.h"
66 #define UM_PARAM_TYPE_NAME "unityed_media"
68 static const char *AVID_MXF_DIRNAME = "Avid MediaFiles/MXF";
69 static const size_t AVID_MXF_DIRNAME_LEN = 19;
70 static const char *OMFI_MEDIAFILES_DIRNAME = "OMFI MediaFiles";
71 static const size_t OMFI_MEDIAFILES_DIRNAME_LEN = 15;
72 static const char *APPLE_DOUBLE_PREFIX = "._";
73 static const size_t APPLE_DOUBLE_PREFIX_LEN = 2;
74 static int vfs_um_debug_level = DBGC_VFS;
76 enum um_clientid {UM_CLIENTID_NAME, UM_CLIENTID_IP, UM_CLIENTID_HOSTNAME};
78 struct um_config_data {
79 enum um_clientid clientid;
82 static const struct enum_list um_clientid[] = {
83 {UM_CLIENTID_NAME, "user"},
84 {UM_CLIENTID_IP, "ip"},
85 {UM_CLIENTID_HOSTNAME, "hostname"},
89 /* supplements the directory list stream */
90 typedef struct um_dirinfo_struct {
95 char *clientSubDirname;
99 * Returns true and first group of digits in path, false and 0 otherwise
101 static bool get_digit_group(const char *path, uintmax_t *digit)
103 const char *p = path;
108 DEBUG(10, ("get_digit_group entering with path '%s'\n",
112 * Delibiretly initialize to 0 because callers use this result
113 * even though the string doesn't contain any number and we
119 cp = next_codepoint(p, &size);
123 if ((size == 1) && (isdigit(cp))) {
124 *digit = (uintmax_t)smb_strtoul(p,
132 DEBUG(10, ("num_suffix = '%ju'\n",
142 /* Add "_<remote_name>.<number>" suffix to path or filename.
145 * Failure: set errno, path NULL, return -1
148 static int alloc_append_client_suffix(vfs_handle_struct *handle,
153 const char *clientid;
154 struct um_config_data *config;
156 DEBUG(10, ("Entering with path '%s'\n", *path));
158 SMB_VFS_HANDLE_GET_DATA(handle, config,
159 struct um_config_data,
162 (void)get_digit_group(*path, &number);
164 switch (config->clientid) {
167 clientid = tsocket_address_inet_addr_string(
168 handle->conn->sconn->remote_address, talloc_tos());
169 if (clientid == NULL) {
176 case UM_CLIENTID_HOSTNAME:
177 clientid = get_remote_machine_name();
180 case UM_CLIENTID_NAME:
182 clientid = get_current_username();
186 *path = talloc_asprintf_append(*path, "_%s.%ju",
189 DEBUG(1, ("alloc_append_client_suffix "
195 DEBUG(10, ("Leaving with *path '%s'\n", *path));
200 /* Returns true if the file or directory begins with the appledouble
203 static bool is_apple_double(const char* fname)
207 DEBUG(10, ("Entering with fname '%s'\n", fname));
209 if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
212 DEBUG(10, ("Leaving with ret '%s'\n",
213 ret == true ? "true" : "false"));
217 static bool starts_with_media_dir(const char* media_dirname,
218 size_t media_dirname_len,
222 const char *path_start = path;
224 DEBUG(10, ("Entering with media_dirname '%s' "
225 "path '%s'\n", media_dirname, path));
227 /* Sometimes Samba gives us "./OMFI MediaFiles". */
228 if (strnequal(path, "./", 2)) {
232 if (strnequal(media_dirname, path_start, media_dirname_len)
234 ((path_start[media_dirname_len] == '\0') ||
235 (path_start[media_dirname_len] == '/'))) {
239 DEBUG(10, ("Leaving with ret '%s'\n",
240 ret == true ? "true" : "false"));
245 * Returns true if the file or directory referenced by the path is ONE
246 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
249 static bool is_in_media_dir(const char *path)
251 int transition_count = 0;
252 const char *path_start = path;
254 const char *media_dirname;
255 size_t media_dirname_len;
257 DEBUG(10, ("Entering with path'%s' ", path));
259 /* Sometimes Samba gives us "./OMFI MediaFiles". */
260 if (strnequal(path, "./", 2)) {
264 if (strnequal(path_start, AVID_MXF_DIRNAME, AVID_MXF_DIRNAME_LEN)) {
265 media_dirname = AVID_MXF_DIRNAME;
266 media_dirname_len = AVID_MXF_DIRNAME_LEN;
267 } else if (strnequal(path_start,
268 OMFI_MEDIAFILES_DIRNAME,
269 OMFI_MEDIAFILES_DIRNAME_LEN)) {
270 media_dirname = OMFI_MEDIAFILES_DIRNAME;
271 media_dirname_len = OMFI_MEDIAFILES_DIRNAME_LEN;
276 if (path_start[media_dirname_len] == '\0') {
280 p = path_start + media_dirname_len + 1;
283 if (*p == '\0' || *p == '/') {
284 if (strnequal(p - 3, "/..", 3)) {
286 } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
297 DEBUG(10, ("Going out with transition_count '%i'\n",
299 if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
301 ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
308 * Returns true if the file or directory referenced by the path is
309 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
310 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
311 * are assumed to be in the root directory, which is generally a safe
312 * assumption in the fixed-path world of Avid.
314 static bool is_in_media_files(const char *path)
318 DEBUG(10, ("Entering with path '%s'\n", path));
320 if (starts_with_media_dir(AVID_MXF_DIRNAME,
321 AVID_MXF_DIRNAME_LEN, path) ||
322 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
323 OMFI_MEDIAFILES_DIRNAME_LEN, path)) {
326 DEBUG(10, ("Leaving with ret '%s'\n",
327 ret == true ? "true" : "false"));
332 /* Add client suffix to "pure-number" path.
334 * Caller must free newPath.
337 * Failure: set errno, newPath NULL, return -1
339 static int alloc_get_client_path(vfs_handle_struct *handle,
350 *path_out = talloc_strdup(ctx, path_in);
351 if (*path_out == NULL) {
352 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
356 (void)get_digit_group(*path_out, &number);
358 digits = talloc_asprintf(NULL, "%ju", number);
359 if (digits == NULL) {
360 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
363 digits_len = strlen(digits);
365 p = strstr_m(path_in, digits);
368 ((p[digits_len] == '\0') || (p[digits_len] == '/'))
370 (((p - path_in > 0) && (p[-1] == '/'))
372 (((p - path_in) > APPLE_DOUBLE_PREFIX_LEN)
374 is_apple_double(p - APPLE_DOUBLE_PREFIX_LEN)
376 (p[-(APPLE_DOUBLE_PREFIX_LEN + 1)] == '/'))))
378 (*path_out)[p - path_in + digits_len] = '\0';
380 status = alloc_append_client_suffix(handle, path_out);
385 *path_out = talloc_strdup_append(*path_out, p + digits_len);
386 if (*path_out == NULL) {
387 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
393 /* path_out must be freed in caller. */
394 DEBUG(10, ("Result:'%s'\n", *path_out));
400 * Failure: set errno, return -1
402 static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
404 const struct smb_filename *smb_fname,
405 struct smb_filename **client_fname)
409 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
410 smb_fname->base_name));
412 *client_fname = cp_smb_filename(ctx, smb_fname);
413 if (*client_fname == NULL) {
414 DEBUG(1, ("cp_smb_filename returned NULL\n"));
417 status = alloc_get_client_path(handle, ctx,
418 smb_fname->base_name,
419 &(*client_fname)->base_name);
424 DEBUG(10, ("Leaving with (*client_fname)->base_name "
425 "'%s'\n", (*client_fname)->base_name));
433 * Failure: set errno, return -1
435 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
438 const char *suffix_number)
442 DEBUG(10, ("Entering with suffix_number '%s'\n",
445 *path = talloc_strdup(ctx, suffix_number);
447 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
450 status = alloc_append_client_suffix(handle, path);
455 DEBUG(10, ("Leaving with *path '%s'\n", *path));
460 static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
462 struct um_dirinfo_struct **di_result)
467 struct um_dirinfo_struct *dip;
469 DEBUG(10, ("Entering with fname '%s'\n", fname));
471 *di_result = talloc(NULL, struct um_dirinfo_struct);
472 if (*di_result == NULL) {
477 dip->dirpath = talloc_strdup(dip, fname);
478 if (dip->dirpath == NULL) {
482 if (!is_in_media_files(fname)) {
483 dip->isInMediaFiles = false;
484 dip->clientPath = NULL;
485 dip->clientSubDirname = NULL;
489 dip->isInMediaFiles = true;
491 (void)get_digit_group(fname, &number);
492 digits = talloc_asprintf(talloc_tos(), "%ju", number);
493 if (digits == NULL) {
497 status = alloc_set_client_dirinfo_path(handle, dip,
498 &dip->clientSubDirname,
504 status = alloc_get_client_path(handle, dip, fname,
506 if (status != 0 || dip->clientPath == NULL) {
511 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
512 "(*dirInfo)->clientPath '%s'\n",
513 dip->dirpath, dip->clientPath));
517 DEBUG(1, ("Failing with fname '%s'\n", fname));
518 TALLOC_FREE(*di_result);
524 /**********************************************************************
526 **********************************************************************/
530 * Failure: set errno, return -1
532 static int um_statvfs(struct vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname,
534 struct vfs_statvfs_struct *statbuf)
537 struct smb_filename *client_fname = NULL;
539 DEBUG(10, ("Entering with path '%s'\n", smb_fname->base_name));
541 if (!is_in_media_files(smb_fname->base_name)) {
542 return SMB_VFS_NEXT_STATVFS(handle, smb_fname, statbuf);
545 status = alloc_get_client_smb_fname(handle,
553 status = SMB_VFS_NEXT_STATVFS(handle, client_fname, statbuf);
555 TALLOC_FREE(client_fname);
556 DEBUG(10, ("Leaving with path '%s'\n", smb_fname->base_name));
560 static DIR *um_fdopendir(vfs_handle_struct *handle,
565 struct um_dirinfo_struct *dirInfo = NULL;
568 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
569 fsp->fsp_name->base_name));
571 dirstream = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
576 if (alloc_set_client_dirinfo(handle,
577 fsp->fsp_name->base_name,
582 dirInfo->dirstream = dirstream;
584 if (!dirInfo->isInMediaFiles) {
586 * FIXME: this is the original code, something must be
587 * missing here, but what? -slow
593 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
594 "dirInfo->clientPath '%s', "
595 "fsp->fsp_name->st.st_ex_mtime %s",
598 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
599 return (DIR *) dirInfo;
602 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
603 fsp->fsp_name->base_name));
604 TALLOC_FREE(dirInfo);
609 * skip own suffixed directory
610 * replace own suffixed directory with non suffixed.
612 * Success: return dirent
613 * End of data: return NULL
614 * Failure: set errno, return NULL
616 static struct dirent *um_readdir(vfs_handle_struct *handle,
618 SMB_STRUCT_STAT *sbuf)
620 um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
621 struct dirent *d = NULL;
624 DEBUG(10, ("dirInfo->dirpath '%s', "
625 "dirInfo->clientPath '%s', "
626 "dirInfo->isInMediaFiles '%s', "
627 "dirInfo->clientSubDirname '%s'\n",
630 dirInfo->isInMediaFiles ? "true" : "false",
631 dirInfo->clientSubDirname));
633 if (!dirInfo->isInMediaFiles) {
634 return SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
645 d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
651 /* ignore apple double prefix for logic below */
652 if (is_apple_double(d->d_name)) {
653 dname = &d->d_name[APPLE_DOUBLE_PREFIX_LEN];
654 isAppleDouble = true;
657 isAppleDouble = false;
660 DEBUG(10, ("dname = '%s'\n", dname));
662 (void)get_digit_group(dname, &number);
663 digits = talloc_asprintf(talloc_tos(), "%ju", number);
664 if (digits == NULL) {
665 DEBUG(1, ("out of memory"));
668 digits_len = strlen(digits);
670 if (alloc_set_client_dirinfo_path(handle,
672 &((dirInfo)->clientSubDirname),
678 * If set to "true", vfs shows digits-only
679 * non-suffixed subdirectories. Normally, such
680 * subdirectories can exists only in non-media
681 * directories, so we set it to "false". Otherwise,
682 * if we have such subdirectories (probably created
683 * over not "unityed" connection), it can be little
686 if (strequal(dname, digits)) {
688 } else if (strequal(dname, dirInfo->clientSubDirname)) {
690 * Remove suffix of this client's suffixed
694 d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
696 d->d_name[digits_len] = '\0';
698 } else if (strnequal(digits, dname, digits_len)) {
700 * Set to false to see another clients subdirectories
706 DEBUG(10, ("Leaving um_readdir\n"));
709 TALLOC_FREE(dirInfo);
713 static void um_seekdir(vfs_handle_struct *handle,
717 DEBUG(10, ("Entering and leaving um_seekdir\n"));
718 SMB_VFS_NEXT_SEEKDIR(handle,
719 ((um_dirinfo_struct*)dirp)->dirstream, offset);
722 static long um_telldir(vfs_handle_struct *handle,
725 DEBUG(10, ("Entering and leaving um_telldir\n"));
726 return SMB_VFS_NEXT_TELLDIR(handle,
727 ((um_dirinfo_struct*)dirp)->dirstream);
730 static void um_rewinddir(vfs_handle_struct *handle,
733 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
734 SMB_VFS_NEXT_REWINDDIR(handle,
735 ((um_dirinfo_struct*)dirp)->dirstream);
738 static int um_mkdirat(vfs_handle_struct *handle,
739 struct files_struct *dirfsp,
740 const struct smb_filename *smb_fname,
744 const char *path = smb_fname->base_name;
745 struct smb_filename *client_fname = NULL;
747 DEBUG(10, ("Entering with path '%s'\n", path));
749 if (!is_in_media_files(path) || !is_in_media_dir(path)) {
750 return SMB_VFS_NEXT_MKDIRAT(handle,
756 status = alloc_get_client_smb_fname(handle,
764 status = SMB_VFS_NEXT_MKDIRAT(handle,
769 TALLOC_FREE(client_fname);
770 DEBUG(10, ("Leaving with path '%s'\n", path));
774 static int um_closedir(vfs_handle_struct *handle,
777 DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
781 return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
784 static int um_openat(struct vfs_handle_struct *handle,
785 const struct files_struct *dirfsp,
786 const struct smb_filename *smb_fname,
787 struct files_struct *fsp,
791 struct smb_filename *client_fname = NULL;
794 DBG_DEBUG("Entering with smb_fname->base_name '%s'\n",
795 smb_fname->base_name);
797 if (!is_in_media_files(smb_fname->base_name)) {
798 return SMB_VFS_NEXT_OPENAT(handle,
806 if (alloc_get_client_smb_fname(handle, talloc_tos(),
815 * What about fsp->fsp_name? We also have to get correct stat
816 * info into fsp and smb_fname for DB files, don't we?
819 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
820 "smb_fname->st.st_ex_mtime %s"
821 "fsp->fsp_name->st.st_ex_mtime %s",
822 smb_fname->base_name,
823 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
824 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
826 ret = SMB_VFS_NEXT_OPENAT(handle,
833 TALLOC_FREE(client_fname);
834 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
835 smb_fname->base_name));
839 static NTSTATUS um_create_file(vfs_handle_struct *handle,
840 struct smb_request *req,
841 struct smb_filename *smb_fname,
842 uint32_t access_mask,
843 uint32_t share_access,
844 uint32_t create_disposition,
845 uint32_t create_options,
846 uint32_t file_attributes,
847 uint32_t oplock_request,
848 const struct smb2_lease *lease,
849 uint64_t allocation_size,
850 uint32_t private_flags,
851 struct security_descriptor *sd,
852 struct ea_list *ea_list,
853 files_struct **result_fsp,
855 const struct smb2_create_blobs *in_context_blobs,
856 struct smb2_create_blobs *out_context_blobs)
859 struct smb_filename *client_fname = NULL;
861 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
862 smb_fname->base_name));
864 if (!is_in_media_files(smb_fname->base_name)) {
865 return SMB_VFS_NEXT_CREATE_FILE(
886 if (alloc_get_client_smb_fname(handle, talloc_tos(),
889 status = map_nt_error_from_unix(errno);
895 * This only creates files, so we don't have to worry about
896 * our fake directory stat'ing here. But we still need to
897 * route stat calls for DB files properly, right?
899 status = SMB_VFS_NEXT_CREATE_FILE(
919 TALLOC_FREE(client_fname);
920 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
921 "smb_fname->st.st_ex_mtime %s"
922 " fsp->fsp_name->st.st_ex_mtime %s",
923 smb_fname->base_name,
924 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
925 (*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
926 ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
931 static int um_renameat(vfs_handle_struct *handle,
932 files_struct *srcfsp,
933 const struct smb_filename *smb_fname_src,
934 files_struct *dstfsp,
935 const struct smb_filename *smb_fname_dst)
938 struct smb_filename *src_client_fname = NULL;
939 struct smb_filename *dst_client_fname = NULL;
941 DEBUG(10, ("Entering with "
942 "smb_fname_src->base_name '%s', "
943 "smb_fname_dst->base_name '%s'\n",
944 smb_fname_src->base_name,
945 smb_fname_dst->base_name));
947 if (!is_in_media_files(smb_fname_src->base_name)
949 !is_in_media_files(smb_fname_dst->base_name)) {
950 return SMB_VFS_NEXT_RENAMEAT(handle,
957 status = alloc_get_client_smb_fname(handle, talloc_tos(),
964 status = alloc_get_client_smb_fname(handle, talloc_tos(),
972 status = SMB_VFS_NEXT_RENAMEAT(handle,
979 TALLOC_FREE(dst_client_fname);
980 TALLOC_FREE(src_client_fname);
981 DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
982 " smb_fname_dst->base_name '%s'\n",
983 smb_fname_src->base_name,
984 smb_fname_dst->base_name));
991 * Failure: set errno, return -1
993 static int um_stat(vfs_handle_struct *handle,
994 struct smb_filename *smb_fname)
997 struct smb_filename *client_fname = NULL;
999 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1000 smb_fname->base_name));
1002 if (!is_in_media_files(smb_fname->base_name)) {
1003 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1006 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1012 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1013 client_fname->base_name));
1015 status = SMB_VFS_NEXT_STAT(handle, client_fname);
1021 * Unlike functions with const smb_filename, we have to modify
1022 * smb_fname itself to pass our info back up.
1024 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1025 smb_fname->base_name, client_fname->base_name));
1026 smb_fname->st = client_fname->st;
1029 TALLOC_FREE(client_fname);
1030 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1031 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1035 static int um_lstat(vfs_handle_struct *handle,
1036 struct smb_filename *smb_fname)
1039 struct smb_filename *client_fname = NULL;
1041 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1042 smb_fname->base_name));
1044 if (!is_in_media_files(smb_fname->base_name)) {
1045 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1048 client_fname = NULL;
1050 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1056 status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
1061 smb_fname->st = client_fname->st;
1064 TALLOC_FREE(client_fname);
1065 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1066 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1070 static int um_fstat(vfs_handle_struct *handle,
1071 files_struct *fsp, SMB_STRUCT_STAT *sbuf)
1075 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1076 "'%s'\n", fsp_str_dbg(fsp)));
1078 status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1083 if ((fsp->fsp_name == NULL) ||
1084 !is_in_media_files(fsp->fsp_name->base_name)) {
1088 status = um_stat(handle, fsp->fsp_name);
1093 *sbuf = fsp->fsp_name->st;
1096 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1097 fsp->fsp_name != NULL ?
1098 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) : "0"));
1102 static int um_unlinkat(vfs_handle_struct *handle,
1103 struct files_struct *dirfsp,
1104 const struct smb_filename *smb_fname,
1108 struct smb_filename *client_fname = NULL;
1110 DEBUG(10, ("Entering um_unlinkat\n"));
1112 if (!is_in_media_files(smb_fname->base_name)) {
1113 return SMB_VFS_NEXT_UNLINKAT(handle,
1119 ret = alloc_get_client_smb_fname(handle, talloc_tos(),
1126 ret = SMB_VFS_NEXT_UNLINKAT(handle,
1132 TALLOC_FREE(client_fname);
1136 static int um_chmod(vfs_handle_struct *handle,
1137 const struct smb_filename *smb_fname,
1141 struct smb_filename *client_fname = NULL;
1143 DEBUG(10, ("Entering um_chmod\n"));
1145 if (!is_in_media_files(smb_fname->base_name)) {
1146 return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
1149 status = alloc_get_client_smb_fname(handle,
1157 status = SMB_VFS_NEXT_CHMOD(handle, client_fname, mode);
1160 TALLOC_FREE(client_fname);
1164 static int um_lchown(vfs_handle_struct *handle,
1165 const struct smb_filename *smb_fname,
1170 struct smb_filename *client_fname = NULL;
1172 DEBUG(10, ("Entering um_lchown\n"));
1173 if (!is_in_media_files(smb_fname->base_name)) {
1174 return SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid);
1177 status = alloc_get_client_smb_fname(handle,
1185 status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid);
1188 TALLOC_FREE(client_fname);
1192 static int um_chdir(vfs_handle_struct *handle,
1193 const struct smb_filename *smb_fname)
1196 struct smb_filename *client_fname = NULL;
1198 DEBUG(10, ("Entering um_chdir\n"));
1200 if (!is_in_media_files(smb_fname->base_name)) {
1201 return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
1204 status = alloc_get_client_smb_fname(handle,
1212 status = SMB_VFS_NEXT_CHDIR(handle, client_fname);
1215 TALLOC_FREE(client_fname);
1219 static int um_ntimes(vfs_handle_struct *handle,
1220 const struct smb_filename *smb_fname,
1221 struct smb_file_time *ft)
1224 struct smb_filename *client_fname = NULL;
1226 DEBUG(10, ("Entering um_ntimes\n"));
1228 if (!is_in_media_files(smb_fname->base_name)) {
1229 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1232 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1233 smb_fname, &client_fname);
1238 status = SMB_VFS_NEXT_NTIMES(handle, client_fname, ft);
1241 TALLOC_FREE(client_fname);
1245 static int um_symlinkat(vfs_handle_struct *handle,
1246 const struct smb_filename *link_contents,
1247 struct files_struct *dirfsp,
1248 const struct smb_filename *new_smb_fname)
1251 struct smb_filename *new_link_target = NULL;
1252 struct smb_filename *new_client_fname = NULL;
1254 DEBUG(10, ("Entering um_symlinkat\n"));
1256 if (!is_in_media_files(link_contents->base_name) &&
1257 !is_in_media_files(new_smb_fname->base_name)) {
1258 return SMB_VFS_NEXT_SYMLINKAT(handle,
1264 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1265 link_contents, &new_link_target);
1269 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1270 new_smb_fname, &new_client_fname);
1275 status = SMB_VFS_NEXT_SYMLINKAT(handle,
1281 TALLOC_FREE(new_link_target);
1282 TALLOC_FREE(new_client_fname);
1286 static int um_readlinkat(vfs_handle_struct *handle,
1287 files_struct *dirfsp,
1288 const struct smb_filename *smb_fname,
1293 struct smb_filename *client_fname = NULL;
1295 DEBUG(10, ("Entering um_readlinkat\n"));
1297 if (!is_in_media_files(smb_fname->base_name)) {
1298 return SMB_VFS_NEXT_READLINKAT(handle,
1305 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1306 smb_fname, &client_fname);
1311 status = SMB_VFS_NEXT_READLINKAT(handle,
1318 TALLOC_FREE(client_fname);
1322 static int um_linkat(vfs_handle_struct *handle,
1323 files_struct *srcfsp,
1324 const struct smb_filename *old_smb_fname,
1325 files_struct *dstfsp,
1326 const struct smb_filename *new_smb_fname,
1330 struct smb_filename *old_client_fname = NULL;
1331 struct smb_filename *new_client_fname = NULL;
1333 DEBUG(10, ("Entering um_linkat\n"));
1334 if (!is_in_media_files(old_smb_fname->base_name) &&
1335 !is_in_media_files(new_smb_fname->base_name)) {
1336 return SMB_VFS_NEXT_LINKAT(handle,
1344 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1345 old_smb_fname, &old_client_fname);
1349 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1350 new_smb_fname, &new_client_fname);
1355 status = SMB_VFS_NEXT_LINKAT(handle,
1363 TALLOC_FREE(old_client_fname);
1364 TALLOC_FREE(new_client_fname);
1368 static int um_mknodat(vfs_handle_struct *handle,
1369 files_struct *dirfsp,
1370 const struct smb_filename *smb_fname,
1375 struct smb_filename *client_fname = NULL;
1377 DEBUG(10, ("Entering um_mknodat\n"));
1378 if (!is_in_media_files(smb_fname->base_name)) {
1379 return SMB_VFS_NEXT_MKNODAT(handle,
1386 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1387 smb_fname, &client_fname);
1392 status = SMB_VFS_NEXT_MKNODAT(handle,
1399 TALLOC_FREE(client_fname);
1403 static struct smb_filename *um_realpath(vfs_handle_struct *handle,
1405 const struct smb_filename *smb_fname)
1407 struct smb_filename *client_fname = NULL;
1408 struct smb_filename *result_fname = NULL;
1411 DEBUG(10, ("Entering um_realpath\n"));
1413 if (!is_in_media_files(smb_fname->base_name)) {
1414 return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname);
1417 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1418 smb_fname, &client_fname);
1423 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, client_fname);
1426 TALLOC_FREE(client_fname);
1427 return result_fname;
1430 static int um_chflags(vfs_handle_struct *handle,
1431 const struct smb_filename *smb_fname,
1435 struct smb_filename *client_fname = NULL;
1437 DEBUG(10, ("Entering um_mknod\n"));
1438 if (!is_in_media_files(smb_fname->base_name)) {
1439 return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
1442 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1443 smb_fname, &client_fname);
1448 status = SMB_VFS_NEXT_CHFLAGS(handle, client_fname, flags);
1451 TALLOC_FREE(client_fname);
1455 static NTSTATUS um_streaminfo(struct vfs_handle_struct *handle,
1456 struct files_struct *fsp,
1457 const struct smb_filename *smb_fname,
1459 unsigned int *num_streams,
1460 struct stream_struct **streams)
1464 struct smb_filename *client_fname = NULL;
1466 DEBUG(10, ("Entering um_streaminfo\n"));
1468 if (!is_in_media_files(smb_fname->base_name)) {
1469 return SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname,
1470 ctx, num_streams, streams);
1473 ret = alloc_get_client_smb_fname(handle,
1478 status = NT_STATUS_NO_MEMORY;
1483 * This only works on files, so we don't have to worry about
1484 * our fake directory stat'ing here. But what does this
1485 * function do, exactly? Does it need extra modifications for
1488 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, client_fname,
1489 ctx, num_streams, streams);
1491 TALLOC_FREE(client_fname);
1496 * Ignoring get_real_filename function because the default doesn't do
1500 static NTSTATUS um_get_nt_acl_at(vfs_handle_struct *handle,
1501 struct files_struct *dirfsp,
1502 const struct smb_filename *smb_fname,
1503 uint32_t security_info,
1504 TALLOC_CTX *mem_ctx,
1505 struct security_descriptor **ppdesc)
1508 char *client_path = NULL;
1509 struct smb_filename *client_smb_fname = NULL;
1513 DBG_DEBUG("Entering um_get_nt_acl_at\n");
1515 ok = is_in_media_files(smb_fname->base_name);
1517 return SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
1525 ret = alloc_get_client_path(handle,
1527 smb_fname->base_name,
1530 status = map_nt_error_from_unix(errno);
1534 client_smb_fname = synthetic_smb_fname(talloc_tos(),
1540 if (client_smb_fname == NULL) {
1541 TALLOC_FREE(client_path);
1542 return NT_STATUS_NO_MEMORY;
1545 status = SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
1552 TALLOC_FREE(client_smb_fname);
1553 TALLOC_FREE(client_path);
1557 static SMB_ACL_T um_sys_acl_get_file(vfs_handle_struct *handle,
1558 const struct smb_filename *smb_fname,
1559 SMB_ACL_TYPE_T type,
1560 TALLOC_CTX *mem_ctx)
1563 int saved_errno = 0;
1564 struct smb_filename *client_fname = NULL;
1567 DEBUG(10, ("Entering um_sys_acl_get_file\n"));
1569 if (!is_in_media_files(smb_fname->base_name)) {
1570 return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, smb_fname,
1574 status = alloc_get_client_smb_fname(handle,
1579 ret = (SMB_ACL_T)NULL;
1583 ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, client_fname,
1587 if (ret == (SMB_ACL_T)NULL) {
1588 saved_errno = errno;
1590 TALLOC_FREE(client_fname);
1591 if (saved_errno != 0) {
1592 errno = saved_errno;
1597 static int um_sys_acl_set_file(vfs_handle_struct *handle,
1598 const struct smb_filename *smb_fname,
1599 SMB_ACL_TYPE_T acltype,
1603 int saved_errno = 0;
1604 struct smb_filename *client_fname = NULL;
1606 DEBUG(10, ("Entering um_sys_acl_set_file\n"));
1608 if (!is_in_media_files(smb_fname->base_name)) {
1609 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, smb_fname,
1613 status = alloc_get_client_smb_fname(handle,
1621 status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, client_fname,
1626 saved_errno = errno;
1628 TALLOC_FREE(client_fname);
1629 if (saved_errno != 0) {
1630 errno = saved_errno;
1635 static int um_sys_acl_delete_def_file(vfs_handle_struct *handle,
1636 const struct smb_filename *smb_fname)
1639 int saved_errno = 0;
1640 struct smb_filename *client_fname = NULL;
1642 DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
1644 if (!is_in_media_files(smb_fname->base_name)) {
1645 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle,
1649 status = alloc_get_client_smb_fname(handle,
1657 status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, client_fname);
1661 saved_errno = errno;
1663 TALLOC_FREE(client_fname);
1664 if (saved_errno != 0) {
1665 errno = saved_errno;
1670 static ssize_t um_getxattr(struct vfs_handle_struct *handle,
1671 const struct smb_filename *smb_fname,
1677 struct smb_filename *client_fname = NULL;
1680 DEBUG(10, ("Entering um_getxattr\n"));
1681 if (!is_in_media_files(smb_fname->base_name)) {
1682 return SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
1686 status = alloc_get_client_smb_fname(handle,
1695 ret = SMB_VFS_NEXT_GETXATTR(handle, client_fname, name, value, size);
1697 TALLOC_FREE(client_fname);
1701 static ssize_t um_listxattr(struct vfs_handle_struct *handle,
1702 const struct smb_filename *smb_fname,
1707 struct smb_filename *client_fname = NULL;
1710 DEBUG(10, ("Entering um_listxattr\n"));
1712 if (!is_in_media_files(smb_fname->base_name)) {
1713 return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
1716 status = alloc_get_client_smb_fname(handle,
1725 ret = SMB_VFS_NEXT_LISTXATTR(handle, client_fname, list, size);
1728 TALLOC_FREE(client_fname);
1732 static int um_removexattr(struct vfs_handle_struct *handle,
1733 const struct smb_filename *smb_fname,
1737 struct smb_filename *client_fname = NULL;
1739 DEBUG(10, ("Entering um_removexattr\n"));
1741 if (!is_in_media_files(smb_fname->base_name)) {
1742 return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
1745 status = alloc_get_client_smb_fname(handle,
1753 status = SMB_VFS_NEXT_REMOVEXATTR(handle, client_fname, name);
1756 TALLOC_FREE(client_fname);
1760 static int um_setxattr(struct vfs_handle_struct *handle,
1761 const struct smb_filename *smb_fname,
1768 struct smb_filename *client_fname = NULL;
1770 DEBUG(10, ("Entering um_setxattr\n"));
1772 if (!is_in_media_files(smb_fname->base_name)) {
1773 return SMB_VFS_NEXT_SETXATTR(handle, smb_fname, name, value,
1777 status = alloc_get_client_smb_fname(handle,
1785 status = SMB_VFS_NEXT_SETXATTR(handle, client_fname, name, value,
1789 TALLOC_FREE(client_fname);
1793 static int um_connect(vfs_handle_struct *handle,
1794 const char *service,
1798 struct um_config_data *config;
1801 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1806 config = talloc_zero(handle->conn, struct um_config_data);
1808 DEBUG(1, ("talloc_zero() failed\n"));
1813 enumval = lp_parm_enum(SNUM(handle->conn), UM_PARAM_TYPE_NAME,
1814 "clientid", um_clientid, UM_CLIENTID_NAME);
1815 if (enumval == -1) {
1816 DEBUG(1, ("value for %s: type unknown\n",
1817 UM_PARAM_TYPE_NAME));
1820 config->clientid = (enum um_clientid)enumval;
1822 SMB_VFS_HANDLE_SET_DATA(handle, config,
1823 NULL, struct um_config_data,
1829 /* VFS operations structure */
1831 static struct vfs_fn_pointers vfs_um_fns = {
1832 .connect_fn = um_connect,
1834 /* Disk operations */
1836 .statvfs_fn = um_statvfs,
1838 /* Directory operations */
1840 .fdopendir_fn = um_fdopendir,
1841 .readdir_fn = um_readdir,
1842 .seekdir_fn = um_seekdir,
1843 .telldir_fn = um_telldir,
1844 .rewind_dir_fn = um_rewinddir,
1845 .mkdirat_fn = um_mkdirat,
1846 .closedir_fn = um_closedir,
1848 /* File operations */
1850 .openat_fn = um_openat,
1851 .create_file_fn = um_create_file,
1852 .renameat_fn = um_renameat,
1854 .lstat_fn = um_lstat,
1855 .fstat_fn = um_fstat,
1856 .unlinkat_fn = um_unlinkat,
1857 .chmod_fn = um_chmod,
1858 .lchown_fn = um_lchown,
1859 .chdir_fn = um_chdir,
1860 .ntimes_fn = um_ntimes,
1861 .symlinkat_fn = um_symlinkat,
1862 .readlinkat_fn = um_readlinkat,
1863 .linkat_fn = um_linkat,
1864 .mknodat_fn = um_mknodat,
1865 .realpath_fn = um_realpath,
1866 .chflags_fn = um_chflags,
1867 .streaminfo_fn = um_streaminfo,
1869 /* NT ACL operations. */
1871 .get_nt_acl_at_fn = um_get_nt_acl_at,
1873 /* POSIX ACL operations. */
1875 .sys_acl_get_file_fn = um_sys_acl_get_file,
1876 .sys_acl_set_file_fn = um_sys_acl_set_file,
1877 .sys_acl_delete_def_file_fn = um_sys_acl_delete_def_file,
1879 /* EA operations. */
1880 .getxattr_fn = um_getxattr,
1881 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1882 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1883 .listxattr_fn = um_listxattr,
1884 .removexattr_fn = um_removexattr,
1885 .setxattr_fn = um_setxattr,
1889 NTSTATUS vfs_unityed_media_init(TALLOC_CTX *ctx)
1891 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1892 "unityed_media", &vfs_um_fns);
1893 if (!NT_STATUS_IS_OK(ret)) {
1897 vfs_um_debug_level = debug_add_class("unityed_media");
1899 if (vfs_um_debug_level == -1) {
1900 vfs_um_debug_level = DBGC_VFS;
1901 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1902 "debugging class.\n"));