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"
65 #define UM_PARAM_TYPE_NAME "unityed_media"
67 static const char *AVID_MXF_DIRNAME = "Avid MediaFiles/MXF";
68 static const size_t AVID_MXF_DIRNAME_LEN = 19;
69 static const char *OMFI_MEDIAFILES_DIRNAME = "OMFI MediaFiles";
70 static const size_t OMFI_MEDIAFILES_DIRNAME_LEN = 15;
71 static const char *APPLE_DOUBLE_PREFIX = "._";
72 static const size_t APPLE_DOUBLE_PREFIX_LEN = 2;
73 static int vfs_um_debug_level = DBGC_VFS;
75 enum um_clientid {UM_CLIENTID_NAME, UM_CLIENTID_IP, UM_CLIENTID_HOSTNAME};
77 struct um_config_data {
78 enum um_clientid clientid;
81 static const struct enum_list um_clientid[] = {
82 {UM_CLIENTID_NAME, "user"},
83 {UM_CLIENTID_IP, "ip"},
84 {UM_CLIENTID_HOSTNAME, "hostname"},
88 /* supplements the directory list stream */
89 typedef struct um_dirinfo_struct {
94 char *clientSubDirname;
98 * Returns true and first group of digits in path, false and 0 otherwise
100 static bool get_digit_group(const char *path, uintmax_t *digit)
102 const char *p = path;
107 DEBUG(10, ("get_digit_group entering with path '%s'\n",
111 * Delibiretly initialize to 0 because callers use this result
112 * even though the string doesn't contain any number and we
118 cp = next_codepoint(p, &size);
122 if ((size == 1) && (isdigit(cp))) {
123 *digit = (uintmax_t)smb_strtoul(p,
131 DEBUG(10, ("num_suffix = '%ju'\n",
141 /* Add "_<remote_name>.<number>" suffix to path or filename.
144 * Failure: set errno, path NULL, return -1
147 static int alloc_append_client_suffix(vfs_handle_struct *handle,
152 const char *clientid;
153 struct um_config_data *config;
155 DEBUG(10, ("Entering with path '%s'\n", *path));
157 SMB_VFS_HANDLE_GET_DATA(handle, config,
158 struct um_config_data,
161 (void)get_digit_group(*path, &number);
163 switch (config->clientid) {
166 clientid = tsocket_address_inet_addr_string(
167 handle->conn->sconn->remote_address, talloc_tos());
168 if (clientid == NULL) {
175 case UM_CLIENTID_HOSTNAME:
176 clientid = get_remote_machine_name();
179 case UM_CLIENTID_NAME:
181 clientid = get_current_username();
185 *path = talloc_asprintf_append(*path, "_%s.%ju",
188 DEBUG(1, ("alloc_append_client_suffix "
194 DEBUG(10, ("Leaving with *path '%s'\n", *path));
199 /* Returns true if the file or directory begins with the appledouble
202 static bool is_apple_double(const char* fname)
206 DEBUG(10, ("Entering with fname '%s'\n", fname));
208 if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
211 DEBUG(10, ("Leaving with ret '%s'\n",
212 ret == true ? "true" : "false"));
216 static bool starts_with_media_dir(const char* media_dirname,
217 size_t media_dirname_len,
221 const char *path_start = path;
223 DEBUG(10, ("Entering with media_dirname '%s' "
224 "path '%s'\n", media_dirname, path));
226 /* Sometimes Samba gives us "./OMFI MediaFiles". */
227 if (strnequal(path, "./", 2)) {
231 if (strnequal(media_dirname, path_start, media_dirname_len)
233 ((path_start[media_dirname_len] == '\0') ||
234 (path_start[media_dirname_len] == '/'))) {
238 DEBUG(10, ("Leaving with ret '%s'\n",
239 ret == true ? "true" : "false"));
244 * Returns true if the file or directory referenced by the path is ONE
245 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
248 static bool is_in_media_dir(const char *path)
250 int transition_count = 0;
251 const char *path_start = path;
253 const char *media_dirname;
254 size_t media_dirname_len;
256 DEBUG(10, ("Entering with path'%s' ", path));
258 /* Sometimes Samba gives us "./OMFI MediaFiles". */
259 if (strnequal(path, "./", 2)) {
263 if (strnequal(path_start, AVID_MXF_DIRNAME, AVID_MXF_DIRNAME_LEN)) {
264 media_dirname = AVID_MXF_DIRNAME;
265 media_dirname_len = AVID_MXF_DIRNAME_LEN;
266 } else if (strnequal(path_start,
267 OMFI_MEDIAFILES_DIRNAME,
268 OMFI_MEDIAFILES_DIRNAME_LEN)) {
269 media_dirname = OMFI_MEDIAFILES_DIRNAME;
270 media_dirname_len = OMFI_MEDIAFILES_DIRNAME_LEN;
275 if (path_start[media_dirname_len] == '\0') {
279 p = path_start + media_dirname_len + 1;
282 if (*p == '\0' || *p == '/') {
283 if (strnequal(p - 3, "/..", 3)) {
285 } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
296 DEBUG(10, ("Going out with transition_count '%i'\n",
298 if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
300 ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
307 * Returns true if the file or directory referenced by the path is
308 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
309 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
310 * are assumed to be in the root directory, which is generally a safe
311 * assumption in the fixed-path world of Avid.
313 static bool is_in_media_files(const char *path)
317 DEBUG(10, ("Entering with path '%s'\n", path));
319 if (starts_with_media_dir(AVID_MXF_DIRNAME,
320 AVID_MXF_DIRNAME_LEN, path) ||
321 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
322 OMFI_MEDIAFILES_DIRNAME_LEN, path)) {
325 DEBUG(10, ("Leaving with ret '%s'\n",
326 ret == true ? "true" : "false"));
331 /* Add client suffix to "pure-number" path.
333 * Caller must free newPath.
336 * Failure: set errno, newPath NULL, return -1
338 static int alloc_get_client_path(vfs_handle_struct *handle,
349 *path_out = talloc_strdup(ctx, path_in);
350 if (*path_out == NULL) {
351 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
355 (void)get_digit_group(*path_out, &number);
357 digits = talloc_asprintf(NULL, "%ju", number);
358 if (digits == NULL) {
359 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
362 digits_len = strlen(digits);
364 p = strstr_m(path_in, digits);
367 ((p[digits_len] == '\0') || (p[digits_len] == '/'))
369 (((p - path_in > 0) && (p[-1] == '/'))
371 (((p - path_in) > APPLE_DOUBLE_PREFIX_LEN)
373 is_apple_double(p - APPLE_DOUBLE_PREFIX_LEN)
375 (p[-(APPLE_DOUBLE_PREFIX_LEN + 1)] == '/'))))
377 (*path_out)[p - path_in + digits_len] = '\0';
379 status = alloc_append_client_suffix(handle, path_out);
384 *path_out = talloc_strdup_append(*path_out, p + digits_len);
385 if (*path_out == NULL) {
386 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
392 /* path_out must be freed in caller. */
393 DEBUG(10, ("Result:'%s'\n", *path_out));
399 * Failure: set errno, return -1
401 static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
403 const struct smb_filename *smb_fname,
404 struct smb_filename **client_fname)
408 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
409 smb_fname->base_name));
411 *client_fname = cp_smb_filename(ctx, smb_fname);
412 if (*client_fname == NULL) {
413 DEBUG(1, ("cp_smb_filename returned NULL\n"));
416 status = alloc_get_client_path(handle, ctx,
417 smb_fname->base_name,
418 &(*client_fname)->base_name);
423 DEBUG(10, ("Leaving with (*client_fname)->base_name "
424 "'%s'\n", (*client_fname)->base_name));
432 * Failure: set errno, return -1
434 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
437 const char *suffix_number)
441 DEBUG(10, ("Entering with suffix_number '%s'\n",
444 *path = talloc_strdup(ctx, suffix_number);
446 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
449 status = alloc_append_client_suffix(handle, path);
454 DEBUG(10, ("Leaving with *path '%s'\n", *path));
459 static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
461 struct um_dirinfo_struct **di_result)
466 struct um_dirinfo_struct *dip;
468 DEBUG(10, ("Entering with fname '%s'\n", fname));
470 *di_result = talloc(NULL, struct um_dirinfo_struct);
471 if (*di_result == NULL) {
476 dip->dirpath = talloc_strdup(dip, fname);
477 if (dip->dirpath == NULL) {
481 if (!is_in_media_files(fname)) {
482 dip->isInMediaFiles = false;
483 dip->clientPath = NULL;
484 dip->clientSubDirname = NULL;
488 dip->isInMediaFiles = true;
490 (void)get_digit_group(fname, &number);
491 digits = talloc_asprintf(talloc_tos(), "%ju", number);
492 if (digits == NULL) {
496 status = alloc_set_client_dirinfo_path(handle, dip,
497 &dip->clientSubDirname,
503 status = alloc_get_client_path(handle, dip, fname,
505 if (status != 0 || dip->clientPath == NULL) {
510 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
511 "(*dirInfo)->clientPath '%s'\n",
512 dip->dirpath, dip->clientPath));
516 DEBUG(1, ("Failing with fname '%s'\n", fname));
517 TALLOC_FREE(*di_result);
523 /**********************************************************************
525 **********************************************************************/
529 * Failure: set errno, return -1
531 static int um_statvfs(struct vfs_handle_struct *handle,
532 const struct smb_filename *smb_fname,
533 struct vfs_statvfs_struct *statbuf)
536 struct smb_filename *client_fname = NULL;
538 DEBUG(10, ("Entering with path '%s'\n", smb_fname->base_name));
540 if (!is_in_media_files(smb_fname->base_name)) {
541 return SMB_VFS_NEXT_STATVFS(handle, smb_fname, statbuf);
544 status = alloc_get_client_smb_fname(handle,
552 status = SMB_VFS_NEXT_STATVFS(handle, client_fname, statbuf);
554 TALLOC_FREE(client_fname);
555 DEBUG(10, ("Leaving with path '%s'\n", smb_fname->base_name));
559 static DIR *um_fdopendir(vfs_handle_struct *handle,
564 struct um_dirinfo_struct *dirInfo = NULL;
567 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
568 fsp->fsp_name->base_name));
570 dirstream = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
575 if (alloc_set_client_dirinfo(handle,
576 fsp->fsp_name->base_name,
581 dirInfo->dirstream = dirstream;
583 if (!dirInfo->isInMediaFiles) {
585 * FIXME: this is the original code, something must be
586 * missing here, but what? -slow
592 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
593 "dirInfo->clientPath '%s', "
594 "fsp->fsp_name->st.st_ex_mtime %s",
597 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
598 return (DIR *) dirInfo;
601 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
602 fsp->fsp_name->base_name));
603 TALLOC_FREE(dirInfo);
608 * skip own suffixed directory
609 * replace own suffixed directory with non suffixed.
611 * Success: return dirent
612 * End of data: return NULL
613 * Failure: set errno, return NULL
615 static struct dirent *um_readdir(vfs_handle_struct *handle,
617 SMB_STRUCT_STAT *sbuf)
619 um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
620 struct dirent *d = NULL;
623 DEBUG(10, ("dirInfo->dirpath '%s', "
624 "dirInfo->clientPath '%s', "
625 "dirInfo->isInMediaFiles '%s', "
626 "dirInfo->clientSubDirname '%s'\n",
629 dirInfo->isInMediaFiles ? "true" : "false",
630 dirInfo->clientSubDirname));
632 if (!dirInfo->isInMediaFiles) {
633 return SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
644 d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
650 /* ignore apple double prefix for logic below */
651 if (is_apple_double(d->d_name)) {
652 dname = &d->d_name[APPLE_DOUBLE_PREFIX_LEN];
653 isAppleDouble = true;
656 isAppleDouble = false;
659 DEBUG(10, ("dname = '%s'\n", dname));
661 (void)get_digit_group(dname, &number);
662 digits = talloc_asprintf(talloc_tos(), "%ju", number);
663 if (digits == NULL) {
664 DEBUG(1, ("out of memory"));
667 digits_len = strlen(digits);
669 if (alloc_set_client_dirinfo_path(handle,
671 &((dirInfo)->clientSubDirname),
677 * If set to "true", vfs shows digits-only
678 * non-suffixed subdirectories. Normally, such
679 * subdirectories can exists only in non-media
680 * directories, so we set it to "false". Otherwise,
681 * if we have such subdirectories (probably created
682 * over not "unityed" connection), it can be little
685 if (strequal(dname, digits)) {
687 } else if (strequal(dname, dirInfo->clientSubDirname)) {
689 * Remove suffix of this client's suffixed
693 d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
695 d->d_name[digits_len] = '\0';
697 } else if (strnequal(digits, dname, digits_len)) {
699 * Set to false to see another clients subdirectories
705 DEBUG(10, ("Leaving um_readdir\n"));
708 TALLOC_FREE(dirInfo);
712 static void um_seekdir(vfs_handle_struct *handle,
716 DEBUG(10, ("Entering and leaving um_seekdir\n"));
717 SMB_VFS_NEXT_SEEKDIR(handle,
718 ((um_dirinfo_struct*)dirp)->dirstream, offset);
721 static long um_telldir(vfs_handle_struct *handle,
724 DEBUG(10, ("Entering and leaving um_telldir\n"));
725 return SMB_VFS_NEXT_TELLDIR(handle,
726 ((um_dirinfo_struct*)dirp)->dirstream);
729 static void um_rewinddir(vfs_handle_struct *handle,
732 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
733 SMB_VFS_NEXT_REWINDDIR(handle,
734 ((um_dirinfo_struct*)dirp)->dirstream);
737 static int um_mkdirat(vfs_handle_struct *handle,
738 struct files_struct *dirfsp,
739 const struct smb_filename *smb_fname,
743 const char *path = smb_fname->base_name;
744 struct smb_filename *client_fname = NULL;
746 DEBUG(10, ("Entering with path '%s'\n", path));
748 if (!is_in_media_files(path) || !is_in_media_dir(path)) {
749 return SMB_VFS_NEXT_MKDIRAT(handle,
755 status = alloc_get_client_smb_fname(handle,
763 status = SMB_VFS_NEXT_MKDIRAT(handle,
768 TALLOC_FREE(client_fname);
769 DEBUG(10, ("Leaving with path '%s'\n", path));
773 static int um_closedir(vfs_handle_struct *handle,
776 DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
780 return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
783 static int um_open(vfs_handle_struct *handle,
784 struct smb_filename *smb_fname,
790 struct smb_filename *client_fname = NULL;
792 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
793 smb_fname->base_name));
795 if (!is_in_media_files(smb_fname->base_name)) {
796 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
799 if (alloc_get_client_smb_fname(handle, talloc_tos(),
808 * What about fsp->fsp_name? We also have to get correct stat
809 * info into fsp and smb_fname for DB files, don't we?
812 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
813 "smb_fname->st.st_ex_mtime %s"
814 "fsp->fsp_name->st.st_ex_mtime %s",
815 smb_fname->base_name,
816 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
817 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
819 ret = SMB_VFS_NEXT_OPEN(handle, client_fname, fsp, flags, mode);
821 TALLOC_FREE(client_fname);
822 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
823 smb_fname->base_name));
827 static int um_openat(struct vfs_handle_struct *handle,
828 const struct files_struct *dirfsp,
829 const struct smb_filename *smb_fname,
830 struct files_struct *fsp,
834 struct smb_filename *client_fname = NULL;
837 DBG_DEBUG("Entering with smb_fname->base_name '%s'\n",
838 smb_fname->base_name);
840 if (!is_in_media_files(smb_fname->base_name)) {
841 return SMB_VFS_NEXT_OPENAT(handle,
849 if (alloc_get_client_smb_fname(handle, talloc_tos(),
858 * What about fsp->fsp_name? We also have to get correct stat
859 * info into fsp and smb_fname for DB files, don't we?
862 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
863 "smb_fname->st.st_ex_mtime %s"
864 "fsp->fsp_name->st.st_ex_mtime %s",
865 smb_fname->base_name,
866 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
867 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
869 ret = SMB_VFS_NEXT_OPENAT(handle,
876 TALLOC_FREE(client_fname);
877 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
878 smb_fname->base_name));
882 static NTSTATUS um_create_file(vfs_handle_struct *handle,
883 struct smb_request *req,
884 struct files_struct **dirfsp,
885 struct smb_filename *smb_fname,
886 uint32_t access_mask,
887 uint32_t share_access,
888 uint32_t create_disposition,
889 uint32_t create_options,
890 uint32_t file_attributes,
891 uint32_t oplock_request,
892 const struct smb2_lease *lease,
893 uint64_t allocation_size,
894 uint32_t private_flags,
895 struct security_descriptor *sd,
896 struct ea_list *ea_list,
897 files_struct **result_fsp,
899 const struct smb2_create_blobs *in_context_blobs,
900 struct smb2_create_blobs *out_context_blobs)
903 struct smb_filename *client_fname = NULL;
905 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
906 smb_fname->base_name));
908 if (!is_in_media_files(smb_fname->base_name)) {
909 return SMB_VFS_NEXT_CREATE_FILE(
931 if (alloc_get_client_smb_fname(handle, talloc_tos(),
934 status = map_nt_error_from_unix(errno);
940 * This only creates files, so we don't have to worry about
941 * our fake directory stat'ing here. But we still need to
942 * route stat calls for DB files properly, right?
944 status = SMB_VFS_NEXT_CREATE_FILE(
965 TALLOC_FREE(client_fname);
966 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
967 "smb_fname->st.st_ex_mtime %s"
968 " fsp->fsp_name->st.st_ex_mtime %s",
969 smb_fname->base_name,
970 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
971 (*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
972 ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
977 static int um_renameat(vfs_handle_struct *handle,
978 files_struct *srcfsp,
979 const struct smb_filename *smb_fname_src,
980 files_struct *dstfsp,
981 const struct smb_filename *smb_fname_dst)
984 struct smb_filename *src_client_fname = NULL;
985 struct smb_filename *dst_client_fname = NULL;
987 DEBUG(10, ("Entering with "
988 "smb_fname_src->base_name '%s', "
989 "smb_fname_dst->base_name '%s'\n",
990 smb_fname_src->base_name,
991 smb_fname_dst->base_name));
993 if (!is_in_media_files(smb_fname_src->base_name)
995 !is_in_media_files(smb_fname_dst->base_name)) {
996 return SMB_VFS_NEXT_RENAMEAT(handle,
1003 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1010 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1018 status = SMB_VFS_NEXT_RENAMEAT(handle,
1025 TALLOC_FREE(dst_client_fname);
1026 TALLOC_FREE(src_client_fname);
1027 DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
1028 " smb_fname_dst->base_name '%s'\n",
1029 smb_fname_src->base_name,
1030 smb_fname_dst->base_name));
1037 * Failure: set errno, return -1
1039 static int um_stat(vfs_handle_struct *handle,
1040 struct smb_filename *smb_fname)
1043 struct smb_filename *client_fname = NULL;
1045 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1046 smb_fname->base_name));
1048 if (!is_in_media_files(smb_fname->base_name)) {
1049 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1052 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1058 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1059 client_fname->base_name));
1061 status = SMB_VFS_NEXT_STAT(handle, client_fname);
1067 * Unlike functions with const smb_filename, we have to modify
1068 * smb_fname itself to pass our info back up.
1070 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1071 smb_fname->base_name, client_fname->base_name));
1072 smb_fname->st = client_fname->st;
1075 TALLOC_FREE(client_fname);
1076 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1077 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1081 static int um_lstat(vfs_handle_struct *handle,
1082 struct smb_filename *smb_fname)
1085 struct smb_filename *client_fname = NULL;
1087 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1088 smb_fname->base_name));
1090 if (!is_in_media_files(smb_fname->base_name)) {
1091 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1094 client_fname = NULL;
1096 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1102 status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
1107 smb_fname->st = client_fname->st;
1110 TALLOC_FREE(client_fname);
1111 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1112 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1116 static int um_fstat(vfs_handle_struct *handle,
1117 files_struct *fsp, SMB_STRUCT_STAT *sbuf)
1121 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1122 "'%s'\n", fsp_str_dbg(fsp)));
1124 status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1129 if ((fsp->fsp_name == NULL) ||
1130 !is_in_media_files(fsp->fsp_name->base_name)) {
1134 status = um_stat(handle, fsp->fsp_name);
1139 *sbuf = fsp->fsp_name->st;
1142 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1143 fsp->fsp_name != NULL ?
1144 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) : "0"));
1148 static int um_unlinkat(vfs_handle_struct *handle,
1149 struct files_struct *dirfsp,
1150 const struct smb_filename *smb_fname,
1154 struct smb_filename *client_fname = NULL;
1156 DEBUG(10, ("Entering um_unlinkat\n"));
1158 if (!is_in_media_files(smb_fname->base_name)) {
1159 return SMB_VFS_NEXT_UNLINKAT(handle,
1165 ret = alloc_get_client_smb_fname(handle, talloc_tos(),
1172 ret = SMB_VFS_NEXT_UNLINKAT(handle,
1178 TALLOC_FREE(client_fname);
1182 static int um_chmod(vfs_handle_struct *handle,
1183 const struct smb_filename *smb_fname,
1187 struct smb_filename *client_fname = NULL;
1189 DEBUG(10, ("Entering um_chmod\n"));
1191 if (!is_in_media_files(smb_fname->base_name)) {
1192 return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
1195 status = alloc_get_client_smb_fname(handle,
1203 status = SMB_VFS_NEXT_CHMOD(handle, client_fname, mode);
1206 TALLOC_FREE(client_fname);
1210 static int um_lchown(vfs_handle_struct *handle,
1211 const struct smb_filename *smb_fname,
1216 struct smb_filename *client_fname = NULL;
1218 DEBUG(10, ("Entering um_lchown\n"));
1219 if (!is_in_media_files(smb_fname->base_name)) {
1220 return SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid);
1223 status = alloc_get_client_smb_fname(handle,
1231 status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid);
1234 TALLOC_FREE(client_fname);
1238 static int um_chdir(vfs_handle_struct *handle,
1239 const struct smb_filename *smb_fname)
1242 struct smb_filename *client_fname = NULL;
1244 DEBUG(10, ("Entering um_chdir\n"));
1246 if (!is_in_media_files(smb_fname->base_name)) {
1247 return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
1250 status = alloc_get_client_smb_fname(handle,
1258 status = SMB_VFS_NEXT_CHDIR(handle, client_fname);
1261 TALLOC_FREE(client_fname);
1265 static int um_ntimes(vfs_handle_struct *handle,
1266 const struct smb_filename *smb_fname,
1267 struct smb_file_time *ft)
1270 struct smb_filename *client_fname = NULL;
1272 DEBUG(10, ("Entering um_ntimes\n"));
1274 if (!is_in_media_files(smb_fname->base_name)) {
1275 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1278 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1279 smb_fname, &client_fname);
1284 status = SMB_VFS_NEXT_NTIMES(handle, client_fname, ft);
1287 TALLOC_FREE(client_fname);
1291 static int um_symlinkat(vfs_handle_struct *handle,
1292 const struct smb_filename *link_contents,
1293 struct files_struct *dirfsp,
1294 const struct smb_filename *new_smb_fname)
1297 struct smb_filename *new_link_target = NULL;
1298 struct smb_filename *new_client_fname = NULL;
1300 DEBUG(10, ("Entering um_symlinkat\n"));
1302 if (!is_in_media_files(link_contents->base_name) &&
1303 !is_in_media_files(new_smb_fname->base_name)) {
1304 return SMB_VFS_NEXT_SYMLINKAT(handle,
1310 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1311 link_contents, &new_link_target);
1315 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1316 new_smb_fname, &new_client_fname);
1321 status = SMB_VFS_NEXT_SYMLINKAT(handle,
1327 TALLOC_FREE(new_link_target);
1328 TALLOC_FREE(new_client_fname);
1332 static int um_readlinkat(vfs_handle_struct *handle,
1333 files_struct *dirfsp,
1334 const struct smb_filename *smb_fname,
1339 struct smb_filename *client_fname = NULL;
1341 DEBUG(10, ("Entering um_readlinkat\n"));
1343 if (!is_in_media_files(smb_fname->base_name)) {
1344 return SMB_VFS_NEXT_READLINKAT(handle,
1351 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1352 smb_fname, &client_fname);
1357 status = SMB_VFS_NEXT_READLINKAT(handle,
1364 TALLOC_FREE(client_fname);
1368 static int um_linkat(vfs_handle_struct *handle,
1369 files_struct *srcfsp,
1370 const struct smb_filename *old_smb_fname,
1371 files_struct *dstfsp,
1372 const struct smb_filename *new_smb_fname,
1376 struct smb_filename *old_client_fname = NULL;
1377 struct smb_filename *new_client_fname = NULL;
1379 DEBUG(10, ("Entering um_linkat\n"));
1380 if (!is_in_media_files(old_smb_fname->base_name) &&
1381 !is_in_media_files(new_smb_fname->base_name)) {
1382 return SMB_VFS_NEXT_LINKAT(handle,
1390 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1391 old_smb_fname, &old_client_fname);
1395 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1396 new_smb_fname, &new_client_fname);
1401 status = SMB_VFS_NEXT_LINKAT(handle,
1409 TALLOC_FREE(old_client_fname);
1410 TALLOC_FREE(new_client_fname);
1414 static int um_mknodat(vfs_handle_struct *handle,
1415 files_struct *dirfsp,
1416 const struct smb_filename *smb_fname,
1421 struct smb_filename *client_fname = NULL;
1423 DEBUG(10, ("Entering um_mknodat\n"));
1424 if (!is_in_media_files(smb_fname->base_name)) {
1425 return SMB_VFS_NEXT_MKNODAT(handle,
1432 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1433 smb_fname, &client_fname);
1438 status = SMB_VFS_NEXT_MKNODAT(handle,
1445 TALLOC_FREE(client_fname);
1449 static struct smb_filename *um_realpath(vfs_handle_struct *handle,
1451 const struct smb_filename *smb_fname)
1453 struct smb_filename *client_fname = NULL;
1454 struct smb_filename *result_fname = NULL;
1457 DEBUG(10, ("Entering um_realpath\n"));
1459 if (!is_in_media_files(smb_fname->base_name)) {
1460 return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname);
1463 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1464 smb_fname, &client_fname);
1469 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, client_fname);
1472 TALLOC_FREE(client_fname);
1473 return result_fname;
1476 static int um_chflags(vfs_handle_struct *handle,
1477 const struct smb_filename *smb_fname,
1481 struct smb_filename *client_fname = NULL;
1483 DEBUG(10, ("Entering um_mknod\n"));
1484 if (!is_in_media_files(smb_fname->base_name)) {
1485 return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
1488 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1489 smb_fname, &client_fname);
1494 status = SMB_VFS_NEXT_CHFLAGS(handle, client_fname, flags);
1497 TALLOC_FREE(client_fname);
1501 static NTSTATUS um_streaminfo(struct vfs_handle_struct *handle,
1502 struct files_struct *fsp,
1503 const struct smb_filename *smb_fname,
1505 unsigned int *num_streams,
1506 struct stream_struct **streams)
1510 struct smb_filename *client_fname = NULL;
1512 DEBUG(10, ("Entering um_streaminfo\n"));
1514 if (!is_in_media_files(smb_fname->base_name)) {
1515 return SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname,
1516 ctx, num_streams, streams);
1519 ret = alloc_get_client_smb_fname(handle,
1524 status = NT_STATUS_NO_MEMORY;
1529 * This only works on files, so we don't have to worry about
1530 * our fake directory stat'ing here. But what does this
1531 * function do, exactly? Does it need extra modifications for
1534 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, client_fname,
1535 ctx, num_streams, streams);
1537 TALLOC_FREE(client_fname);
1542 * Ignoring get_real_filename function because the default doesn't do
1546 static NTSTATUS um_get_nt_acl_at(vfs_handle_struct *handle,
1547 struct files_struct *dirfsp,
1548 const struct smb_filename *smb_fname,
1549 uint32_t security_info,
1550 TALLOC_CTX *mem_ctx,
1551 struct security_descriptor **ppdesc)
1554 char *client_path = NULL;
1555 struct smb_filename *client_smb_fname = NULL;
1559 DBG_DEBUG("Entering um_get_nt_acl_at\n");
1561 ok = is_in_media_files(smb_fname->base_name);
1563 return SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
1571 ret = alloc_get_client_path(handle,
1573 smb_fname->base_name,
1576 status = map_nt_error_from_unix(errno);
1580 client_smb_fname = synthetic_smb_fname(talloc_tos(),
1586 if (client_smb_fname == NULL) {
1587 TALLOC_FREE(client_path);
1588 return NT_STATUS_NO_MEMORY;
1591 status = SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
1598 TALLOC_FREE(client_smb_fname);
1599 TALLOC_FREE(client_path);
1603 static SMB_ACL_T um_sys_acl_get_file(vfs_handle_struct *handle,
1604 const struct smb_filename *smb_fname,
1605 SMB_ACL_TYPE_T type,
1606 TALLOC_CTX *mem_ctx)
1609 int saved_errno = 0;
1610 struct smb_filename *client_fname = NULL;
1613 DEBUG(10, ("Entering um_sys_acl_get_file\n"));
1615 if (!is_in_media_files(smb_fname->base_name)) {
1616 return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, smb_fname,
1620 status = alloc_get_client_smb_fname(handle,
1625 ret = (SMB_ACL_T)NULL;
1629 ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, client_fname,
1633 if (ret == (SMB_ACL_T)NULL) {
1634 saved_errno = errno;
1636 TALLOC_FREE(client_fname);
1637 if (saved_errno != 0) {
1638 errno = saved_errno;
1643 static int um_sys_acl_set_file(vfs_handle_struct *handle,
1644 const struct smb_filename *smb_fname,
1645 SMB_ACL_TYPE_T acltype,
1649 int saved_errno = 0;
1650 struct smb_filename *client_fname = NULL;
1652 DEBUG(10, ("Entering um_sys_acl_set_file\n"));
1654 if (!is_in_media_files(smb_fname->base_name)) {
1655 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, smb_fname,
1659 status = alloc_get_client_smb_fname(handle,
1667 status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, client_fname,
1672 saved_errno = errno;
1674 TALLOC_FREE(client_fname);
1675 if (saved_errno != 0) {
1676 errno = saved_errno;
1681 static int um_sys_acl_delete_def_file(vfs_handle_struct *handle,
1682 const struct smb_filename *smb_fname)
1685 int saved_errno = 0;
1686 struct smb_filename *client_fname = NULL;
1688 DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
1690 if (!is_in_media_files(smb_fname->base_name)) {
1691 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle,
1695 status = alloc_get_client_smb_fname(handle,
1703 status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, client_fname);
1707 saved_errno = errno;
1709 TALLOC_FREE(client_fname);
1710 if (saved_errno != 0) {
1711 errno = saved_errno;
1716 static ssize_t um_getxattr(struct vfs_handle_struct *handle,
1717 const struct smb_filename *smb_fname,
1723 struct smb_filename *client_fname = NULL;
1726 DEBUG(10, ("Entering um_getxattr\n"));
1727 if (!is_in_media_files(smb_fname->base_name)) {
1728 return SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
1732 status = alloc_get_client_smb_fname(handle,
1741 ret = SMB_VFS_NEXT_GETXATTR(handle, client_fname, name, value, size);
1743 TALLOC_FREE(client_fname);
1747 static ssize_t um_listxattr(struct vfs_handle_struct *handle,
1748 const struct smb_filename *smb_fname,
1753 struct smb_filename *client_fname = NULL;
1756 DEBUG(10, ("Entering um_listxattr\n"));
1758 if (!is_in_media_files(smb_fname->base_name)) {
1759 return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
1762 status = alloc_get_client_smb_fname(handle,
1771 ret = SMB_VFS_NEXT_LISTXATTR(handle, client_fname, list, size);
1774 TALLOC_FREE(client_fname);
1778 static int um_removexattr(struct vfs_handle_struct *handle,
1779 const struct smb_filename *smb_fname,
1783 struct smb_filename *client_fname = NULL;
1785 DEBUG(10, ("Entering um_removexattr\n"));
1787 if (!is_in_media_files(smb_fname->base_name)) {
1788 return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
1791 status = alloc_get_client_smb_fname(handle,
1799 status = SMB_VFS_NEXT_REMOVEXATTR(handle, client_fname, name);
1802 TALLOC_FREE(client_fname);
1806 static int um_setxattr(struct vfs_handle_struct *handle,
1807 const struct smb_filename *smb_fname,
1814 struct smb_filename *client_fname = NULL;
1816 DEBUG(10, ("Entering um_setxattr\n"));
1818 if (!is_in_media_files(smb_fname->base_name)) {
1819 return SMB_VFS_NEXT_SETXATTR(handle, smb_fname, name, value,
1823 status = alloc_get_client_smb_fname(handle,
1831 status = SMB_VFS_NEXT_SETXATTR(handle, client_fname, name, value,
1835 TALLOC_FREE(client_fname);
1839 static int um_connect(vfs_handle_struct *handle,
1840 const char *service,
1844 struct um_config_data *config;
1847 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1852 config = talloc_zero(handle->conn, struct um_config_data);
1854 DEBUG(1, ("talloc_zero() failed\n"));
1859 enumval = lp_parm_enum(SNUM(handle->conn), UM_PARAM_TYPE_NAME,
1860 "clientid", um_clientid, UM_CLIENTID_NAME);
1861 if (enumval == -1) {
1862 DEBUG(1, ("value for %s: type unknown\n",
1863 UM_PARAM_TYPE_NAME));
1866 config->clientid = (enum um_clientid)enumval;
1868 SMB_VFS_HANDLE_SET_DATA(handle, config,
1869 NULL, struct um_config_data,
1875 /* VFS operations structure */
1877 static struct vfs_fn_pointers vfs_um_fns = {
1878 .connect_fn = um_connect,
1880 /* Disk operations */
1882 .statvfs_fn = um_statvfs,
1884 /* Directory operations */
1886 .fdopendir_fn = um_fdopendir,
1887 .readdir_fn = um_readdir,
1888 .seekdir_fn = um_seekdir,
1889 .telldir_fn = um_telldir,
1890 .rewind_dir_fn = um_rewinddir,
1891 .mkdirat_fn = um_mkdirat,
1892 .closedir_fn = um_closedir,
1894 /* File operations */
1897 .openat_fn = um_openat,
1898 .create_file_fn = um_create_file,
1899 .renameat_fn = um_renameat,
1901 .lstat_fn = um_lstat,
1902 .fstat_fn = um_fstat,
1903 .unlinkat_fn = um_unlinkat,
1904 .chmod_fn = um_chmod,
1905 .lchown_fn = um_lchown,
1906 .chdir_fn = um_chdir,
1907 .ntimes_fn = um_ntimes,
1908 .symlinkat_fn = um_symlinkat,
1909 .readlinkat_fn = um_readlinkat,
1910 .linkat_fn = um_linkat,
1911 .mknodat_fn = um_mknodat,
1912 .realpath_fn = um_realpath,
1913 .chflags_fn = um_chflags,
1914 .streaminfo_fn = um_streaminfo,
1916 /* NT ACL operations. */
1918 .get_nt_acl_at_fn = um_get_nt_acl_at,
1920 /* POSIX ACL operations. */
1922 .sys_acl_get_file_fn = um_sys_acl_get_file,
1923 .sys_acl_set_file_fn = um_sys_acl_set_file,
1924 .sys_acl_delete_def_file_fn = um_sys_acl_delete_def_file,
1926 /* EA operations. */
1927 .getxattr_fn = um_getxattr,
1928 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1929 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1930 .listxattr_fn = um_listxattr,
1931 .removexattr_fn = um_removexattr,
1932 .setxattr_fn = um_setxattr,
1936 NTSTATUS vfs_unityed_media_init(TALLOC_CTX *ctx)
1938 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1939 "unityed_media", &vfs_um_fns);
1940 if (!NT_STATUS_IS_OK(ret)) {
1944 vfs_um_debug_level = debug_add_class("unityed_media");
1946 if (vfs_um_debug_level == -1) {
1947 vfs_um_debug_level = DBGC_VFS;
1948 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1949 "debugging class.\n"));