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;
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)strtoul_err(p, &endp, 10, &error);
128 DEBUG(10, ("num_suffix = '%ju'\n",
138 /* Add "_<remote_name>.<number>" suffix to path or filename.
141 * Failure: set errno, path NULL, return -1
144 static int alloc_append_client_suffix(vfs_handle_struct *handle,
149 const char *clientid;
150 struct um_config_data *config;
152 DEBUG(10, ("Entering with path '%s'\n", *path));
154 SMB_VFS_HANDLE_GET_DATA(handle, config,
155 struct um_config_data,
158 (void)get_digit_group(*path, &number);
160 switch (config->clientid) {
163 clientid = tsocket_address_inet_addr_string(
164 handle->conn->sconn->remote_address, talloc_tos());
165 if (clientid == NULL) {
172 case UM_CLIENTID_HOSTNAME:
173 clientid = get_remote_machine_name();
176 case UM_CLIENTID_NAME:
178 clientid = get_current_username();
182 *path = talloc_asprintf_append(*path, "_%s.%ju",
185 DEBUG(1, ("alloc_append_client_suffix "
191 DEBUG(10, ("Leaving with *path '%s'\n", *path));
196 /* Returns true if the file or directory begins with the appledouble
199 static bool is_apple_double(const char* fname)
203 DEBUG(10, ("Entering with fname '%s'\n", fname));
205 if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
208 DEBUG(10, ("Leaving with ret '%s'\n",
209 ret == true ? "true" : "false"));
213 static bool starts_with_media_dir(const char* media_dirname,
214 size_t media_dirname_len,
218 const char *path_start = path;
220 DEBUG(10, ("Entering with media_dirname '%s' "
221 "path '%s'\n", media_dirname, path));
223 /* Sometimes Samba gives us "./OMFI MediaFiles". */
224 if (strnequal(path, "./", 2)) {
228 if (strnequal(media_dirname, path_start, media_dirname_len)
230 ((path_start[media_dirname_len] == '\0') ||
231 (path_start[media_dirname_len] == '/'))) {
235 DEBUG(10, ("Leaving with ret '%s'\n",
236 ret == true ? "true" : "false"));
241 * Returns true if the file or directory referenced by the path is ONE
242 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
245 static bool is_in_media_dir(const char *path)
247 int transition_count = 0;
248 const char *path_start = path;
250 const char *media_dirname;
251 size_t media_dirname_len;
253 DEBUG(10, ("Entering with path'%s' ", path));
255 /* Sometimes Samba gives us "./OMFI MediaFiles". */
256 if (strnequal(path, "./", 2)) {
260 if (strnequal(path_start, AVID_MXF_DIRNAME, AVID_MXF_DIRNAME_LEN)) {
261 media_dirname = AVID_MXF_DIRNAME;
262 media_dirname_len = AVID_MXF_DIRNAME_LEN;
263 } else if (strnequal(path_start,
264 OMFI_MEDIAFILES_DIRNAME,
265 OMFI_MEDIAFILES_DIRNAME_LEN)) {
266 media_dirname = OMFI_MEDIAFILES_DIRNAME;
267 media_dirname_len = OMFI_MEDIAFILES_DIRNAME_LEN;
272 if (path_start[media_dirname_len] == '\0') {
276 p = path_start + media_dirname_len + 1;
279 if (*p == '\0' || *p == '/') {
280 if (strnequal(p - 3, "/..", 3)) {
282 } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
293 DEBUG(10, ("Going out with transition_count '%i'\n",
295 if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
297 ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
304 * Returns true if the file or directory referenced by the path is
305 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
306 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
307 * are assumed to be in the root directory, which is generally a safe
308 * assumption in the fixed-path world of Avid.
310 static bool is_in_media_files(const char *path)
314 DEBUG(10, ("Entering with path '%s'\n", path));
316 if (starts_with_media_dir(AVID_MXF_DIRNAME,
317 AVID_MXF_DIRNAME_LEN, path) ||
318 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
319 OMFI_MEDIAFILES_DIRNAME_LEN, path)) {
322 DEBUG(10, ("Leaving with ret '%s'\n",
323 ret == true ? "true" : "false"));
328 /* Add client suffix to "pure-number" path.
330 * Caller must free newPath.
333 * Failure: set errno, newPath NULL, return -1
335 static int alloc_get_client_path(vfs_handle_struct *handle,
346 *path_out = talloc_strdup(ctx, path_in);
347 if (*path_out == NULL) {
348 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
352 (void)get_digit_group(*path_out, &number);
354 digits = talloc_asprintf(NULL, "%ju", number);
355 if (digits == NULL) {
356 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
359 digits_len = strlen(digits);
361 p = strstr_m(path_in, digits);
364 ((p[digits_len] == '\0') || (p[digits_len] == '/'))
366 (((p - path_in > 0) && (p[-1] == '/'))
368 (((p - path_in) > APPLE_DOUBLE_PREFIX_LEN)
370 is_apple_double(p - APPLE_DOUBLE_PREFIX_LEN)
372 (p[-(APPLE_DOUBLE_PREFIX_LEN + 1)] == '/'))))
374 (*path_out)[p - path_in + digits_len] = '\0';
376 status = alloc_append_client_suffix(handle, path_out);
381 *path_out = talloc_strdup_append(*path_out, p + digits_len);
382 if (*path_out == NULL) {
383 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
389 /* path_out must be freed in caller. */
390 DEBUG(10, ("Result:'%s'\n", *path_out));
396 * Failure: set errno, return -1
398 static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
400 const struct smb_filename *smb_fname,
401 struct smb_filename **client_fname)
405 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
406 smb_fname->base_name));
408 *client_fname = cp_smb_filename(ctx, smb_fname);
409 if (*client_fname == NULL) {
410 DEBUG(1, ("cp_smb_filename returned NULL\n"));
413 status = alloc_get_client_path(handle, ctx,
414 smb_fname->base_name,
415 &(*client_fname)->base_name);
420 DEBUG(10, ("Leaving with (*client_fname)->base_name "
421 "'%s'\n", (*client_fname)->base_name));
429 * Failure: set errno, return -1
431 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
434 const char *suffix_number)
438 DEBUG(10, ("Entering with suffix_number '%s'\n",
441 *path = talloc_strdup(ctx, suffix_number);
443 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
446 status = alloc_append_client_suffix(handle, path);
451 DEBUG(10, ("Leaving with *path '%s'\n", *path));
456 static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
458 struct um_dirinfo_struct **di_result)
463 struct um_dirinfo_struct *dip;
465 DEBUG(10, ("Entering with fname '%s'\n", fname));
467 *di_result = talloc(NULL, struct um_dirinfo_struct);
468 if (*di_result == NULL) {
473 dip->dirpath = talloc_strdup(dip, fname);
474 if (dip->dirpath == NULL) {
478 if (!is_in_media_files(fname)) {
479 dip->isInMediaFiles = false;
480 dip->clientPath = NULL;
481 dip->clientSubDirname = NULL;
485 dip->isInMediaFiles = true;
487 (void)get_digit_group(fname, &number);
488 digits = talloc_asprintf(talloc_tos(), "%ju", number);
489 if (digits == NULL) {
493 status = alloc_set_client_dirinfo_path(handle, dip,
494 &dip->clientSubDirname,
500 status = alloc_get_client_path(handle, dip, fname,
502 if (status != 0 || dip->clientPath == NULL) {
507 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
508 "(*dirInfo)->clientPath '%s'\n",
509 dip->dirpath, dip->clientPath));
513 DEBUG(1, ("Failing with fname '%s'\n", fname));
514 TALLOC_FREE(*di_result);
520 /**********************************************************************
522 **********************************************************************/
526 * Failure: set errno, return -1
528 static int um_statvfs(struct vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 struct vfs_statvfs_struct *statbuf)
533 struct smb_filename *client_fname = NULL;
535 DEBUG(10, ("Entering with path '%s'\n", smb_fname->base_name));
537 if (!is_in_media_files(smb_fname->base_name)) {
538 return SMB_VFS_NEXT_STATVFS(handle, smb_fname, statbuf);
541 status = alloc_get_client_smb_fname(handle,
549 status = SMB_VFS_NEXT_STATVFS(handle, client_fname, statbuf);
551 TALLOC_FREE(client_fname);
552 DEBUG(10, ("Leaving with path '%s'\n", smb_fname->base_name));
556 /* Success: return a um_dirinfo_struct cast as a DIR
557 * Failure: set errno, return NULL
559 static DIR *um_opendir(vfs_handle_struct *handle,
560 const struct smb_filename *smb_fname,
564 struct um_dirinfo_struct *dirInfo;
566 DEBUG(10, ("Entering with fname '%s'\n", smb_fname->base_name));
568 if (alloc_set_client_dirinfo(handle, smb_fname->base_name, &dirInfo)) {
572 if (!dirInfo->isInMediaFiles) {
573 dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
574 handle, smb_fname, mask, attr);
576 struct smb_filename *client_smb_fname =
577 synthetic_smb_fname(talloc_tos(),
582 if (client_smb_fname == NULL) {
586 dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
587 handle, client_smb_fname, mask, attr);
589 TALLOC_FREE(client_smb_fname);
592 if (dirInfo->dirstream == NULL) {
596 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
597 "dirInfo->clientPath '%s'\n",
599 dirInfo->clientPath));
600 return (DIR*)dirInfo;
603 DEBUG(1, ("Failing with fname '%s'\n", smb_fname->base_name));
604 TALLOC_FREE(dirInfo);
608 static DIR *um_fdopendir(vfs_handle_struct *handle,
613 struct um_dirinfo_struct *dirInfo = NULL;
616 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
617 fsp->fsp_name->base_name));
619 dirstream = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
624 if (alloc_set_client_dirinfo(handle,
625 fsp->fsp_name->base_name,
630 dirInfo->dirstream = dirstream;
632 if (!dirInfo->isInMediaFiles) {
634 * FIXME: this is the original code, something must be
635 * missing here, but what? -slow
641 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
642 "dirInfo->clientPath '%s', "
643 "fsp->fsp_name->st.st_ex_mtime %s",
646 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
647 return (DIR *) dirInfo;
650 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
651 fsp->fsp_name->base_name));
652 TALLOC_FREE(dirInfo);
657 * skip own suffixed directory
658 * replace own suffixed directory with non suffixed.
660 * Success: return dirent
661 * End of data: return NULL
662 * Failure: set errno, return NULL
664 static struct dirent *um_readdir(vfs_handle_struct *handle,
666 SMB_STRUCT_STAT *sbuf)
668 um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
669 struct dirent *d = NULL;
672 DEBUG(10, ("dirInfo->dirpath '%s', "
673 "dirInfo->clientPath '%s', "
674 "dirInfo->isInMediaFiles '%s', "
675 "dirInfo->clientSubDirname '%s'\n",
678 dirInfo->isInMediaFiles ? "true" : "false",
679 dirInfo->clientSubDirname));
681 if (!dirInfo->isInMediaFiles) {
682 return SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
693 d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
699 /* ignore apple double prefix for logic below */
700 if (is_apple_double(d->d_name)) {
701 dname = &d->d_name[APPLE_DOUBLE_PREFIX_LEN];
702 isAppleDouble = true;
705 isAppleDouble = false;
708 DEBUG(10, ("dname = '%s'\n", dname));
710 (void)get_digit_group(dname, &number);
711 digits = talloc_asprintf(talloc_tos(), "%ju", number);
712 if (digits == NULL) {
713 DEBUG(1, ("out of memory"));
716 digits_len = strlen(digits);
718 if (alloc_set_client_dirinfo_path(handle,
720 &((dirInfo)->clientSubDirname),
726 * If set to "true", vfs shows digits-only
727 * non-suffixed subdirectories. Normally, such
728 * subdirectories can exists only in non-media
729 * directories, so we set it to "false". Otherwise,
730 * if we have such subdirectories (probably created
731 * over not "unityed" connection), it can be little
734 if (strequal(dname, digits)) {
736 } else if (strequal(dname, dirInfo->clientSubDirname)) {
738 * Remove suffix of this client's suffixed
742 d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
744 d->d_name[digits_len] = '\0';
746 } else if (strnequal(digits, dname, digits_len)) {
748 * Set to false to see another clients subdirectories
754 DEBUG(10, ("Leaving um_readdir\n"));
757 TALLOC_FREE(dirInfo);
761 static void um_seekdir(vfs_handle_struct *handle,
765 DEBUG(10, ("Entering and leaving um_seekdir\n"));
766 SMB_VFS_NEXT_SEEKDIR(handle,
767 ((um_dirinfo_struct*)dirp)->dirstream, offset);
770 static long um_telldir(vfs_handle_struct *handle,
773 DEBUG(10, ("Entering and leaving um_telldir\n"));
774 return SMB_VFS_NEXT_TELLDIR(handle,
775 ((um_dirinfo_struct*)dirp)->dirstream);
778 static void um_rewinddir(vfs_handle_struct *handle,
781 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
782 SMB_VFS_NEXT_REWINDDIR(handle,
783 ((um_dirinfo_struct*)dirp)->dirstream);
786 static int um_mkdir(vfs_handle_struct *handle,
787 const struct smb_filename *smb_fname,
791 const char *path = smb_fname->base_name;
792 struct smb_filename *client_fname = NULL;
794 DEBUG(10, ("Entering with path '%s'\n", path));
796 if (!is_in_media_files(path) || !is_in_media_dir(path)) {
797 return SMB_VFS_NEXT_MKDIR(handle, smb_fname, mode);
800 status = alloc_get_client_smb_fname(handle,
808 status = SMB_VFS_NEXT_MKDIR(handle, client_fname, mode);
810 TALLOC_FREE(client_fname);
811 DEBUG(10, ("Leaving with path '%s'\n", path));
815 static int um_rmdir(vfs_handle_struct *handle,
816 const struct smb_filename *smb_fname)
819 const char *path = smb_fname->base_name;
820 struct smb_filename *client_fname = NULL;
822 DEBUG(10, ("Entering with path '%s'\n", path));
824 if (!is_in_media_files(path)) {
825 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
828 status = alloc_get_client_smb_fname(handle,
836 status = SMB_VFS_NEXT_RMDIR(handle, client_fname);
838 TALLOC_FREE(client_fname);
839 DEBUG(10, ("Leaving with path '%s'\n", path));
843 static int um_closedir(vfs_handle_struct *handle,
846 DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
850 return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
853 static int um_open(vfs_handle_struct *handle,
854 struct smb_filename *smb_fname,
860 struct smb_filename *client_fname = NULL;
862 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
863 smb_fname->base_name));
865 if (!is_in_media_files(smb_fname->base_name)) {
866 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
869 if (alloc_get_client_smb_fname(handle, talloc_tos(),
878 * What about fsp->fsp_name? We also have to get correct stat
879 * info into fsp and smb_fname for DB files, don't we?
882 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
883 "smb_fname->st.st_ex_mtime %s"
884 "fsp->fsp_name->st.st_ex_mtime %s",
885 smb_fname->base_name,
886 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
887 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
889 ret = SMB_VFS_NEXT_OPEN(handle, client_fname, fsp, flags, mode);
891 TALLOC_FREE(client_fname);
892 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
893 smb_fname->base_name));
897 static NTSTATUS um_create_file(vfs_handle_struct *handle,
898 struct smb_request *req,
899 uint16_t root_dir_fid,
900 struct smb_filename *smb_fname,
901 uint32_t access_mask,
902 uint32_t share_access,
903 uint32_t create_disposition,
904 uint32_t create_options,
905 uint32_t file_attributes,
906 uint32_t oplock_request,
907 struct smb2_lease *lease,
908 uint64_t allocation_size,
909 uint32_t private_flags,
910 struct security_descriptor *sd,
911 struct ea_list *ea_list,
912 files_struct **result_fsp,
914 const struct smb2_create_blobs *in_context_blobs,
915 struct smb2_create_blobs *out_context_blobs)
918 struct smb_filename *client_fname = NULL;
920 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
921 smb_fname->base_name));
923 if (!is_in_media_files(smb_fname->base_name)) {
924 return SMB_VFS_NEXT_CREATE_FILE(
946 if (alloc_get_client_smb_fname(handle, talloc_tos(),
949 status = map_nt_error_from_unix(errno);
955 * This only creates files, so we don't have to worry about
956 * our fake directory stat'ing here. But we still need to
957 * route stat calls for DB files properly, right?
959 status = SMB_VFS_NEXT_CREATE_FILE(
980 TALLOC_FREE(client_fname);
981 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
982 "smb_fname->st.st_ex_mtime %s"
983 " fsp->fsp_name->st.st_ex_mtime %s",
984 smb_fname->base_name,
985 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
986 (*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
987 ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
992 static int um_rename(vfs_handle_struct *handle,
993 const struct smb_filename *smb_fname_src,
994 const struct smb_filename *smb_fname_dst)
997 struct smb_filename *src_client_fname = NULL;
998 struct smb_filename *dst_client_fname = NULL;
1000 DEBUG(10, ("Entering with "
1001 "smb_fname_src->base_name '%s', "
1002 "smb_fname_dst->base_name '%s'\n",
1003 smb_fname_src->base_name,
1004 smb_fname_dst->base_name));
1006 if (!is_in_media_files(smb_fname_src->base_name)
1008 !is_in_media_files(smb_fname_dst->base_name)) {
1009 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src,
1013 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1020 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1028 status = SMB_VFS_NEXT_RENAME(handle, src_client_fname,
1031 TALLOC_FREE(dst_client_fname);
1032 TALLOC_FREE(src_client_fname);
1033 DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
1034 " smb_fname_dst->base_name '%s'\n",
1035 smb_fname_src->base_name,
1036 smb_fname_dst->base_name));
1042 * Failure: set errno, return -1
1044 static int um_stat(vfs_handle_struct *handle,
1045 struct smb_filename *smb_fname)
1048 struct smb_filename *client_fname = NULL;
1050 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1051 smb_fname->base_name));
1053 if (!is_in_media_files(smb_fname->base_name)) {
1054 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1057 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1063 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1064 client_fname->base_name));
1066 status = SMB_VFS_NEXT_STAT(handle, client_fname);
1072 * Unlike functions with const smb_filename, we have to modify
1073 * smb_fname itself to pass our info back up.
1075 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1076 smb_fname->base_name, client_fname->base_name));
1077 smb_fname->st = client_fname->st;
1080 TALLOC_FREE(client_fname);
1081 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1082 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1086 static int um_lstat(vfs_handle_struct *handle,
1087 struct smb_filename *smb_fname)
1090 struct smb_filename *client_fname = NULL;
1092 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1093 smb_fname->base_name));
1095 if (!is_in_media_files(smb_fname->base_name)) {
1096 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1099 client_fname = NULL;
1101 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1107 status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
1112 smb_fname->st = client_fname->st;
1115 TALLOC_FREE(client_fname);
1116 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1117 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1121 static int um_fstat(vfs_handle_struct *handle,
1122 files_struct *fsp, SMB_STRUCT_STAT *sbuf)
1126 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1127 "'%s'\n", fsp_str_dbg(fsp)));
1129 status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1134 if ((fsp->fsp_name == NULL) ||
1135 !is_in_media_files(fsp->fsp_name->base_name)) {
1139 status = um_stat(handle, fsp->fsp_name);
1144 *sbuf = fsp->fsp_name->st;
1147 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1148 fsp->fsp_name != NULL ?
1149 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) : "0"));
1153 static int um_unlink(vfs_handle_struct *handle,
1154 const struct smb_filename *smb_fname)
1157 struct smb_filename *client_fname = NULL;
1159 DEBUG(10, ("Entering um_unlink\n"));
1161 if (!is_in_media_files(smb_fname->base_name)) {
1162 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1165 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1172 status = SMB_VFS_NEXT_UNLINK(handle, client_fname);
1175 TALLOC_FREE(client_fname);
1179 static int um_chmod(vfs_handle_struct *handle,
1180 const struct smb_filename *smb_fname,
1184 struct smb_filename *client_fname = NULL;
1186 DEBUG(10, ("Entering um_chmod\n"));
1188 if (!is_in_media_files(smb_fname->base_name)) {
1189 return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
1192 status = alloc_get_client_smb_fname(handle,
1200 status = SMB_VFS_NEXT_CHMOD(handle, client_fname, mode);
1203 TALLOC_FREE(client_fname);
1207 static int um_chown(vfs_handle_struct *handle,
1208 const struct smb_filename *smb_fname,
1213 struct smb_filename *client_fname = NULL;
1215 DEBUG(10, ("Entering um_chown\n"));
1217 if (!is_in_media_files(smb_fname->base_name)) {
1218 return SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
1221 status = alloc_get_client_smb_fname(handle,
1229 status = SMB_VFS_NEXT_CHOWN(handle, client_fname, uid, gid);
1232 TALLOC_FREE(client_fname);
1236 static int um_lchown(vfs_handle_struct *handle,
1237 const struct smb_filename *smb_fname,
1242 struct smb_filename *client_fname = NULL;
1244 DEBUG(10, ("Entering um_lchown\n"));
1245 if (!is_in_media_files(smb_fname->base_name)) {
1246 return SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid);
1249 status = alloc_get_client_smb_fname(handle,
1257 status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid);
1260 TALLOC_FREE(client_fname);
1264 static int um_chdir(vfs_handle_struct *handle,
1265 const struct smb_filename *smb_fname)
1268 struct smb_filename *client_fname = NULL;
1270 DEBUG(10, ("Entering um_chdir\n"));
1272 if (!is_in_media_files(smb_fname->base_name)) {
1273 return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
1276 status = alloc_get_client_smb_fname(handle,
1284 status = SMB_VFS_NEXT_CHDIR(handle, client_fname);
1287 TALLOC_FREE(client_fname);
1291 static int um_ntimes(vfs_handle_struct *handle,
1292 const struct smb_filename *smb_fname,
1293 struct smb_file_time *ft)
1296 struct smb_filename *client_fname = NULL;
1298 DEBUG(10, ("Entering um_ntimes\n"));
1300 if (!is_in_media_files(smb_fname->base_name)) {
1301 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1304 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1305 smb_fname, &client_fname);
1310 status = SMB_VFS_NEXT_NTIMES(handle, client_fname, ft);
1313 TALLOC_FREE(client_fname);
1317 static int um_symlink(vfs_handle_struct *handle,
1318 const char *link_contents,
1319 const struct smb_filename *new_smb_fname)
1322 char *client_link_contents = NULL;
1323 struct smb_filename *new_client_fname = NULL;
1325 DEBUG(10, ("Entering um_symlink\n"));
1327 if (!is_in_media_files(link_contents) &&
1328 !is_in_media_files(new_smb_fname->base_name)) {
1329 return SMB_VFS_NEXT_SYMLINK(handle,
1334 status = alloc_get_client_path(handle, talloc_tos(),
1335 link_contents, &client_link_contents);
1339 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1340 new_smb_fname, &new_client_fname);
1345 status = SMB_VFS_NEXT_SYMLINK(handle,
1346 client_link_contents,
1350 TALLOC_FREE(client_link_contents);
1351 TALLOC_FREE(new_client_fname);
1355 static int um_readlink(vfs_handle_struct *handle,
1356 const struct smb_filename *smb_fname,
1361 struct smb_filename *client_fname = NULL;
1363 DEBUG(10, ("Entering um_readlink\n"));
1365 if (!is_in_media_files(smb_fname->base_name)) {
1366 return SMB_VFS_NEXT_READLINK(handle, smb_fname,
1370 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1371 smb_fname, &client_fname);
1376 status = SMB_VFS_NEXT_READLINK(handle, client_fname, buf, bufsiz);
1379 TALLOC_FREE(client_fname);
1383 static int um_link(vfs_handle_struct *handle,
1384 const struct smb_filename *old_smb_fname,
1385 const struct smb_filename *new_smb_fname)
1388 struct smb_filename *old_client_fname = NULL;
1389 struct smb_filename *new_client_fname = NULL;
1391 DEBUG(10, ("Entering um_link\n"));
1392 if (!is_in_media_files(old_smb_fname->base_name) &&
1393 !is_in_media_files(new_smb_fname->base_name)) {
1394 return SMB_VFS_NEXT_LINK(handle, old_smb_fname, new_smb_fname);
1397 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1398 old_smb_fname, &old_client_fname);
1402 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1403 new_smb_fname, &new_client_fname);
1408 status = SMB_VFS_NEXT_LINK(handle, old_client_fname, new_client_fname);
1411 TALLOC_FREE(old_client_fname);
1412 TALLOC_FREE(new_client_fname);
1416 static int um_mknod(vfs_handle_struct *handle,
1417 const struct smb_filename *smb_fname,
1422 struct smb_filename *client_fname = NULL;
1424 DEBUG(10, ("Entering um_mknod\n"));
1425 if (!is_in_media_files(smb_fname->base_name)) {
1426 return SMB_VFS_NEXT_MKNOD(handle, smb_fname, mode, dev);
1429 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1430 smb_fname, &client_fname);
1435 status = SMB_VFS_NEXT_MKNOD(handle, client_fname, mode, dev);
1438 TALLOC_FREE(client_fname);
1442 static struct smb_filename *um_realpath(vfs_handle_struct *handle,
1444 const struct smb_filename *smb_fname)
1446 struct smb_filename *client_fname = NULL;
1447 struct smb_filename *result_fname = NULL;
1450 DEBUG(10, ("Entering um_realpath\n"));
1452 if (!is_in_media_files(smb_fname->base_name)) {
1453 return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname);
1456 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1457 smb_fname, &client_fname);
1462 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, client_fname);
1465 TALLOC_FREE(client_fname);
1466 return result_fname;
1469 static int um_chflags(vfs_handle_struct *handle,
1470 const struct smb_filename *smb_fname,
1474 struct smb_filename *client_fname = NULL;
1476 DEBUG(10, ("Entering um_mknod\n"));
1477 if (!is_in_media_files(smb_fname->base_name)) {
1478 return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
1481 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1482 smb_fname, &client_fname);
1487 status = SMB_VFS_NEXT_CHFLAGS(handle, client_fname, flags);
1490 TALLOC_FREE(client_fname);
1494 static NTSTATUS um_streaminfo(struct vfs_handle_struct *handle,
1495 struct files_struct *fsp,
1496 const struct smb_filename *smb_fname,
1498 unsigned int *num_streams,
1499 struct stream_struct **streams)
1503 struct smb_filename *client_fname = NULL;
1505 DEBUG(10, ("Entering um_streaminfo\n"));
1507 if (!is_in_media_files(smb_fname->base_name)) {
1508 return SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname,
1509 ctx, num_streams, streams);
1512 ret = alloc_get_client_smb_fname(handle,
1517 status = NT_STATUS_NO_MEMORY;
1522 * This only works on files, so we don't have to worry about
1523 * our fake directory stat'ing here. But what does this
1524 * function do, exactly? Does it need extra modifications for
1527 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, client_fname,
1528 ctx, num_streams, streams);
1530 TALLOC_FREE(client_fname);
1535 * Ignoring get_real_filename function because the default doesn't do
1539 static NTSTATUS um_get_nt_acl(vfs_handle_struct *handle,
1540 const struct smb_filename *smb_fname,
1541 uint32_t security_info,
1542 TALLOC_CTX *mem_ctx,
1543 struct security_descriptor **ppdesc)
1546 char *client_path = NULL;
1547 struct smb_filename *client_smb_fname = NULL;
1550 DEBUG(10, ("Entering um_get_nt_acl\n"));
1552 if (!is_in_media_files(smb_fname->base_name)) {
1553 return SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname,
1558 ret = alloc_get_client_path(handle, talloc_tos(),
1559 smb_fname->base_name, &client_path);
1561 status = map_nt_error_from_unix(errno);
1565 client_smb_fname = synthetic_smb_fname(talloc_tos(),
1570 if (client_smb_fname == NULL) {
1571 TALLOC_FREE(client_path);
1572 return NT_STATUS_NO_MEMORY;
1575 status = SMB_VFS_NEXT_GET_NT_ACL(handle, client_smb_fname,
1579 TALLOC_FREE(client_smb_fname);
1580 TALLOC_FREE(client_path);
1584 static SMB_ACL_T um_sys_acl_get_file(vfs_handle_struct *handle,
1585 const struct smb_filename *smb_fname,
1586 SMB_ACL_TYPE_T type,
1587 TALLOC_CTX *mem_ctx)
1590 int saved_errno = 0;
1591 struct smb_filename *client_fname = NULL;
1594 DEBUG(10, ("Entering um_sys_acl_get_file\n"));
1596 if (!is_in_media_files(smb_fname->base_name)) {
1597 return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, smb_fname,
1601 status = alloc_get_client_smb_fname(handle,
1606 ret = (SMB_ACL_T)NULL;
1610 ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, client_fname,
1614 if (ret == (SMB_ACL_T)NULL) {
1615 saved_errno = errno;
1617 TALLOC_FREE(client_fname);
1618 if (saved_errno != 0) {
1619 errno = saved_errno;
1624 static int um_sys_acl_set_file(vfs_handle_struct *handle,
1625 const struct smb_filename *smb_fname,
1626 SMB_ACL_TYPE_T acltype,
1630 int saved_errno = 0;
1631 struct smb_filename *client_fname = NULL;
1633 DEBUG(10, ("Entering um_sys_acl_set_file\n"));
1635 if (!is_in_media_files(smb_fname->base_name)) {
1636 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, smb_fname,
1640 status = alloc_get_client_smb_fname(handle,
1648 status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, client_fname,
1653 saved_errno = errno;
1655 TALLOC_FREE(client_fname);
1656 if (saved_errno != 0) {
1657 errno = saved_errno;
1662 static int um_sys_acl_delete_def_file(vfs_handle_struct *handle,
1663 const struct smb_filename *smb_fname)
1666 int saved_errno = 0;
1667 struct smb_filename *client_fname = NULL;
1669 DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
1671 if (!is_in_media_files(smb_fname->base_name)) {
1672 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle,
1676 status = alloc_get_client_smb_fname(handle,
1684 status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, client_fname);
1688 saved_errno = errno;
1690 TALLOC_FREE(client_fname);
1691 if (saved_errno != 0) {
1692 errno = saved_errno;
1697 static ssize_t um_getxattr(struct vfs_handle_struct *handle,
1698 const struct smb_filename *smb_fname,
1704 struct smb_filename *client_fname = NULL;
1707 DEBUG(10, ("Entering um_getxattr\n"));
1708 if (!is_in_media_files(smb_fname->base_name)) {
1709 return SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
1713 status = alloc_get_client_smb_fname(handle,
1722 ret = SMB_VFS_NEXT_GETXATTR(handle, client_fname, name, value, size);
1724 TALLOC_FREE(client_fname);
1728 static ssize_t um_listxattr(struct vfs_handle_struct *handle,
1729 const struct smb_filename *smb_fname,
1734 struct smb_filename *client_fname = NULL;
1737 DEBUG(10, ("Entering um_listxattr\n"));
1739 if (!is_in_media_files(smb_fname->base_name)) {
1740 return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
1743 status = alloc_get_client_smb_fname(handle,
1752 ret = SMB_VFS_NEXT_LISTXATTR(handle, client_fname, list, size);
1755 TALLOC_FREE(client_fname);
1759 static int um_removexattr(struct vfs_handle_struct *handle,
1760 const struct smb_filename *smb_fname,
1764 struct smb_filename *client_fname = NULL;
1766 DEBUG(10, ("Entering um_removexattr\n"));
1768 if (!is_in_media_files(smb_fname->base_name)) {
1769 return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
1772 status = alloc_get_client_smb_fname(handle,
1780 status = SMB_VFS_NEXT_REMOVEXATTR(handle, client_fname, name);
1783 TALLOC_FREE(client_fname);
1787 static int um_setxattr(struct vfs_handle_struct *handle,
1788 const struct smb_filename *smb_fname,
1795 struct smb_filename *client_fname = NULL;
1797 DEBUG(10, ("Entering um_setxattr\n"));
1799 if (!is_in_media_files(smb_fname->base_name)) {
1800 return SMB_VFS_NEXT_SETXATTR(handle, smb_fname, name, value,
1804 status = alloc_get_client_smb_fname(handle,
1812 status = SMB_VFS_NEXT_SETXATTR(handle, client_fname, name, value,
1816 TALLOC_FREE(client_fname);
1820 static int um_connect(vfs_handle_struct *handle,
1821 const char *service,
1825 struct um_config_data *config;
1828 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1833 config = talloc_zero(handle->conn, struct um_config_data);
1835 DEBUG(1, ("talloc_zero() failed\n"));
1840 enumval = lp_parm_enum(SNUM(handle->conn), UM_PARAM_TYPE_NAME,
1841 "clientid", um_clientid, UM_CLIENTID_NAME);
1842 if (enumval == -1) {
1843 DEBUG(1, ("value for %s: type unknown\n",
1844 UM_PARAM_TYPE_NAME));
1847 config->clientid = (enum um_clientid)enumval;
1849 SMB_VFS_HANDLE_SET_DATA(handle, config,
1850 NULL, struct um_config_data,
1856 /* VFS operations structure */
1858 static struct vfs_fn_pointers vfs_um_fns = {
1859 .connect_fn = um_connect,
1861 /* Disk operations */
1863 .statvfs_fn = um_statvfs,
1865 /* Directory operations */
1867 .opendir_fn = um_opendir,
1868 .fdopendir_fn = um_fdopendir,
1869 .readdir_fn = um_readdir,
1870 .seekdir_fn = um_seekdir,
1871 .telldir_fn = um_telldir,
1872 .rewind_dir_fn = um_rewinddir,
1873 .mkdir_fn = um_mkdir,
1874 .rmdir_fn = um_rmdir,
1875 .closedir_fn = um_closedir,
1877 /* File operations */
1880 .create_file_fn = um_create_file,
1881 .rename_fn = um_rename,
1883 .lstat_fn = um_lstat,
1884 .fstat_fn = um_fstat,
1885 .unlink_fn = um_unlink,
1886 .chmod_fn = um_chmod,
1887 .chown_fn = um_chown,
1888 .lchown_fn = um_lchown,
1889 .chdir_fn = um_chdir,
1890 .ntimes_fn = um_ntimes,
1891 .symlink_fn = um_symlink,
1892 .readlink_fn = um_readlink,
1894 .mknod_fn = um_mknod,
1895 .realpath_fn = um_realpath,
1896 .chflags_fn = um_chflags,
1897 .streaminfo_fn = um_streaminfo,
1899 /* NT ACL operations. */
1901 .get_nt_acl_fn = um_get_nt_acl,
1903 /* POSIX ACL operations. */
1905 .sys_acl_get_file_fn = um_sys_acl_get_file,
1906 .sys_acl_set_file_fn = um_sys_acl_set_file,
1907 .sys_acl_delete_def_file_fn = um_sys_acl_delete_def_file,
1909 /* EA operations. */
1910 .getxattr_fn = um_getxattr,
1911 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1912 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1913 .listxattr_fn = um_listxattr,
1914 .removexattr_fn = um_removexattr,
1915 .setxattr_fn = um_setxattr,
1919 NTSTATUS vfs_unityed_media_init(TALLOC_CTX *ctx)
1921 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1922 "unityed_media", &vfs_um_fns);
1923 if (!NT_STATUS_IS_OK(ret)) {
1927 vfs_um_debug_level = debug_add_class("unityed_media");
1929 if (vfs_um_debug_level == -1) {
1930 vfs_um_debug_level = DBGC_VFS;
1931 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1932 "debugging class.\n"));