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)strtoul(p, &endp, 10);
124 DEBUG(10, ("num_suffix = '%ju'\n",
134 /* Add "_<remote_name>.<number>" suffix to path or filename.
137 * Failure: set errno, path NULL, return -1
140 static int alloc_append_client_suffix(vfs_handle_struct *handle,
145 const char *clientid;
146 struct um_config_data *config;
148 DEBUG(10, ("Entering with path '%s'\n", *path));
150 SMB_VFS_HANDLE_GET_DATA(handle, config,
151 struct um_config_data,
154 (void)get_digit_group(*path, &number);
156 switch (config->clientid) {
159 clientid = tsocket_address_inet_addr_string(
160 handle->conn->sconn->remote_address, talloc_tos());
161 if (clientid == NULL) {
168 case UM_CLIENTID_HOSTNAME:
169 clientid = get_remote_machine_name();
172 case UM_CLIENTID_NAME:
174 clientid = get_current_username();
178 *path = talloc_asprintf_append(*path, "_%s.%ju",
181 DEBUG(1, ("alloc_append_client_suffix "
187 DEBUG(10, ("Leaving with *path '%s'\n", *path));
192 /* Returns true if the file or directory begins with the appledouble
195 static bool is_apple_double(const char* fname)
199 DEBUG(10, ("Entering with fname '%s'\n", fname));
201 if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
204 DEBUG(10, ("Leaving with ret '%s'\n",
205 ret == true ? "true" : "false"));
209 static bool starts_with_media_dir(const char* media_dirname,
210 size_t media_dirname_len,
214 const char *path_start = path;
216 DEBUG(10, ("Entering with media_dirname '%s' "
217 "path '%s'\n", media_dirname, path));
219 /* Sometimes Samba gives us "./OMFI MediaFiles". */
220 if (strnequal(path, "./", 2)) {
224 if (strnequal(media_dirname, path_start, media_dirname_len)
226 ((path_start[media_dirname_len] == '\0') ||
227 (path_start[media_dirname_len] == '/'))) {
231 DEBUG(10, ("Leaving with ret '%s'\n",
232 ret == true ? "true" : "false"));
237 * Returns true if the file or directory referenced by the path is ONE
238 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
241 static bool is_in_media_dir(const char *path)
243 int transition_count = 0;
244 const char *path_start = path;
246 const char *media_dirname;
247 size_t media_dirname_len;
249 DEBUG(10, ("Entering with path'%s' ", path));
251 /* Sometimes Samba gives us "./OMFI MediaFiles". */
252 if (strnequal(path, "./", 2)) {
256 if (strnequal(path_start, AVID_MXF_DIRNAME, AVID_MXF_DIRNAME_LEN)) {
257 media_dirname = AVID_MXF_DIRNAME;
258 media_dirname_len = AVID_MXF_DIRNAME_LEN;
259 } else if (strnequal(path_start,
260 OMFI_MEDIAFILES_DIRNAME,
261 OMFI_MEDIAFILES_DIRNAME_LEN)) {
262 media_dirname = OMFI_MEDIAFILES_DIRNAME;
263 media_dirname_len = OMFI_MEDIAFILES_DIRNAME_LEN;
268 if (path_start[media_dirname_len] == '\0') {
272 p = path_start + media_dirname_len + 1;
275 if (*p == '\0' || *p == '/') {
276 if (strnequal(p - 3, "/..", 3)) {
278 } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
289 DEBUG(10, ("Going out with transition_count '%i'\n",
291 if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
293 ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
300 * Returns true if the file or directory referenced by the path is
301 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
302 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
303 * are assumed to be in the root directory, which is generally a safe
304 * assumption in the fixed-path world of Avid.
306 static bool is_in_media_files(const char *path)
310 DEBUG(10, ("Entering with path '%s'\n", path));
312 if (starts_with_media_dir(AVID_MXF_DIRNAME,
313 AVID_MXF_DIRNAME_LEN, path) ||
314 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME,
315 OMFI_MEDIAFILES_DIRNAME_LEN, path)) {
318 DEBUG(10, ("Leaving with ret '%s'\n",
319 ret == true ? "true" : "false"));
324 /* Add client suffix to "pure-number" path.
326 * Caller must free newPath.
329 * Failure: set errno, newPath NULL, return -1
331 static int alloc_get_client_path(vfs_handle_struct *handle,
342 *path_out = talloc_strdup(ctx, path_in);
343 if (*path_out == NULL) {
344 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
348 (void)get_digit_group(*path_out, &number);
350 digits = talloc_asprintf(NULL, "%ju", number);
351 if (digits == NULL) {
352 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
355 digits_len = strlen(digits);
357 p = strstr_m(path_in, digits);
360 ((p[digits_len] == '\0') || (p[digits_len] == '/'))
362 (((p - path_in > 0) && (p[-1] == '/'))
364 (((p - path_in) > APPLE_DOUBLE_PREFIX_LEN)
366 is_apple_double(p - APPLE_DOUBLE_PREFIX_LEN)
368 (p[-(APPLE_DOUBLE_PREFIX_LEN + 1)] == '/'))))
370 (*path_out)[p - path_in + digits_len] = '\0';
372 status = alloc_append_client_suffix(handle, path_out);
377 *path_out = talloc_strdup_append(*path_out, p + digits_len);
378 if (*path_out == NULL) {
379 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
385 /* path_out must be freed in caller. */
386 DEBUG(10, ("Result:'%s'\n", *path_out));
392 * Failure: set errno, return -1
394 static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
396 const struct smb_filename *smb_fname,
397 struct smb_filename **client_fname)
401 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
402 smb_fname->base_name));
404 *client_fname = cp_smb_filename(ctx, smb_fname);
405 if (*client_fname == NULL) {
406 DEBUG(1, ("cp_smb_filename returned NULL\n"));
409 status = alloc_get_client_path(handle, ctx,
410 smb_fname->base_name,
411 &(*client_fname)->base_name);
416 DEBUG(10, ("Leaving with (*client_fname)->base_name "
417 "'%s'\n", (*client_fname)->base_name));
425 * Failure: set errno, return -1
427 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
430 const char *suffix_number)
434 DEBUG(10, ("Entering with suffix_number '%s'\n",
437 *path = talloc_strdup(ctx, suffix_number);
439 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
442 status = alloc_append_client_suffix(handle, path);
447 DEBUG(10, ("Leaving with *path '%s'\n", *path));
452 static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
454 struct um_dirinfo_struct **di_result)
459 struct um_dirinfo_struct *dip;
461 DEBUG(10, ("Entering with fname '%s'\n", fname));
463 *di_result = talloc(NULL, struct um_dirinfo_struct);
464 if (*di_result == NULL) {
469 dip->dirpath = talloc_strdup(dip, fname);
470 if (dip->dirpath == NULL) {
474 if (!is_in_media_files(fname)) {
475 dip->isInMediaFiles = false;
476 dip->clientPath = NULL;
477 dip->clientSubDirname = NULL;
481 dip->isInMediaFiles = true;
483 (void)get_digit_group(fname, &number);
484 digits = talloc_asprintf(talloc_tos(), "%ju", number);
485 if (digits == NULL) {
489 status = alloc_set_client_dirinfo_path(handle, dip,
490 &dip->clientSubDirname,
496 status = alloc_get_client_path(handle, dip, fname,
498 if (status != 0 || dip->clientPath == NULL) {
503 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
504 "(*dirInfo)->clientPath '%s'\n",
505 dip->dirpath, dip->clientPath));
509 DEBUG(1, ("Failing with fname '%s'\n", fname));
510 TALLOC_FREE(*di_result);
516 /**********************************************************************
518 **********************************************************************/
522 * Failure: set errno, return -1
524 static int um_statvfs(struct vfs_handle_struct *handle,
526 struct vfs_statvfs_struct *statbuf)
529 char *clientPath = NULL;
531 DEBUG(10, ("Entering with path '%s'\n", path));
533 if (!is_in_media_files(path)) {
534 return SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
537 status = alloc_get_client_path(handle, talloc_tos(),
543 status = SMB_VFS_NEXT_STATVFS(handle, clientPath, statbuf);
545 TALLOC_FREE(clientPath);
546 DEBUG(10, ("Leaving with path '%s'\n", path));
550 /* Success: return a um_dirinfo_struct cast as a DIR
551 * Failure: set errno, return NULL
553 static DIR *um_opendir(vfs_handle_struct *handle,
554 const struct smb_filename *smb_fname,
558 struct um_dirinfo_struct *dirInfo;
560 DEBUG(10, ("Entering with fname '%s'\n", smb_fname->base_name));
562 if (alloc_set_client_dirinfo(handle, smb_fname->base_name, &dirInfo)) {
566 if (!dirInfo->isInMediaFiles) {
567 dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
568 handle, smb_fname, mask, attr);
570 struct smb_filename *client_smb_fname =
571 synthetic_smb_fname(talloc_tos(),
575 if (client_smb_fname == NULL) {
579 dirInfo->dirstream = SMB_VFS_NEXT_OPENDIR(
580 handle, client_smb_fname, mask, attr);
582 TALLOC_FREE(client_smb_fname);
585 if (dirInfo->dirstream == NULL) {
589 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
590 "dirInfo->clientPath '%s'\n",
592 dirInfo->clientPath));
593 return (DIR*)dirInfo;
596 DEBUG(1, ("Failing with fname '%s'\n", smb_fname->base_name));
597 TALLOC_FREE(dirInfo);
601 static DIR *um_fdopendir(vfs_handle_struct *handle,
606 struct um_dirinfo_struct *dirInfo = NULL;
609 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
610 fsp->fsp_name->base_name));
612 dirstream = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
617 if (alloc_set_client_dirinfo(handle,
618 fsp->fsp_name->base_name,
623 dirInfo->dirstream = dirstream;
625 if (!dirInfo->isInMediaFiles) {
627 * FIXME: this is the original code, something must be
628 * missing here, but what? -slow
634 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
635 "dirInfo->clientPath '%s', "
636 "fsp->fsp_name->st.st_ex_mtime %s",
639 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
640 return (DIR *) dirInfo;
643 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
644 fsp->fsp_name->base_name));
645 TALLOC_FREE(dirInfo);
650 * skip own suffixed directory
651 * replace own suffixed directory with non suffixed.
653 * Success: return dirent
654 * End of data: return NULL
655 * Failure: set errno, return NULL
657 static struct dirent *um_readdir(vfs_handle_struct *handle,
659 SMB_STRUCT_STAT *sbuf)
661 um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
662 struct dirent *d = NULL;
665 DEBUG(10, ("dirInfo->dirpath '%s', "
666 "dirInfo->clientPath '%s', "
667 "dirInfo->isInMediaFiles '%s', "
668 "dirInfo->clientSubDirname '%s'\n",
671 dirInfo->isInMediaFiles ? "true" : "false",
672 dirInfo->clientSubDirname));
674 if (!dirInfo->isInMediaFiles) {
675 return SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
686 d = SMB_VFS_NEXT_READDIR(handle, dirInfo->dirstream, sbuf);
692 /* ignore apple double prefix for logic below */
693 if (is_apple_double(d->d_name)) {
694 dname = &d->d_name[APPLE_DOUBLE_PREFIX_LEN];
695 isAppleDouble = true;
698 isAppleDouble = false;
701 DEBUG(10, ("dname = '%s'\n", dname));
703 (void)get_digit_group(dname, &number);
704 digits = talloc_asprintf(talloc_tos(), "%ju", number);
705 if (digits == NULL) {
706 DEBUG(1, ("out of memory"));
709 digits_len = strlen(digits);
711 if (alloc_set_client_dirinfo_path(handle,
713 &((dirInfo)->clientSubDirname),
719 * If set to "true", vfs shows digits-only
720 * non-suffixed subdirectories. Normally, such
721 * subdirectories can exists only in non-media
722 * directories, so we set it to "false". Otherwise,
723 * if we have such subdirectories (probably created
724 * over not "unityed" connection), it can be little
727 if (strequal(dname, digits)) {
729 } else if (strequal(dname, dirInfo->clientSubDirname)) {
731 * Remove suffix of this client's suffixed
735 d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
737 d->d_name[digits_len] = '\0';
739 } else if (strnequal(digits, dname, digits_len)) {
741 * Set to false to see another clients subdirectories
747 DEBUG(10, ("Leaving um_readdir\n"));
750 TALLOC_FREE(dirInfo);
754 static void um_seekdir(vfs_handle_struct *handle,
758 DEBUG(10, ("Entering and leaving um_seekdir\n"));
759 SMB_VFS_NEXT_SEEKDIR(handle,
760 ((um_dirinfo_struct*)dirp)->dirstream, offset);
763 static long um_telldir(vfs_handle_struct *handle,
766 DEBUG(10, ("Entering and leaving um_telldir\n"));
767 return SMB_VFS_NEXT_TELLDIR(handle,
768 ((um_dirinfo_struct*)dirp)->dirstream);
771 static void um_rewinddir(vfs_handle_struct *handle,
774 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
775 SMB_VFS_NEXT_REWINDDIR(handle,
776 ((um_dirinfo_struct*)dirp)->dirstream);
779 static int um_mkdir(vfs_handle_struct *handle,
780 const struct smb_filename *smb_fname,
784 const char *path = smb_fname->base_name;
785 struct smb_filename *client_fname = NULL;
787 DEBUG(10, ("Entering with path '%s'\n", path));
789 if (!is_in_media_files(path) || !is_in_media_dir(path)) {
790 return SMB_VFS_NEXT_MKDIR(handle, smb_fname, mode);
793 status = alloc_get_client_smb_fname(handle,
801 status = SMB_VFS_NEXT_MKDIR(handle, client_fname, mode);
803 TALLOC_FREE(client_fname);
804 DEBUG(10, ("Leaving with path '%s'\n", path));
808 static int um_rmdir(vfs_handle_struct *handle,
809 const struct smb_filename *smb_fname)
812 const char *path = smb_fname->base_name;
813 struct smb_filename *client_fname = NULL;
815 DEBUG(10, ("Entering with path '%s'\n", path));
817 if (!is_in_media_files(path)) {
818 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
821 status = alloc_get_client_smb_fname(handle,
829 status = SMB_VFS_NEXT_RMDIR(handle, client_fname);
831 TALLOC_FREE(client_fname);
832 DEBUG(10, ("Leaving with path '%s'\n", path));
836 static int um_closedir(vfs_handle_struct *handle,
839 DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
843 return SMB_VFS_NEXT_CLOSEDIR(handle, realdirp);
846 static void um_init_search_op(vfs_handle_struct *handle,
849 DEBUG(10, ("Entering and leaving um_init_search_op\n"));
851 SMB_VFS_NEXT_INIT_SEARCH_OP(handle,
852 ((um_dirinfo_struct*)dirp)->dirstream);
855 static int um_open(vfs_handle_struct *handle,
856 struct smb_filename *smb_fname,
862 struct smb_filename *client_fname = NULL;
864 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
865 smb_fname->base_name));
867 if (!is_in_media_files(smb_fname->base_name)) {
868 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
871 if (alloc_get_client_smb_fname(handle, talloc_tos(),
880 * What about fsp->fsp_name? We also have to get correct stat
881 * info into fsp and smb_fname for DB files, don't we?
884 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
885 "smb_fname->st.st_ex_mtime %s"
886 "fsp->fsp_name->st.st_ex_mtime %s",
887 smb_fname->base_name,
888 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
889 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
891 ret = SMB_VFS_NEXT_OPEN(handle, client_fname, fsp, flags, mode);
893 TALLOC_FREE(client_fname);
894 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
895 smb_fname->base_name));
899 static NTSTATUS um_create_file(vfs_handle_struct *handle,
900 struct smb_request *req,
901 uint16_t root_dir_fid,
902 struct smb_filename *smb_fname,
903 uint32_t access_mask,
904 uint32_t share_access,
905 uint32_t create_disposition,
906 uint32_t create_options,
907 uint32_t file_attributes,
908 uint32_t oplock_request,
909 struct smb2_lease *lease,
910 uint64_t allocation_size,
911 uint32_t private_flags,
912 struct security_descriptor *sd,
913 struct ea_list *ea_list,
914 files_struct **result_fsp,
916 const struct smb2_create_blobs *in_context_blobs,
917 struct smb2_create_blobs *out_context_blobs)
920 struct smb_filename *client_fname = NULL;
922 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
923 smb_fname->base_name));
925 if (!is_in_media_files(smb_fname->base_name)) {
926 return SMB_VFS_NEXT_CREATE_FILE(
948 if (alloc_get_client_smb_fname(handle, talloc_tos(),
951 status = map_nt_error_from_unix(errno);
957 * This only creates files, so we don't have to worry about
958 * our fake directory stat'ing here. But we still need to
959 * route stat calls for DB files properly, right?
961 status = SMB_VFS_NEXT_CREATE_FILE(
982 TALLOC_FREE(client_fname);
983 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
984 "smb_fname->st.st_ex_mtime %s"
985 " fsp->fsp_name->st.st_ex_mtime %s",
986 smb_fname->base_name,
987 ctime(&(smb_fname->st.st_ex_mtime.tv_sec)),
988 (*result_fsp) && VALID_STAT((*result_fsp)->fsp_name->st) ?
989 ctime(&((*result_fsp)->fsp_name->st.st_ex_mtime.tv_sec)) :
994 static int um_rename(vfs_handle_struct *handle,
995 const struct smb_filename *smb_fname_src,
996 const struct smb_filename *smb_fname_dst)
999 struct smb_filename *src_client_fname = NULL;
1000 struct smb_filename *dst_client_fname = NULL;
1002 DEBUG(10, ("Entering with "
1003 "smb_fname_src->base_name '%s', "
1004 "smb_fname_dst->base_name '%s'\n",
1005 smb_fname_src->base_name,
1006 smb_fname_dst->base_name));
1008 if (!is_in_media_files(smb_fname_src->base_name)
1010 !is_in_media_files(smb_fname_dst->base_name)) {
1011 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src,
1015 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1022 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1030 status = SMB_VFS_NEXT_RENAME(handle, src_client_fname,
1033 TALLOC_FREE(dst_client_fname);
1034 TALLOC_FREE(src_client_fname);
1035 DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
1036 " smb_fname_dst->base_name '%s'\n",
1037 smb_fname_src->base_name,
1038 smb_fname_dst->base_name));
1044 * Failure: set errno, return -1
1046 static int um_stat(vfs_handle_struct *handle,
1047 struct smb_filename *smb_fname)
1050 struct smb_filename *client_fname = NULL;
1052 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1053 smb_fname->base_name));
1055 if (!is_in_media_files(smb_fname->base_name)) {
1056 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1059 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1065 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1066 client_fname->base_name));
1068 status = SMB_VFS_NEXT_STAT(handle, client_fname);
1074 * Unlike functions with const smb_filename, we have to modify
1075 * smb_fname itself to pass our info back up.
1077 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1078 smb_fname->base_name, client_fname->base_name));
1079 smb_fname->st = client_fname->st;
1082 TALLOC_FREE(client_fname);
1083 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1084 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1088 static int um_lstat(vfs_handle_struct *handle,
1089 struct smb_filename *smb_fname)
1092 struct smb_filename *client_fname = NULL;
1094 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1095 smb_fname->base_name));
1097 if (!is_in_media_files(smb_fname->base_name)) {
1098 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1101 client_fname = NULL;
1103 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1109 status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
1114 smb_fname->st = client_fname->st;
1117 TALLOC_FREE(client_fname);
1118 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1119 ctime(&(smb_fname->st.st_ex_mtime.tv_sec))));
1123 static int um_fstat(vfs_handle_struct *handle,
1124 files_struct *fsp, SMB_STRUCT_STAT *sbuf)
1128 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1129 "'%s'\n", fsp_str_dbg(fsp)));
1131 status = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1136 if ((fsp->fsp_name == NULL) ||
1137 !is_in_media_files(fsp->fsp_name->base_name)) {
1141 status = um_stat(handle, fsp->fsp_name);
1146 *sbuf = fsp->fsp_name->st;
1149 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1150 fsp->fsp_name != NULL ?
1151 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec)) : "0"));
1155 static int um_unlink(vfs_handle_struct *handle,
1156 const struct smb_filename *smb_fname)
1159 struct smb_filename *client_fname = NULL;
1161 DEBUG(10, ("Entering um_unlink\n"));
1163 if (!is_in_media_files(smb_fname->base_name)) {
1164 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1167 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1174 status = SMB_VFS_NEXT_UNLINK(handle, client_fname);
1177 TALLOC_FREE(client_fname);
1181 static int um_chmod(vfs_handle_struct *handle,
1182 const struct smb_filename *smb_fname,
1186 struct smb_filename *client_fname = NULL;
1188 DEBUG(10, ("Entering um_chmod\n"));
1190 if (!is_in_media_files(smb_fname->base_name)) {
1191 return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
1194 status = alloc_get_client_smb_fname(handle,
1202 status = SMB_VFS_NEXT_CHMOD(handle, client_fname, mode);
1205 TALLOC_FREE(client_fname);
1209 static int um_chown(vfs_handle_struct *handle,
1210 const struct smb_filename *smb_fname,
1215 struct smb_filename *client_fname = NULL;
1217 DEBUG(10, ("Entering um_chown\n"));
1219 if (!is_in_media_files(smb_fname->base_name)) {
1220 return SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
1223 status = alloc_get_client_smb_fname(handle,
1231 status = SMB_VFS_NEXT_CHOWN(handle, client_fname, uid, gid);
1234 TALLOC_FREE(client_fname);
1238 static int um_lchown(vfs_handle_struct *handle,
1239 const struct smb_filename *smb_fname,
1244 struct smb_filename *client_fname = NULL;
1246 DEBUG(10, ("Entering um_lchown\n"));
1247 if (!is_in_media_files(smb_fname->base_name)) {
1248 return SMB_VFS_NEXT_LCHOWN(handle, smb_fname, uid, gid);
1251 status = alloc_get_client_smb_fname(handle,
1259 status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid);
1262 TALLOC_FREE(client_fname);
1266 static int um_chdir(vfs_handle_struct *handle,
1270 char *client_path = NULL;
1272 DEBUG(10, ("Entering um_chdir\n"));
1274 if (!is_in_media_files(path)) {
1275 return SMB_VFS_NEXT_CHDIR(handle, path);
1278 status = alloc_get_client_path(handle, talloc_tos(),
1279 path, &client_path);
1284 status = SMB_VFS_NEXT_CHDIR(handle, client_path);
1287 TALLOC_FREE(client_path);
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 *oldpath,
1319 const char *newpath)
1322 char *old_client_path = NULL;
1323 char *new_client_path = NULL;
1325 DEBUG(10, ("Entering um_symlink\n"));
1327 if (!is_in_media_files(oldpath) && !is_in_media_files(newpath)) {
1328 return SMB_VFS_NEXT_SYMLINK(handle, oldpath, newpath);
1331 status = alloc_get_client_path(handle, talloc_tos(),
1332 oldpath, &old_client_path);
1337 status = alloc_get_client_path(handle, talloc_tos(),
1338 newpath, &new_client_path);
1343 status = SMB_VFS_NEXT_SYMLINK(handle,
1348 TALLOC_FREE(new_client_path);
1349 TALLOC_FREE(old_client_path);
1353 static int um_readlink(vfs_handle_struct *handle,
1359 char *client_path = NULL;
1361 DEBUG(10, ("Entering um_readlink\n"));
1363 if (!is_in_media_files(path)) {
1364 return SMB_VFS_NEXT_READLINK(handle, path, buf, bufsiz);
1367 status = alloc_get_client_path(handle, talloc_tos(),
1368 path, &client_path);
1373 status = SMB_VFS_NEXT_READLINK(handle, client_path, buf, bufsiz);
1376 TALLOC_FREE(client_path);
1380 static int um_link(vfs_handle_struct *handle,
1381 const char *oldpath,
1382 const char *newpath)
1385 char *old_client_path = NULL;
1386 char *new_client_path = NULL;
1388 DEBUG(10, ("Entering um_link\n"));
1389 if (!is_in_media_files(oldpath) && !is_in_media_files(newpath)) {
1390 return SMB_VFS_NEXT_LINK(handle, oldpath, newpath);
1393 status = alloc_get_client_path(handle, talloc_tos(),
1394 oldpath, &old_client_path);
1399 status = alloc_get_client_path(handle, talloc_tos(),
1400 newpath, &new_client_path);
1405 status = SMB_VFS_NEXT_LINK(handle, old_client_path, new_client_path);
1408 TALLOC_FREE(new_client_path);
1409 TALLOC_FREE(old_client_path);
1413 static int um_mknod(vfs_handle_struct *handle,
1414 const char *pathname,
1419 char *client_path = NULL;
1421 DEBUG(10, ("Entering um_mknod\n"));
1422 if (!is_in_media_files(pathname)) {
1423 return SMB_VFS_NEXT_MKNOD(handle, pathname, mode, dev);
1426 status = alloc_get_client_path(handle, talloc_tos(),
1427 pathname, &client_path);
1432 status = SMB_VFS_NEXT_MKNOD(handle, client_path, mode, dev);
1435 TALLOC_FREE(client_path);
1439 static char *um_realpath(vfs_handle_struct *handle,
1443 char *client_path = NULL;
1446 DEBUG(10, ("Entering um_realpath\n"));
1448 if (!is_in_media_files(path)) {
1449 return SMB_VFS_NEXT_REALPATH(handle, path);
1452 status = alloc_get_client_path(handle, talloc_tos(),
1453 path, &client_path);
1458 buf = SMB_VFS_NEXT_REALPATH(handle, client_path);
1461 TALLOC_FREE(client_path);
1465 static int um_chflags(vfs_handle_struct *handle,
1470 char *client_path = NULL;
1472 DEBUG(10, ("Entering um_chflags\n"));
1474 if (!is_in_media_files(path)) {
1475 return SMB_VFS_NEXT_CHFLAGS(handle, path, flags);
1478 status = alloc_get_client_path(handle, talloc_tos(),
1479 path, &client_path);
1484 status = SMB_VFS_NEXT_CHFLAGS(handle, client_path, flags);
1486 TALLOC_FREE(client_path);
1490 static NTSTATUS um_streaminfo(struct vfs_handle_struct *handle,
1491 struct files_struct *fsp,
1492 const struct smb_filename *smb_fname,
1494 unsigned int *num_streams,
1495 struct stream_struct **streams)
1499 struct smb_filename *client_fname = NULL;
1501 DEBUG(10, ("Entering um_streaminfo\n"));
1503 if (!is_in_media_files(smb_fname->base_name)) {
1504 return SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname,
1505 ctx, num_streams, streams);
1508 ret = alloc_get_client_smb_fname(handle,
1513 status = NT_STATUS_NO_MEMORY;
1518 * This only works on files, so we don't have to worry about
1519 * our fake directory stat'ing here. But what does this
1520 * function do, exactly? Does it need extra modifications for
1523 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, client_fname,
1524 ctx, num_streams, streams);
1526 TALLOC_FREE(client_fname);
1531 * Ignoring get_real_filename function because the default doesn't do
1535 static NTSTATUS um_get_nt_acl(vfs_handle_struct *handle,
1536 const struct smb_filename *smb_fname,
1537 uint32_t security_info,
1538 TALLOC_CTX *mem_ctx,
1539 struct security_descriptor **ppdesc)
1542 char *client_path = NULL;
1543 struct smb_filename *client_smb_fname = NULL;
1546 DEBUG(10, ("Entering um_get_nt_acl\n"));
1548 if (!is_in_media_files(smb_fname->base_name)) {
1549 return SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname,
1554 ret = alloc_get_client_path(handle, talloc_tos(),
1555 smb_fname->base_name, &client_path);
1557 status = map_nt_error_from_unix(errno);
1561 client_smb_fname = synthetic_smb_fname(talloc_tos(),
1565 if (client_smb_fname == NULL) {
1566 TALLOC_FREE(client_path);
1567 return NT_STATUS_NO_MEMORY;
1570 status = SMB_VFS_NEXT_GET_NT_ACL(handle, client_smb_fname,
1574 TALLOC_FREE(client_smb_fname);
1575 TALLOC_FREE(client_path);
1579 static int um_chmod_acl(vfs_handle_struct *handle,
1580 const struct smb_filename *smb_fname,
1585 struct smb_filename *client_fname = NULL;
1587 DEBUG(10, ("Entering um_chmod_acl\n"));
1589 if (!is_in_media_files(smb_fname->base_name)) {
1590 return SMB_VFS_NEXT_CHMOD_ACL(handle, smb_fname, mode);
1593 status = alloc_get_client_smb_fname(handle,
1600 status = SMB_VFS_NEXT_CHMOD_ACL(handle, client_fname, mode);
1603 saved_errno = errno;
1604 TALLOC_FREE(client_fname);
1605 errno = saved_errno;
1609 static SMB_ACL_T um_sys_acl_get_file(vfs_handle_struct *handle,
1611 SMB_ACL_TYPE_T type,
1612 TALLOC_CTX *mem_ctx)
1615 char *client_path = NULL;
1618 DEBUG(10, ("Entering um_sys_acl_get_file\n"));
1620 if (!is_in_media_files(path_p)) {
1621 return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p,
1625 status = alloc_get_client_path(handle, talloc_tos(),
1626 path_p, &client_path);
1632 ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, client_path, type, mem_ctx);
1635 TALLOC_FREE(client_path);
1639 static int um_sys_acl_set_file(vfs_handle_struct *handle,
1641 SMB_ACL_TYPE_T acltype,
1645 char *client_path = NULL;
1647 DEBUG(10, ("Entering um_sys_acl_set_file\n"));
1649 if (!is_in_media_files(name)) {
1650 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name,
1654 status = alloc_get_client_path(handle, talloc_tos(),
1655 name, &client_path);
1660 status = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, client_path,
1664 TALLOC_FREE(client_path);
1668 static int um_sys_acl_delete_def_file(vfs_handle_struct *handle,
1672 char *client_path = NULL;
1674 DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
1676 if (!is_in_media_files(path)) {
1677 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, path);
1680 status = alloc_get_client_path(handle, talloc_tos(),
1681 path, &client_path);
1686 status = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, client_path);
1689 TALLOC_FREE(client_path);
1693 static ssize_t um_getxattr(struct vfs_handle_struct *handle,
1700 char *client_path = NULL;
1703 DEBUG(10, ("Entering um_getxattr\n"));
1704 if (!is_in_media_files(path)) {
1705 return SMB_VFS_NEXT_GETXATTR(handle, path, name, value, size);
1708 status = alloc_get_client_path(handle, talloc_tos(),
1709 path, &client_path);
1715 ret = SMB_VFS_NEXT_GETXATTR(handle, client_path, name, value, size);
1717 TALLOC_FREE(client_path);
1721 static ssize_t um_listxattr(struct vfs_handle_struct *handle,
1727 char *client_path = NULL;
1730 DEBUG(10, ("Entering um_listxattr\n"));
1732 if (!is_in_media_files(path)) {
1733 return SMB_VFS_NEXT_LISTXATTR(handle, path, list, size);
1736 status = alloc_get_client_path(handle, talloc_tos(),
1737 path, &client_path);
1743 ret = SMB_VFS_NEXT_LISTXATTR(handle, client_path, list, size);
1746 TALLOC_FREE(client_path);
1750 static int um_removexattr(struct vfs_handle_struct *handle,
1755 char *client_path = NULL;
1757 DEBUG(10, ("Entering um_removexattr\n"));
1759 if (!is_in_media_files(path)) {
1760 return SMB_VFS_NEXT_REMOVEXATTR(handle, path, name);
1763 status = alloc_get_client_path(handle, talloc_tos(),
1764 path, &client_path);
1769 status = SMB_VFS_NEXT_REMOVEXATTR(handle, client_path, name);
1772 TALLOC_FREE(client_path);
1776 static int um_setxattr(struct vfs_handle_struct *handle,
1784 char *client_path = NULL;
1786 DEBUG(10, ("Entering um_setxattr\n"));
1788 if (!is_in_media_files(path)) {
1789 return SMB_VFS_NEXT_SETXATTR(handle, path, name, value,
1793 status = alloc_get_client_path(handle, talloc_tos(),
1794 path, &client_path);
1799 status = SMB_VFS_NEXT_SETXATTR(handle, client_path, name, value,
1803 TALLOC_FREE(client_path);
1807 static bool um_is_offline(struct vfs_handle_struct *handle,
1808 const struct smb_filename *fname,
1809 SMB_STRUCT_STAT *sbuf)
1812 struct smb_filename *client_fname = NULL;
1815 DEBUG(10, ("Entering um_is_offline\n"));
1817 if (!is_in_media_files(fname->base_name)) {
1818 return SMB_VFS_NEXT_IS_OFFLINE(handle, fname, sbuf);
1821 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1822 fname, &client_fname);
1828 ret = SMB_VFS_NEXT_IS_OFFLINE(handle, client_fname, sbuf);
1831 TALLOC_FREE(client_fname);
1835 static int um_set_offline(struct vfs_handle_struct *handle,
1836 const struct smb_filename *fname)
1839 struct smb_filename *client_fname = NULL;
1841 DEBUG(10, ("Entering um_set_offline\n"));
1843 if (!is_in_media_files(fname->base_name)) {
1844 return SMB_VFS_NEXT_SET_OFFLINE(handle, fname);
1847 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1848 fname, &client_fname);
1853 status = SMB_VFS_NEXT_SET_OFFLINE(handle, client_fname);
1856 TALLOC_FREE(client_fname);
1860 static int um_connect(vfs_handle_struct *handle,
1861 const char *service,
1865 struct um_config_data *config;
1868 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1873 config = talloc_zero(handle->conn, struct um_config_data);
1875 DEBUG(1, ("talloc_zero() failed\n"));
1880 enumval = lp_parm_enum(SNUM(handle->conn), UM_PARAM_TYPE_NAME,
1881 "clientid", um_clientid, UM_CLIENTID_NAME);
1882 if (enumval == -1) {
1883 DEBUG(1, ("value for %s: type unknown\n",
1884 UM_PARAM_TYPE_NAME));
1887 config->clientid = (enum um_clientid)enumval;
1889 SMB_VFS_HANDLE_SET_DATA(handle, config,
1890 NULL, struct um_config_data,
1896 /* VFS operations structure */
1898 static struct vfs_fn_pointers vfs_um_fns = {
1899 .connect_fn = um_connect,
1901 /* Disk operations */
1903 .statvfs_fn = um_statvfs,
1905 /* Directory operations */
1907 .opendir_fn = um_opendir,
1908 .fdopendir_fn = um_fdopendir,
1909 .readdir_fn = um_readdir,
1910 .seekdir_fn = um_seekdir,
1911 .telldir_fn = um_telldir,
1912 .rewind_dir_fn = um_rewinddir,
1913 .mkdir_fn = um_mkdir,
1914 .rmdir_fn = um_rmdir,
1915 .closedir_fn = um_closedir,
1916 .init_search_op_fn = um_init_search_op,
1918 /* File operations */
1921 .create_file_fn = um_create_file,
1922 .rename_fn = um_rename,
1924 .lstat_fn = um_lstat,
1925 .fstat_fn = um_fstat,
1926 .unlink_fn = um_unlink,
1927 .chmod_fn = um_chmod,
1928 .chown_fn = um_chown,
1929 .lchown_fn = um_lchown,
1930 .chdir_fn = um_chdir,
1931 .ntimes_fn = um_ntimes,
1932 .symlink_fn = um_symlink,
1933 .readlink_fn = um_readlink,
1935 .mknod_fn = um_mknod,
1936 .realpath_fn = um_realpath,
1937 .chflags_fn = um_chflags,
1938 .streaminfo_fn = um_streaminfo,
1940 /* NT ACL operations. */
1942 .get_nt_acl_fn = um_get_nt_acl,
1944 /* POSIX ACL operations. */
1946 .chmod_acl_fn = um_chmod_acl,
1948 .sys_acl_get_file_fn = um_sys_acl_get_file,
1949 .sys_acl_set_file_fn = um_sys_acl_set_file,
1950 .sys_acl_delete_def_file_fn = um_sys_acl_delete_def_file,
1952 /* EA operations. */
1953 .getxattr_fn = um_getxattr,
1954 .listxattr_fn = um_listxattr,
1955 .removexattr_fn = um_removexattr,
1956 .setxattr_fn = um_setxattr,
1958 /* aio operations */
1960 /* offline operations */
1961 .is_offline_fn = um_is_offline,
1962 .set_offline_fn = um_set_offline
1965 NTSTATUS vfs_unityed_media_init(void);
1966 NTSTATUS vfs_unityed_media_init(void)
1968 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1969 "unityed_media", &vfs_um_fns);
1970 if (!NT_STATUS_IS_OK(ret)) {
1974 vfs_um_debug_level = debug_add_class("unityed_media");
1976 if (vfs_um_debug_level == -1) {
1977 vfs_um_debug_level = DBGC_VFS;
1978 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1979 "debugging class.\n"));