2 * Samba VFS module supporting multiple AVID clients sharing media.
4 * Copyright (C) 2005 Philip de Nier <philipn@users.sourceforge.net>
5 * Copyright (C) 2012 Andrew Klaassen <clawsoon@yahoo.com>
6 * Copyright (C) 2013 Milos Lukacek
7 * Copyright (C) 2013 Ralph Boehme <slow@samba.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 * Unityed Media is a Samba VFS module that allows multiple AVID
27 * clients to share media.
29 * Add this module to the vfs objects option in your Samba share
35 * vfs objects = unityed_media
38 * It is recommended that you separate out Samba shares for Mac
39 * and Windows clients, and add the following options to the shares
40 * for Windows clients (NOTE: replace @ with *):
42 * veto files = /.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/
43 * delete veto files = yes
45 * This prevents hidden files from Mac clients interfering with Windows
46 * clients. If you find any more problem hidden files then add them to
50 * This module is designed to work with AVID editing applications that
51 * look in the Avid MediaFiles or OMFI MediaFiles directory for media.
52 * It is not designed to work as expected in all circumstances for
58 #include "system/filesys.h"
59 #include "smbd/smbd.h"
60 #include "../smbd/globals.h"
62 #include "../lib/tsocket/tsocket.h"
63 #include "lib/util/smb_strtox.h"
65 #include "source3/lib/substitute.h"
67 #define UM_PARAM_TYPE_NAME "unityed_media"
69 static const char *AVID_MXF_DIRNAME
= "Avid MediaFiles/MXF";
70 static const size_t AVID_MXF_DIRNAME_LEN
= 19;
71 static const char *OMFI_MEDIAFILES_DIRNAME
= "OMFI MediaFiles";
72 static const size_t OMFI_MEDIAFILES_DIRNAME_LEN
= 15;
73 static const char *APPLE_DOUBLE_PREFIX
= "._";
74 static const size_t APPLE_DOUBLE_PREFIX_LEN
= 2;
75 static int vfs_um_debug_level
= DBGC_VFS
;
77 enum um_clientid
{UM_CLIENTID_NAME
, UM_CLIENTID_IP
, UM_CLIENTID_HOSTNAME
};
79 struct um_config_data
{
80 enum um_clientid clientid
;
83 static const struct enum_list um_clientid
[] = {
84 {UM_CLIENTID_NAME
, "user"},
85 {UM_CLIENTID_IP
, "ip"},
86 {UM_CLIENTID_HOSTNAME
, "hostname"},
90 /* supplements the directory list stream */
91 typedef struct um_dirinfo_struct
{
96 char *clientSubDirname
;
100 * Returns true and first group of digits in path, false and 0 otherwise
102 static bool get_digit_group(const char *path
, uintmax_t *digit
)
104 const char *p
= path
;
109 DEBUG(10, ("get_digit_group entering with path '%s'\n",
113 * Delibiretly initialize to 0 because callers use this result
114 * even though the string doesn't contain any number and we
120 cp
= next_codepoint(p
, &size
);
124 if ((size
== 1) && (isdigit(cp
))) {
125 *digit
= (uintmax_t)smb_strtoul(p
,
133 DEBUG(10, ("num_suffix = '%ju'\n",
143 /* Add "_<remote_name>.<number>" suffix to path or filename.
146 * Failure: set errno, path NULL, return -1
149 static int alloc_append_client_suffix(vfs_handle_struct
*handle
,
154 const char *clientid
;
155 struct um_config_data
*config
;
157 DEBUG(10, ("Entering with path '%s'\n", *path
));
159 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
160 struct um_config_data
,
163 (void)get_digit_group(*path
, &number
);
165 switch (config
->clientid
) {
168 clientid
= tsocket_address_inet_addr_string(
169 handle
->conn
->sconn
->remote_address
, talloc_tos());
170 if (clientid
== NULL
) {
177 case UM_CLIENTID_HOSTNAME
:
178 clientid
= get_remote_machine_name();
181 case UM_CLIENTID_NAME
:
183 clientid
= get_current_username();
187 *path
= talloc_asprintf_append(*path
, "_%s.%ju",
190 DEBUG(1, ("alloc_append_client_suffix "
196 DEBUG(10, ("Leaving with *path '%s'\n", *path
));
201 /* Returns true if the file or directory begins with the appledouble
204 static bool is_apple_double(const char* fname
)
208 DEBUG(10, ("Entering with fname '%s'\n", fname
));
210 if (strnequal(APPLE_DOUBLE_PREFIX
, fname
, APPLE_DOUBLE_PREFIX_LEN
)) {
213 DEBUG(10, ("Leaving with ret '%s'\n",
214 ret
== true ? "true" : "false"));
218 static bool starts_with_media_dir(const char* media_dirname
,
219 size_t media_dirname_len
,
223 const char *path_start
= path
;
225 DEBUG(10, ("Entering with media_dirname '%s' "
226 "path '%s'\n", media_dirname
, path
));
228 /* Sometimes Samba gives us "./OMFI MediaFiles". */
229 if (strnequal(path
, "./", 2)) {
233 if (strnequal(media_dirname
, path_start
, media_dirname_len
)
235 ((path_start
[media_dirname_len
] == '\0') ||
236 (path_start
[media_dirname_len
] == '/'))) {
240 DEBUG(10, ("Leaving with ret '%s'\n",
241 ret
== true ? "true" : "false"));
246 * Returns true if the file or directory referenced by the path is ONE
247 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
250 static bool is_in_media_dir(const char *path
)
252 int transition_count
= 0;
253 const char *path_start
= path
;
255 const char *media_dirname
;
256 size_t media_dirname_len
;
258 DEBUG(10, ("Entering with path'%s' ", path
));
260 /* Sometimes Samba gives us "./OMFI MediaFiles". */
261 if (strnequal(path
, "./", 2)) {
265 if (strnequal(path_start
, AVID_MXF_DIRNAME
, AVID_MXF_DIRNAME_LEN
)) {
266 media_dirname
= AVID_MXF_DIRNAME
;
267 media_dirname_len
= AVID_MXF_DIRNAME_LEN
;
268 } else if (strnequal(path_start
,
269 OMFI_MEDIAFILES_DIRNAME
,
270 OMFI_MEDIAFILES_DIRNAME_LEN
)) {
271 media_dirname
= OMFI_MEDIAFILES_DIRNAME
;
272 media_dirname_len
= OMFI_MEDIAFILES_DIRNAME_LEN
;
277 if (path_start
[media_dirname_len
] == '\0') {
281 p
= path_start
+ media_dirname_len
+ 1;
284 if (*p
== '\0' || *p
== '/') {
285 if (strnequal(p
- 3, "/..", 3)) {
287 } else if ((p
[-1] != '/') || !strnequal(p
- 2, "/.", 2)) {
298 DEBUG(10, ("Going out with transition_count '%i'\n",
300 if (((transition_count
== 1) && (media_dirname
== AVID_MXF_DIRNAME
))
302 ((transition_count
== 0) && (media_dirname
== OMFI_MEDIAFILES_DIRNAME
))) {
309 * Returns true if the file or directory referenced by the path is
310 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
311 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
312 * are assumed to be in the root directory, which is generally a safe
313 * assumption in the fixed-path world of Avid.
315 static bool is_in_media_files(const char *path
)
319 DEBUG(10, ("Entering with path '%s'\n", path
));
321 if (starts_with_media_dir(AVID_MXF_DIRNAME
,
322 AVID_MXF_DIRNAME_LEN
, path
) ||
323 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME
,
324 OMFI_MEDIAFILES_DIRNAME_LEN
, path
)) {
327 DEBUG(10, ("Leaving with ret '%s'\n",
328 ret
== true ? "true" : "false"));
333 /* Add client suffix to "pure-number" path.
335 * Caller must free newPath.
338 * Failure: set errno, newPath NULL, return -1
340 static int alloc_get_client_path(vfs_handle_struct
*handle
,
351 *path_out
= talloc_strdup(ctx
, path_in
);
352 if (*path_out
== NULL
) {
353 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
357 (void)get_digit_group(*path_out
, &number
);
359 digits
= talloc_asprintf(NULL
, "%ju", number
);
360 if (digits
== NULL
) {
361 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
364 digits_len
= strlen(digits
);
366 p
= strstr_m(path_in
, digits
);
369 ((p
[digits_len
] == '\0') || (p
[digits_len
] == '/'))
371 (((p
- path_in
> 0) && (p
[-1] == '/'))
373 (((p
- path_in
) > APPLE_DOUBLE_PREFIX_LEN
)
375 is_apple_double(p
- APPLE_DOUBLE_PREFIX_LEN
)
377 (p
[-(APPLE_DOUBLE_PREFIX_LEN
+ 1)] == '/'))))
379 (*path_out
)[p
- path_in
+ digits_len
] = '\0';
381 status
= alloc_append_client_suffix(handle
, path_out
);
386 *path_out
= talloc_strdup_append(*path_out
, p
+ digits_len
);
387 if (*path_out
== NULL
) {
388 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
394 /* path_out must be freed in caller. */
395 DEBUG(10, ("Result:'%s'\n", *path_out
));
401 * Failure: set errno, return -1
403 static int alloc_get_client_smb_fname(struct vfs_handle_struct
*handle
,
405 const struct smb_filename
*smb_fname
,
406 struct smb_filename
**client_fname
)
410 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
411 smb_fname
->base_name
));
413 *client_fname
= cp_smb_filename(ctx
, smb_fname
);
414 if (*client_fname
== NULL
) {
415 DEBUG(1, ("cp_smb_filename returned NULL\n"));
418 status
= alloc_get_client_path(handle
, ctx
,
419 smb_fname
->base_name
,
420 &(*client_fname
)->base_name
);
425 DEBUG(10, ("Leaving with (*client_fname)->base_name "
426 "'%s'\n", (*client_fname
)->base_name
));
434 * Failure: set errno, return -1
436 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct
*handle
,
439 const char *suffix_number
)
443 DEBUG(10, ("Entering with suffix_number '%s'\n",
446 *path
= talloc_strdup(ctx
, suffix_number
);
448 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
451 status
= alloc_append_client_suffix(handle
, path
);
456 DEBUG(10, ("Leaving with *path '%s'\n", *path
));
461 static int alloc_set_client_dirinfo(vfs_handle_struct
*handle
,
463 struct um_dirinfo_struct
**di_result
)
468 struct um_dirinfo_struct
*dip
;
470 DEBUG(10, ("Entering with fname '%s'\n", fname
));
472 *di_result
= talloc(NULL
, struct um_dirinfo_struct
);
473 if (*di_result
== NULL
) {
478 dip
->dirpath
= talloc_strdup(dip
, fname
);
479 if (dip
->dirpath
== NULL
) {
483 if (!is_in_media_files(fname
)) {
484 dip
->isInMediaFiles
= false;
485 dip
->clientPath
= NULL
;
486 dip
->clientSubDirname
= NULL
;
490 dip
->isInMediaFiles
= true;
492 (void)get_digit_group(fname
, &number
);
493 digits
= talloc_asprintf(talloc_tos(), "%ju", number
);
494 if (digits
== NULL
) {
498 status
= alloc_set_client_dirinfo_path(handle
, dip
,
499 &dip
->clientSubDirname
,
505 status
= alloc_get_client_path(handle
, dip
, fname
,
507 if (status
!= 0 || dip
->clientPath
== NULL
) {
512 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
513 "(*dirInfo)->clientPath '%s'\n",
514 dip
->dirpath
, dip
->clientPath
));
518 DEBUG(1, ("Failing with fname '%s'\n", fname
));
519 TALLOC_FREE(*di_result
);
525 /**********************************************************************
527 **********************************************************************/
531 * Failure: set errno, return -1
533 static int um_statvfs(struct vfs_handle_struct
*handle
,
534 const struct smb_filename
*smb_fname
,
535 struct vfs_statvfs_struct
*statbuf
)
538 struct smb_filename
*client_fname
= NULL
;
540 DEBUG(10, ("Entering with path '%s'\n", smb_fname
->base_name
));
542 if (!is_in_media_files(smb_fname
->base_name
)) {
543 return SMB_VFS_NEXT_STATVFS(handle
, smb_fname
, statbuf
);
546 status
= alloc_get_client_smb_fname(handle
,
554 status
= SMB_VFS_NEXT_STATVFS(handle
, client_fname
, statbuf
);
556 TALLOC_FREE(client_fname
);
557 DEBUG(10, ("Leaving with path '%s'\n", smb_fname
->base_name
));
561 static DIR *um_fdopendir(vfs_handle_struct
*handle
,
566 struct um_dirinfo_struct
*dirInfo
= NULL
;
569 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
570 fsp
->fsp_name
->base_name
));
572 dirstream
= SMB_VFS_NEXT_FDOPENDIR(handle
, fsp
, mask
, attr
);
577 if (alloc_set_client_dirinfo(handle
,
578 fsp
->fsp_name
->base_name
,
583 dirInfo
->dirstream
= dirstream
;
585 if (!dirInfo
->isInMediaFiles
) {
587 * FIXME: this is the original code, something must be
588 * missing here, but what? -slow
594 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
595 "dirInfo->clientPath '%s', "
596 "fsp->fsp_name->st.st_ex_mtime %s",
599 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
))));
600 return (DIR *) dirInfo
;
603 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
604 fsp
->fsp_name
->base_name
));
605 TALLOC_FREE(dirInfo
);
610 * skip own suffixed directory
611 * replace own suffixed directory with non suffixed.
613 * Success: return dirent
614 * End of data: return NULL
615 * Failure: set errno, return NULL
617 static struct dirent
*um_readdir(vfs_handle_struct
*handle
,
618 struct files_struct
*dirfsp
,
620 SMB_STRUCT_STAT
*sbuf
)
622 um_dirinfo_struct
* dirInfo
= (um_dirinfo_struct
*)dirp
;
623 struct dirent
*d
= NULL
;
626 DEBUG(10, ("dirInfo->dirpath '%s', "
627 "dirInfo->clientPath '%s', "
628 "dirInfo->isInMediaFiles '%s', "
629 "dirInfo->clientSubDirname '%s'\n",
632 dirInfo
->isInMediaFiles
? "true" : "false",
633 dirInfo
->clientSubDirname
));
635 if (!dirInfo
->isInMediaFiles
) {
636 return SMB_VFS_NEXT_READDIR(handle
, dirfsp
, dirInfo
->dirstream
, sbuf
);
647 d
= SMB_VFS_NEXT_READDIR(handle
, dirfsp
, dirInfo
->dirstream
, sbuf
);
653 /* ignore apple double prefix for logic below */
654 if (is_apple_double(d
->d_name
)) {
655 dname
= &d
->d_name
[APPLE_DOUBLE_PREFIX_LEN
];
656 isAppleDouble
= true;
659 isAppleDouble
= false;
662 DEBUG(10, ("dname = '%s'\n", dname
));
664 (void)get_digit_group(dname
, &number
);
665 digits
= talloc_asprintf(talloc_tos(), "%ju", number
);
666 if (digits
== NULL
) {
667 DEBUG(1, ("out of memory"));
670 digits_len
= strlen(digits
);
672 if (alloc_set_client_dirinfo_path(handle
,
674 &((dirInfo
)->clientSubDirname
),
680 * If set to "true", vfs shows digits-only
681 * non-suffixed subdirectories. Normally, such
682 * subdirectories can exists only in non-media
683 * directories, so we set it to "false". Otherwise,
684 * if we have such subdirectories (probably created
685 * over not "unityed" connection), it can be little
688 if (strequal(dname
, digits
)) {
690 } else if (strequal(dname
, dirInfo
->clientSubDirname
)) {
692 * Remove suffix of this client's suffixed
696 d
->d_name
[digits_len
+ APPLE_DOUBLE_PREFIX_LEN
] = '\0';
698 d
->d_name
[digits_len
] = '\0';
700 } else if (strnequal(digits
, dname
, digits_len
)) {
702 * Set to false to see another clients subdirectories
708 DEBUG(10, ("Leaving um_readdir\n"));
711 TALLOC_FREE(dirInfo
);
715 static void um_seekdir(vfs_handle_struct
*handle
,
719 DEBUG(10, ("Entering and leaving um_seekdir\n"));
720 SMB_VFS_NEXT_SEEKDIR(handle
,
721 ((um_dirinfo_struct
*)dirp
)->dirstream
, offset
);
724 static long um_telldir(vfs_handle_struct
*handle
,
727 DEBUG(10, ("Entering and leaving um_telldir\n"));
728 return SMB_VFS_NEXT_TELLDIR(handle
,
729 ((um_dirinfo_struct
*)dirp
)->dirstream
);
732 static void um_rewinddir(vfs_handle_struct
*handle
,
735 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
736 SMB_VFS_NEXT_REWINDDIR(handle
,
737 ((um_dirinfo_struct
*)dirp
)->dirstream
);
740 static int um_mkdirat(vfs_handle_struct
*handle
,
741 struct files_struct
*dirfsp
,
742 const struct smb_filename
*smb_fname
,
746 const char *path
= NULL
;
747 struct smb_filename
*client_fname
= NULL
;
748 struct smb_filename
*full_fname
= NULL
;
750 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
753 if (full_fname
== NULL
) {
757 path
= full_fname
->base_name
;
758 DEBUG(10, ("Entering with path '%s'\n", path
));
760 if (!is_in_media_files(path
) || !is_in_media_dir(path
)) {
761 TALLOC_FREE(full_fname
);
762 return SMB_VFS_NEXT_MKDIRAT(handle
,
768 status
= alloc_get_client_smb_fname(handle
,
776 status
= SMB_VFS_NEXT_MKDIRAT(handle
,
777 handle
->conn
->cwd_fsp
,
781 DEBUG(10, ("Leaving with path '%s'\n", path
));
782 TALLOC_FREE(client_fname
);
783 TALLOC_FREE(full_fname
);
787 static int um_closedir(vfs_handle_struct
*handle
,
790 DIR *realdirp
= ((um_dirinfo_struct
*)dirp
)->dirstream
;
794 return SMB_VFS_NEXT_CLOSEDIR(handle
, realdirp
);
797 static int um_openat(struct vfs_handle_struct
*handle
,
798 const struct files_struct
*dirfsp
,
799 const struct smb_filename
*smb_fname
,
800 struct files_struct
*fsp
,
801 const struct vfs_open_how
*how
)
803 struct smb_filename
*client_fname
= NULL
;
806 DBG_DEBUG("Entering with smb_fname->base_name '%s'\n",
807 smb_fname
->base_name
);
809 if (!is_in_media_files(smb_fname
->base_name
)) {
810 return SMB_VFS_NEXT_OPENAT(handle
,
817 if (alloc_get_client_smb_fname(handle
, talloc_tos(),
826 * What about fsp->fsp_name? We also have to get correct stat
827 * info into fsp and smb_fname for DB files, don't we?
830 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
831 "smb_fname->st.st_ex_mtime %s"
832 "fsp->fsp_name->st.st_ex_mtime %s",
833 smb_fname
->base_name
,
834 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
)),
835 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
))));
837 ret
= SMB_VFS_NEXT_OPENAT(handle
,
843 TALLOC_FREE(client_fname
);
844 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
845 smb_fname
->base_name
));
849 static NTSTATUS
um_create_file(vfs_handle_struct
*handle
,
850 struct smb_request
*req
,
851 struct files_struct
*dirfsp
,
852 struct smb_filename
*smb_fname
,
853 uint32_t access_mask
,
854 uint32_t share_access
,
855 uint32_t create_disposition
,
856 uint32_t create_options
,
857 uint32_t file_attributes
,
858 uint32_t oplock_request
,
859 const struct smb2_lease
*lease
,
860 uint64_t allocation_size
,
861 uint32_t private_flags
,
862 struct security_descriptor
*sd
,
863 struct ea_list
*ea_list
,
864 files_struct
**result_fsp
,
866 const struct smb2_create_blobs
*in_context_blobs
,
867 struct smb2_create_blobs
*out_context_blobs
)
870 struct smb_filename
*client_fname
= NULL
;
872 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
873 smb_fname
->base_name
));
875 if (!is_in_media_files(smb_fname
->base_name
)) {
876 return SMB_VFS_NEXT_CREATE_FILE(
898 if (alloc_get_client_smb_fname(handle
, talloc_tos(),
901 status
= map_nt_error_from_unix(errno
);
907 * This only creates files, so we don't have to worry about
908 * our fake directory stat'ing here. But we still need to
909 * route stat calls for DB files properly, right?
911 status
= SMB_VFS_NEXT_CREATE_FILE(
932 TALLOC_FREE(client_fname
);
933 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
934 "smb_fname->st.st_ex_mtime %s"
935 " fsp->fsp_name->st.st_ex_mtime %s",
936 smb_fname
->base_name
,
937 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
)),
938 (*result_fsp
) && VALID_STAT((*result_fsp
)->fsp_name
->st
) ?
939 ctime(&((*result_fsp
)->fsp_name
->st
.st_ex_mtime
.tv_sec
)) :
944 static int um_renameat(vfs_handle_struct
*handle
,
945 files_struct
*srcfsp
,
946 const struct smb_filename
*smb_fname_src
,
947 files_struct
*dstfsp
,
948 const struct smb_filename
*smb_fname_dst
)
951 struct smb_filename
*src_full_fname
= NULL
;
952 struct smb_filename
*dst_full_fname
= NULL
;
953 struct smb_filename
*src_client_fname
= NULL
;
954 struct smb_filename
*dst_client_fname
= NULL
;
956 src_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
959 if (src_full_fname
== NULL
) {
963 dst_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
966 if (dst_full_fname
== NULL
) {
967 TALLOC_FREE(src_full_fname
);
972 DBG_DEBUG( "Entering with "
973 "smb_fname_src->base_name '%s', "
974 "smb_fname_dst->base_name '%s'\n",
975 smb_fname_src
->base_name
,
976 smb_fname_dst
->base_name
);
978 if (!is_in_media_files(src_full_fname
->base_name
)
980 !is_in_media_files(dst_full_fname
->base_name
)) {
981 TALLOC_FREE(src_full_fname
);
982 TALLOC_FREE(dst_full_fname
);
983 return SMB_VFS_NEXT_RENAMEAT(handle
,
990 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
997 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1005 status
= SMB_VFS_NEXT_RENAMEAT(handle
,
1006 handle
->conn
->cwd_fsp
,
1008 handle
->conn
->cwd_fsp
,
1012 TALLOC_FREE(dst_client_fname
);
1013 TALLOC_FREE(src_client_fname
);
1014 TALLOC_FREE(src_full_fname
);
1015 TALLOC_FREE(dst_full_fname
);
1016 DBG_DEBUG( "Leaving with smb_fname_src->base_name '%s',"
1017 " smb_fname_dst->base_name '%s'\n",
1018 smb_fname_src
->base_name
,
1019 smb_fname_dst
->base_name
);
1026 * Failure: set errno, return -1
1028 static int um_stat(vfs_handle_struct
*handle
,
1029 struct smb_filename
*smb_fname
)
1032 struct smb_filename
*client_fname
= NULL
;
1034 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1035 smb_fname
->base_name
));
1037 if (!is_in_media_files(smb_fname
->base_name
)) {
1038 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1041 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1047 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1048 client_fname
->base_name
));
1050 status
= SMB_VFS_NEXT_STAT(handle
, client_fname
);
1056 * Unlike functions with const smb_filename, we have to modify
1057 * smb_fname itself to pass our info back up.
1059 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1060 smb_fname
->base_name
, client_fname
->base_name
));
1061 smb_fname
->st
= client_fname
->st
;
1064 TALLOC_FREE(client_fname
);
1065 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1066 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
))));
1070 static int um_lstat(vfs_handle_struct
*handle
,
1071 struct smb_filename
*smb_fname
)
1074 struct smb_filename
*client_fname
= NULL
;
1076 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1077 smb_fname
->base_name
));
1079 if (!is_in_media_files(smb_fname
->base_name
)) {
1080 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1083 client_fname
= NULL
;
1085 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1091 status
= SMB_VFS_NEXT_LSTAT(handle
, client_fname
);
1096 smb_fname
->st
= client_fname
->st
;
1099 TALLOC_FREE(client_fname
);
1100 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1101 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
))));
1105 static int um_fstat(vfs_handle_struct
*handle
,
1106 files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
1110 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1111 "'%s'\n", fsp_str_dbg(fsp
)));
1113 status
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1118 if ((fsp
->fsp_name
== NULL
) ||
1119 !is_in_media_files(fsp
->fsp_name
->base_name
)) {
1123 status
= um_stat(handle
, fsp
->fsp_name
);
1128 *sbuf
= fsp
->fsp_name
->st
;
1131 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1132 fsp
->fsp_name
!= NULL
?
1133 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
)) : "0"));
1137 static int um_unlinkat(vfs_handle_struct
*handle
,
1138 struct files_struct
*dirfsp
,
1139 const struct smb_filename
*smb_fname
,
1143 struct smb_filename
*full_fname
= NULL
;
1144 struct smb_filename
*client_fname
= NULL
;
1146 DEBUG(10, ("Entering um_unlinkat\n"));
1148 if (!is_in_media_files(smb_fname
->base_name
)) {
1149 return SMB_VFS_NEXT_UNLINKAT(handle
,
1155 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1158 if (full_fname
== NULL
) {
1162 ret
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1169 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
1170 dirfsp
->conn
->cwd_fsp
,
1175 TALLOC_FREE(full_fname
);
1176 TALLOC_FREE(client_fname
);
1180 static int um_lchown(vfs_handle_struct
*handle
,
1181 const struct smb_filename
*smb_fname
,
1186 struct smb_filename
*client_fname
= NULL
;
1188 DEBUG(10, ("Entering um_lchown\n"));
1189 if (!is_in_media_files(smb_fname
->base_name
)) {
1190 return SMB_VFS_NEXT_LCHOWN(handle
, smb_fname
, uid
, gid
);
1193 status
= alloc_get_client_smb_fname(handle
,
1201 status
= SMB_VFS_NEXT_LCHOWN(handle
, client_fname
, uid
, gid
);
1204 TALLOC_FREE(client_fname
);
1208 static int um_chdir(vfs_handle_struct
*handle
,
1209 const struct smb_filename
*smb_fname
)
1212 struct smb_filename
*client_fname
= NULL
;
1214 DEBUG(10, ("Entering um_chdir\n"));
1216 if (!is_in_media_files(smb_fname
->base_name
)) {
1217 return SMB_VFS_NEXT_CHDIR(handle
, smb_fname
);
1220 status
= alloc_get_client_smb_fname(handle
,
1228 status
= SMB_VFS_NEXT_CHDIR(handle
, client_fname
);
1231 TALLOC_FREE(client_fname
);
1235 static int um_symlinkat(vfs_handle_struct
*handle
,
1236 const struct smb_filename
*link_contents
,
1237 struct files_struct
*dirfsp
,
1238 const struct smb_filename
*new_smb_fname
)
1241 struct smb_filename
*new_link_target
= NULL
;
1242 struct smb_filename
*new_client_fname
= NULL
;
1243 struct smb_filename
*full_fname
= NULL
;
1245 DEBUG(10, ("Entering um_symlinkat\n"));
1247 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1250 if (full_fname
== NULL
) {
1254 if (!is_in_media_files(link_contents
->base_name
) &&
1255 !is_in_media_files(full_fname
->base_name
)) {
1256 TALLOC_FREE(full_fname
);
1257 return SMB_VFS_NEXT_SYMLINKAT(handle
,
1263 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1264 link_contents
, &new_link_target
);
1268 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1269 full_fname
, &new_client_fname
);
1274 status
= SMB_VFS_NEXT_SYMLINKAT(handle
,
1276 handle
->conn
->cwd_fsp
,
1280 TALLOC_FREE(new_link_target
);
1281 TALLOC_FREE(new_client_fname
);
1282 TALLOC_FREE(full_fname
);
1286 static int um_readlinkat(vfs_handle_struct
*handle
,
1287 const struct files_struct
*dirfsp
,
1288 const struct smb_filename
*smb_fname
,
1293 struct smb_filename
*client_fname
= NULL
;
1294 struct smb_filename
*full_fname
= NULL
;
1296 DEBUG(10, ("Entering um_readlinkat\n"));
1298 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1301 if (full_fname
== NULL
) {
1305 if (!is_in_media_files(full_fname
->base_name
)) {
1306 TALLOC_FREE(full_fname
);
1307 return SMB_VFS_NEXT_READLINKAT(handle
,
1314 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1315 full_fname
, &client_fname
);
1320 status
= SMB_VFS_NEXT_READLINKAT(handle
,
1321 handle
->conn
->cwd_fsp
,
1327 TALLOC_FREE(full_fname
);
1328 TALLOC_FREE(client_fname
);
1332 static int um_linkat(vfs_handle_struct
*handle
,
1333 files_struct
*srcfsp
,
1334 const struct smb_filename
*old_smb_fname
,
1335 files_struct
*dstfsp
,
1336 const struct smb_filename
*new_smb_fname
,
1340 struct smb_filename
*old_full_fname
= NULL
;
1341 struct smb_filename
*new_full_fname
= NULL
;
1342 struct smb_filename
*old_client_fname
= NULL
;
1343 struct smb_filename
*new_client_fname
= NULL
;
1345 old_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1348 if (old_full_fname
== NULL
) {
1351 new_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1354 if (new_full_fname
== NULL
) {
1355 TALLOC_FREE(old_full_fname
);
1359 DEBUG(10, ("Entering um_linkat\n"));
1360 if (!is_in_media_files(old_full_fname
->base_name
) &&
1361 !is_in_media_files(new_full_fname
->base_name
)) {
1362 TALLOC_FREE(old_full_fname
);
1363 TALLOC_FREE(new_full_fname
);
1364 return SMB_VFS_NEXT_LINKAT(handle
,
1372 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1373 old_full_fname
, &old_client_fname
);
1377 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1378 new_full_fname
, &new_client_fname
);
1383 status
= SMB_VFS_NEXT_LINKAT(handle
,
1384 handle
->conn
->cwd_fsp
,
1386 handle
->conn
->cwd_fsp
,
1391 TALLOC_FREE(old_full_fname
);
1392 TALLOC_FREE(new_full_fname
);
1393 TALLOC_FREE(old_client_fname
);
1394 TALLOC_FREE(new_client_fname
);
1398 static int um_mknodat(vfs_handle_struct
*handle
,
1399 files_struct
*dirfsp
,
1400 const struct smb_filename
*smb_fname
,
1405 struct smb_filename
*client_fname
= NULL
;
1406 struct smb_filename
*full_fname
= NULL
;
1408 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1411 if (full_fname
== NULL
) {
1415 DEBUG(10, ("Entering um_mknodat\n"));
1416 if (!is_in_media_files(full_fname
->base_name
)) {
1417 TALLOC_FREE(full_fname
);
1418 return SMB_VFS_NEXT_MKNODAT(handle
,
1425 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1426 full_fname
, &client_fname
);
1431 status
= SMB_VFS_NEXT_MKNODAT(handle
,
1432 handle
->conn
->cwd_fsp
,
1438 TALLOC_FREE(client_fname
);
1439 TALLOC_FREE(full_fname
);
1443 static struct smb_filename
*um_realpath(vfs_handle_struct
*handle
,
1445 const struct smb_filename
*smb_fname
)
1447 struct smb_filename
*client_fname
= NULL
;
1448 struct smb_filename
*result_fname
= NULL
;
1451 DEBUG(10, ("Entering um_realpath\n"));
1453 if (!is_in_media_files(smb_fname
->base_name
)) {
1454 return SMB_VFS_NEXT_REALPATH(handle
, ctx
, smb_fname
);
1457 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1458 smb_fname
, &client_fname
);
1463 result_fname
= SMB_VFS_NEXT_REALPATH(handle
, ctx
, client_fname
);
1466 TALLOC_FREE(client_fname
);
1467 return result_fname
;
1470 static int um_connect(vfs_handle_struct
*handle
,
1471 const char *service
,
1475 struct um_config_data
*config
;
1478 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1483 config
= talloc_zero(handle
->conn
, struct um_config_data
);
1485 DEBUG(1, ("talloc_zero() failed\n"));
1490 enumval
= lp_parm_enum(SNUM(handle
->conn
), UM_PARAM_TYPE_NAME
,
1491 "clientid", um_clientid
, UM_CLIENTID_NAME
);
1492 if (enumval
== -1) {
1493 DEBUG(1, ("value for %s: type unknown\n",
1494 UM_PARAM_TYPE_NAME
));
1497 config
->clientid
= (enum um_clientid
)enumval
;
1499 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1500 NULL
, struct um_config_data
,
1506 /* VFS operations structure */
1508 static struct vfs_fn_pointers vfs_um_fns
= {
1509 .connect_fn
= um_connect
,
1511 /* Disk operations */
1513 .statvfs_fn
= um_statvfs
,
1515 /* Directory operations */
1517 .fdopendir_fn
= um_fdopendir
,
1518 .readdir_fn
= um_readdir
,
1519 .seekdir_fn
= um_seekdir
,
1520 .telldir_fn
= um_telldir
,
1521 .rewind_dir_fn
= um_rewinddir
,
1522 .mkdirat_fn
= um_mkdirat
,
1523 .closedir_fn
= um_closedir
,
1525 /* File operations */
1527 .openat_fn
= um_openat
,
1528 .create_file_fn
= um_create_file
,
1529 .renameat_fn
= um_renameat
,
1531 .lstat_fn
= um_lstat
,
1532 .fstat_fn
= um_fstat
,
1533 .unlinkat_fn
= um_unlinkat
,
1534 .lchown_fn
= um_lchown
,
1535 .chdir_fn
= um_chdir
,
1536 .symlinkat_fn
= um_symlinkat
,
1537 .readlinkat_fn
= um_readlinkat
,
1538 .linkat_fn
= um_linkat
,
1539 .mknodat_fn
= um_mknodat
,
1540 .realpath_fn
= um_realpath
,
1542 /* EA operations. */
1543 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
1544 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
1548 NTSTATUS
vfs_unityed_media_init(TALLOC_CTX
*ctx
)
1550 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1551 "unityed_media", &vfs_um_fns
);
1552 if (!NT_STATUS_IS_OK(ret
)) {
1556 vfs_um_debug_level
= debug_add_class("unityed_media");
1558 if (vfs_um_debug_level
== -1) {
1559 vfs_um_debug_level
= DBGC_VFS
;
1560 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1561 "debugging class.\n"));