2 * Samba VFS module supporting multiple AVID clients sharing media.
4 * Copyright (C) 2005 Philip de Nier <philipn@users.sourceforge.net>
5 * Copyright (C) 2012 Andrew Klaassen <clawsoon@yahoo.com>
6 * Copyright (C) 2013 Milos Lukacek
7 * Copyright (C) 2013 Ralph Boehme <slow@samba.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 * Unityed Media is a Samba VFS module that allows multiple AVID
27 * clients to share media.
29 * Add this module to the vfs objects option in your Samba share
35 * vfs objects = unityed_media
38 * It is recommended that you separate out Samba shares for Mac
39 * and Windows clients, and add the following options to the shares
40 * for Windows clients (NOTE: replace @ with *):
42 * veto files = /.DS_Store/._@/.Trash@/.Spotlight@/.hidden/.hotfiles@/.vol/
43 * delete veto files = yes
45 * This prevents hidden files from Mac clients interfering with Windows
46 * clients. If you find any more problem hidden files then add them to
50 * This module is designed to work with AVID editing applications that
51 * look in the Avid MediaFiles or OMFI MediaFiles directory for media.
52 * It is not designed to work as expected in all circumstances for
58 #include "system/filesys.h"
59 #include "smbd/smbd.h"
60 #include "../smbd/globals.h"
62 #include "../lib/tsocket/tsocket.h"
65 #define UM_PARAM_TYPE_NAME "unityed_media"
67 static const char *AVID_MXF_DIRNAME
= "Avid MediaFiles/MXF";
68 static const size_t AVID_MXF_DIRNAME_LEN
= 19;
69 static const char *OMFI_MEDIAFILES_DIRNAME
= "OMFI MediaFiles";
70 static const size_t OMFI_MEDIAFILES_DIRNAME_LEN
= 15;
71 static const char *APPLE_DOUBLE_PREFIX
= "._";
72 static const size_t APPLE_DOUBLE_PREFIX_LEN
= 2;
73 static int vfs_um_debug_level
= DBGC_VFS
;
75 enum um_clientid
{UM_CLIENTID_NAME
, UM_CLIENTID_IP
, UM_CLIENTID_HOSTNAME
};
77 struct um_config_data
{
78 enum um_clientid clientid
;
81 static const struct enum_list um_clientid
[] = {
82 {UM_CLIENTID_NAME
, "user"},
83 {UM_CLIENTID_IP
, "ip"},
84 {UM_CLIENTID_HOSTNAME
, "hostname"},
88 /* supplements the directory list stream */
89 typedef struct um_dirinfo_struct
{
94 char *clientSubDirname
;
98 * Returns true and first group of digits in path, false and 0 otherwise
100 static bool get_digit_group(const char *path
, uintmax_t *digit
)
102 const char *p
= path
;
107 DEBUG(10, ("get_digit_group entering with path '%s'\n",
111 * Delibiretly initialize to 0 because callers use this result
112 * even though the string doesn't contain any number and we
118 cp
= next_codepoint(p
, &size
);
122 if ((size
== 1) && (isdigit(cp
))) {
123 *digit
= (uintmax_t)smb_strtoul(p
,
131 DEBUG(10, ("num_suffix = '%ju'\n",
141 /* Add "_<remote_name>.<number>" suffix to path or filename.
144 * Failure: set errno, path NULL, return -1
147 static int alloc_append_client_suffix(vfs_handle_struct
*handle
,
152 const char *clientid
;
153 struct um_config_data
*config
;
155 DEBUG(10, ("Entering with path '%s'\n", *path
));
157 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
158 struct um_config_data
,
161 (void)get_digit_group(*path
, &number
);
163 switch (config
->clientid
) {
166 clientid
= tsocket_address_inet_addr_string(
167 handle
->conn
->sconn
->remote_address
, talloc_tos());
168 if (clientid
== NULL
) {
175 case UM_CLIENTID_HOSTNAME
:
176 clientid
= get_remote_machine_name();
179 case UM_CLIENTID_NAME
:
181 clientid
= get_current_username();
185 *path
= talloc_asprintf_append(*path
, "_%s.%ju",
188 DEBUG(1, ("alloc_append_client_suffix "
194 DEBUG(10, ("Leaving with *path '%s'\n", *path
));
199 /* Returns true if the file or directory begins with the appledouble
202 static bool is_apple_double(const char* fname
)
206 DEBUG(10, ("Entering with fname '%s'\n", fname
));
208 if (strnequal(APPLE_DOUBLE_PREFIX
, fname
, APPLE_DOUBLE_PREFIX_LEN
)) {
211 DEBUG(10, ("Leaving with ret '%s'\n",
212 ret
== true ? "true" : "false"));
216 static bool starts_with_media_dir(const char* media_dirname
,
217 size_t media_dirname_len
,
221 const char *path_start
= path
;
223 DEBUG(10, ("Entering with media_dirname '%s' "
224 "path '%s'\n", media_dirname
, path
));
226 /* Sometimes Samba gives us "./OMFI MediaFiles". */
227 if (strnequal(path
, "./", 2)) {
231 if (strnequal(media_dirname
, path_start
, media_dirname_len
)
233 ((path_start
[media_dirname_len
] == '\0') ||
234 (path_start
[media_dirname_len
] == '/'))) {
238 DEBUG(10, ("Leaving with ret '%s'\n",
239 ret
== true ? "true" : "false"));
244 * Returns true if the file or directory referenced by the path is ONE
245 * LEVEL below the AVID_MXF_DIRNAME or OMFI_MEDIAFILES_DIRNAME
248 static bool is_in_media_dir(const char *path
)
250 int transition_count
= 0;
251 const char *path_start
= path
;
253 const char *media_dirname
;
254 size_t media_dirname_len
;
256 DEBUG(10, ("Entering with path'%s' ", path
));
258 /* Sometimes Samba gives us "./OMFI MediaFiles". */
259 if (strnequal(path
, "./", 2)) {
263 if (strnequal(path_start
, AVID_MXF_DIRNAME
, AVID_MXF_DIRNAME_LEN
)) {
264 media_dirname
= AVID_MXF_DIRNAME
;
265 media_dirname_len
= AVID_MXF_DIRNAME_LEN
;
266 } else if (strnequal(path_start
,
267 OMFI_MEDIAFILES_DIRNAME
,
268 OMFI_MEDIAFILES_DIRNAME_LEN
)) {
269 media_dirname
= OMFI_MEDIAFILES_DIRNAME
;
270 media_dirname_len
= OMFI_MEDIAFILES_DIRNAME_LEN
;
275 if (path_start
[media_dirname_len
] == '\0') {
279 p
= path_start
+ media_dirname_len
+ 1;
282 if (*p
== '\0' || *p
== '/') {
283 if (strnequal(p
- 3, "/..", 3)) {
285 } else if ((p
[-1] != '/') || !strnequal(p
- 2, "/.", 2)) {
296 DEBUG(10, ("Going out with transition_count '%i'\n",
298 if (((transition_count
== 1) && (media_dirname
== AVID_MXF_DIRNAME
))
300 ((transition_count
== 0) && (media_dirname
== OMFI_MEDIAFILES_DIRNAME
))) {
307 * Returns true if the file or directory referenced by the path is
308 * below the AVID_MEDIAFILES_DIRNAME or OMFI_MEDIAFILES_DIRNAME
309 * directory The AVID_MEDIAFILES_DIRNAME and OMFI_MEDIAFILES_DIRNAME
310 * are assumed to be in the root directory, which is generally a safe
311 * assumption in the fixed-path world of Avid.
313 static bool is_in_media_files(const char *path
)
317 DEBUG(10, ("Entering with path '%s'\n", path
));
319 if (starts_with_media_dir(AVID_MXF_DIRNAME
,
320 AVID_MXF_DIRNAME_LEN
, path
) ||
321 starts_with_media_dir(OMFI_MEDIAFILES_DIRNAME
,
322 OMFI_MEDIAFILES_DIRNAME_LEN
, path
)) {
325 DEBUG(10, ("Leaving with ret '%s'\n",
326 ret
== true ? "true" : "false"));
331 /* Add client suffix to "pure-number" path.
333 * Caller must free newPath.
336 * Failure: set errno, newPath NULL, return -1
338 static int alloc_get_client_path(vfs_handle_struct
*handle
,
349 *path_out
= talloc_strdup(ctx
, path_in
);
350 if (*path_out
== NULL
) {
351 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
355 (void)get_digit_group(*path_out
, &number
);
357 digits
= talloc_asprintf(NULL
, "%ju", number
);
358 if (digits
== NULL
) {
359 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
362 digits_len
= strlen(digits
);
364 p
= strstr_m(path_in
, digits
);
367 ((p
[digits_len
] == '\0') || (p
[digits_len
] == '/'))
369 (((p
- path_in
> 0) && (p
[-1] == '/'))
371 (((p
- path_in
) > APPLE_DOUBLE_PREFIX_LEN
)
373 is_apple_double(p
- APPLE_DOUBLE_PREFIX_LEN
)
375 (p
[-(APPLE_DOUBLE_PREFIX_LEN
+ 1)] == '/'))))
377 (*path_out
)[p
- path_in
+ digits_len
] = '\0';
379 status
= alloc_append_client_suffix(handle
, path_out
);
384 *path_out
= talloc_strdup_append(*path_out
, p
+ digits_len
);
385 if (*path_out
== NULL
) {
386 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
392 /* path_out must be freed in caller. */
393 DEBUG(10, ("Result:'%s'\n", *path_out
));
399 * Failure: set errno, return -1
401 static int alloc_get_client_smb_fname(struct vfs_handle_struct
*handle
,
403 const struct smb_filename
*smb_fname
,
404 struct smb_filename
**client_fname
)
408 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
409 smb_fname
->base_name
));
411 *client_fname
= cp_smb_filename(ctx
, smb_fname
);
412 if (*client_fname
== NULL
) {
413 DEBUG(1, ("cp_smb_filename returned NULL\n"));
416 status
= alloc_get_client_path(handle
, ctx
,
417 smb_fname
->base_name
,
418 &(*client_fname
)->base_name
);
423 DEBUG(10, ("Leaving with (*client_fname)->base_name "
424 "'%s'\n", (*client_fname
)->base_name
));
432 * Failure: set errno, return -1
434 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct
*handle
,
437 const char *suffix_number
)
441 DEBUG(10, ("Entering with suffix_number '%s'\n",
444 *path
= talloc_strdup(ctx
, suffix_number
);
446 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
449 status
= alloc_append_client_suffix(handle
, path
);
454 DEBUG(10, ("Leaving with *path '%s'\n", *path
));
459 static int alloc_set_client_dirinfo(vfs_handle_struct
*handle
,
461 struct um_dirinfo_struct
**di_result
)
466 struct um_dirinfo_struct
*dip
;
468 DEBUG(10, ("Entering with fname '%s'\n", fname
));
470 *di_result
= talloc(NULL
, struct um_dirinfo_struct
);
471 if (*di_result
== NULL
) {
476 dip
->dirpath
= talloc_strdup(dip
, fname
);
477 if (dip
->dirpath
== NULL
) {
481 if (!is_in_media_files(fname
)) {
482 dip
->isInMediaFiles
= false;
483 dip
->clientPath
= NULL
;
484 dip
->clientSubDirname
= NULL
;
488 dip
->isInMediaFiles
= true;
490 (void)get_digit_group(fname
, &number
);
491 digits
= talloc_asprintf(talloc_tos(), "%ju", number
);
492 if (digits
== NULL
) {
496 status
= alloc_set_client_dirinfo_path(handle
, dip
,
497 &dip
->clientSubDirname
,
503 status
= alloc_get_client_path(handle
, dip
, fname
,
505 if (status
!= 0 || dip
->clientPath
== NULL
) {
510 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
511 "(*dirInfo)->clientPath '%s'\n",
512 dip
->dirpath
, dip
->clientPath
));
516 DEBUG(1, ("Failing with fname '%s'\n", fname
));
517 TALLOC_FREE(*di_result
);
523 /**********************************************************************
525 **********************************************************************/
529 * Failure: set errno, return -1
531 static int um_statvfs(struct vfs_handle_struct
*handle
,
532 const struct smb_filename
*smb_fname
,
533 struct vfs_statvfs_struct
*statbuf
)
536 struct smb_filename
*client_fname
= NULL
;
538 DEBUG(10, ("Entering with path '%s'\n", smb_fname
->base_name
));
540 if (!is_in_media_files(smb_fname
->base_name
)) {
541 return SMB_VFS_NEXT_STATVFS(handle
, smb_fname
, statbuf
);
544 status
= alloc_get_client_smb_fname(handle
,
552 status
= SMB_VFS_NEXT_STATVFS(handle
, client_fname
, statbuf
);
554 TALLOC_FREE(client_fname
);
555 DEBUG(10, ("Leaving with path '%s'\n", smb_fname
->base_name
));
559 static DIR *um_fdopendir(vfs_handle_struct
*handle
,
564 struct um_dirinfo_struct
*dirInfo
= NULL
;
567 DEBUG(10, ("Entering with fsp->fsp_name->base_name '%s'\n",
568 fsp
->fsp_name
->base_name
));
570 dirstream
= SMB_VFS_NEXT_FDOPENDIR(handle
, fsp
, mask
, attr
);
575 if (alloc_set_client_dirinfo(handle
,
576 fsp
->fsp_name
->base_name
,
581 dirInfo
->dirstream
= dirstream
;
583 if (!dirInfo
->isInMediaFiles
) {
585 * FIXME: this is the original code, something must be
586 * missing here, but what? -slow
592 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
593 "dirInfo->clientPath '%s', "
594 "fsp->fsp_name->st.st_ex_mtime %s",
597 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
))));
598 return (DIR *) dirInfo
;
601 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
602 fsp
->fsp_name
->base_name
));
603 TALLOC_FREE(dirInfo
);
608 * skip own suffixed directory
609 * replace own suffixed directory with non suffixed.
611 * Success: return dirent
612 * End of data: return NULL
613 * Failure: set errno, return NULL
615 static struct dirent
*um_readdir(vfs_handle_struct
*handle
,
617 SMB_STRUCT_STAT
*sbuf
)
619 um_dirinfo_struct
* dirInfo
= (um_dirinfo_struct
*)dirp
;
620 struct dirent
*d
= NULL
;
623 DEBUG(10, ("dirInfo->dirpath '%s', "
624 "dirInfo->clientPath '%s', "
625 "dirInfo->isInMediaFiles '%s', "
626 "dirInfo->clientSubDirname '%s'\n",
629 dirInfo
->isInMediaFiles
? "true" : "false",
630 dirInfo
->clientSubDirname
));
632 if (!dirInfo
->isInMediaFiles
) {
633 return SMB_VFS_NEXT_READDIR(handle
, dirInfo
->dirstream
, sbuf
);
644 d
= SMB_VFS_NEXT_READDIR(handle
, dirInfo
->dirstream
, sbuf
);
650 /* ignore apple double prefix for logic below */
651 if (is_apple_double(d
->d_name
)) {
652 dname
= &d
->d_name
[APPLE_DOUBLE_PREFIX_LEN
];
653 isAppleDouble
= true;
656 isAppleDouble
= false;
659 DEBUG(10, ("dname = '%s'\n", dname
));
661 (void)get_digit_group(dname
, &number
);
662 digits
= talloc_asprintf(talloc_tos(), "%ju", number
);
663 if (digits
== NULL
) {
664 DEBUG(1, ("out of memory"));
667 digits_len
= strlen(digits
);
669 if (alloc_set_client_dirinfo_path(handle
,
671 &((dirInfo
)->clientSubDirname
),
677 * If set to "true", vfs shows digits-only
678 * non-suffixed subdirectories. Normally, such
679 * subdirectories can exists only in non-media
680 * directories, so we set it to "false". Otherwise,
681 * if we have such subdirectories (probably created
682 * over not "unityed" connection), it can be little
685 if (strequal(dname
, digits
)) {
687 } else if (strequal(dname
, dirInfo
->clientSubDirname
)) {
689 * Remove suffix of this client's suffixed
693 d
->d_name
[digits_len
+ APPLE_DOUBLE_PREFIX_LEN
] = '\0';
695 d
->d_name
[digits_len
] = '\0';
697 } else if (strnequal(digits
, dname
, digits_len
)) {
699 * Set to false to see another clients subdirectories
705 DEBUG(10, ("Leaving um_readdir\n"));
708 TALLOC_FREE(dirInfo
);
712 static void um_seekdir(vfs_handle_struct
*handle
,
716 DEBUG(10, ("Entering and leaving um_seekdir\n"));
717 SMB_VFS_NEXT_SEEKDIR(handle
,
718 ((um_dirinfo_struct
*)dirp
)->dirstream
, offset
);
721 static long um_telldir(vfs_handle_struct
*handle
,
724 DEBUG(10, ("Entering and leaving um_telldir\n"));
725 return SMB_VFS_NEXT_TELLDIR(handle
,
726 ((um_dirinfo_struct
*)dirp
)->dirstream
);
729 static void um_rewinddir(vfs_handle_struct
*handle
,
732 DEBUG(10, ("Entering and leaving um_rewinddir\n"));
733 SMB_VFS_NEXT_REWINDDIR(handle
,
734 ((um_dirinfo_struct
*)dirp
)->dirstream
);
737 static int um_mkdirat(vfs_handle_struct
*handle
,
738 struct files_struct
*dirfsp
,
739 const struct smb_filename
*smb_fname
,
743 const char *path
= smb_fname
->base_name
;
744 struct smb_filename
*client_fname
= NULL
;
746 DEBUG(10, ("Entering with path '%s'\n", path
));
748 if (!is_in_media_files(path
) || !is_in_media_dir(path
)) {
749 return SMB_VFS_NEXT_MKDIRAT(handle
,
755 status
= alloc_get_client_smb_fname(handle
,
763 status
= SMB_VFS_NEXT_MKDIRAT(handle
,
768 TALLOC_FREE(client_fname
);
769 DEBUG(10, ("Leaving with path '%s'\n", path
));
773 static int um_closedir(vfs_handle_struct
*handle
,
776 DIR *realdirp
= ((um_dirinfo_struct
*)dirp
)->dirstream
;
780 return SMB_VFS_NEXT_CLOSEDIR(handle
, realdirp
);
783 static int um_openat(struct vfs_handle_struct
*handle
,
784 const struct files_struct
*dirfsp
,
785 const struct smb_filename
*smb_fname
,
786 struct files_struct
*fsp
,
790 struct smb_filename
*client_fname
= NULL
;
793 DBG_DEBUG("Entering with smb_fname->base_name '%s'\n",
794 smb_fname
->base_name
);
796 if (!is_in_media_files(smb_fname
->base_name
)) {
797 return SMB_VFS_NEXT_OPENAT(handle
,
805 if (alloc_get_client_smb_fname(handle
, talloc_tos(),
814 * What about fsp->fsp_name? We also have to get correct stat
815 * info into fsp and smb_fname for DB files, don't we?
818 DEBUG(10, ("Leaving with smb_fname->base_name '%s' "
819 "smb_fname->st.st_ex_mtime %s"
820 "fsp->fsp_name->st.st_ex_mtime %s",
821 smb_fname
->base_name
,
822 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
)),
823 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
))));
825 ret
= SMB_VFS_NEXT_OPENAT(handle
,
832 TALLOC_FREE(client_fname
);
833 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
834 smb_fname
->base_name
));
838 static NTSTATUS
um_create_file(vfs_handle_struct
*handle
,
839 struct smb_request
*req
,
840 struct files_struct
**dirfsp
,
841 struct smb_filename
*smb_fname
,
842 uint32_t access_mask
,
843 uint32_t share_access
,
844 uint32_t create_disposition
,
845 uint32_t create_options
,
846 uint32_t file_attributes
,
847 uint32_t oplock_request
,
848 const struct smb2_lease
*lease
,
849 uint64_t allocation_size
,
850 uint32_t private_flags
,
851 struct security_descriptor
*sd
,
852 struct ea_list
*ea_list
,
853 files_struct
**result_fsp
,
855 const struct smb2_create_blobs
*in_context_blobs
,
856 struct smb2_create_blobs
*out_context_blobs
)
859 struct smb_filename
*client_fname
= NULL
;
861 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
862 smb_fname
->base_name
));
864 if (!is_in_media_files(smb_fname
->base_name
)) {
865 return SMB_VFS_NEXT_CREATE_FILE(
887 if (alloc_get_client_smb_fname(handle
, talloc_tos(),
890 status
= map_nt_error_from_unix(errno
);
896 * This only creates files, so we don't have to worry about
897 * our fake directory stat'ing here. But we still need to
898 * route stat calls for DB files properly, right?
900 status
= SMB_VFS_NEXT_CREATE_FILE(
921 TALLOC_FREE(client_fname
);
922 DEBUG(10, ("Leaving with smb_fname->base_name '%s'"
923 "smb_fname->st.st_ex_mtime %s"
924 " fsp->fsp_name->st.st_ex_mtime %s",
925 smb_fname
->base_name
,
926 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
)),
927 (*result_fsp
) && VALID_STAT((*result_fsp
)->fsp_name
->st
) ?
928 ctime(&((*result_fsp
)->fsp_name
->st
.st_ex_mtime
.tv_sec
)) :
933 static int um_renameat(vfs_handle_struct
*handle
,
934 files_struct
*srcfsp
,
935 const struct smb_filename
*smb_fname_src
,
936 files_struct
*dstfsp
,
937 const struct smb_filename
*smb_fname_dst
)
940 struct smb_filename
*src_client_fname
= NULL
;
941 struct smb_filename
*dst_client_fname
= NULL
;
943 DEBUG(10, ("Entering with "
944 "smb_fname_src->base_name '%s', "
945 "smb_fname_dst->base_name '%s'\n",
946 smb_fname_src
->base_name
,
947 smb_fname_dst
->base_name
));
949 if (!is_in_media_files(smb_fname_src
->base_name
)
951 !is_in_media_files(smb_fname_dst
->base_name
)) {
952 return SMB_VFS_NEXT_RENAMEAT(handle
,
959 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
966 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
974 status
= SMB_VFS_NEXT_RENAMEAT(handle
,
981 TALLOC_FREE(dst_client_fname
);
982 TALLOC_FREE(src_client_fname
);
983 DEBUG(10, ("Leaving with smb_fname_src->base_name '%s',"
984 " smb_fname_dst->base_name '%s'\n",
985 smb_fname_src
->base_name
,
986 smb_fname_dst
->base_name
));
993 * Failure: set errno, return -1
995 static int um_stat(vfs_handle_struct
*handle
,
996 struct smb_filename
*smb_fname
)
999 struct smb_filename
*client_fname
= NULL
;
1001 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1002 smb_fname
->base_name
));
1004 if (!is_in_media_files(smb_fname
->base_name
)) {
1005 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1008 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1014 DEBUG(10, ("Stat'ing client_fname->base_name '%s'\n",
1015 client_fname
->base_name
));
1017 status
= SMB_VFS_NEXT_STAT(handle
, client_fname
);
1023 * Unlike functions with const smb_filename, we have to modify
1024 * smb_fname itself to pass our info back up.
1026 DEBUG(10, ("Setting smb_fname '%s' stat from client_fname '%s'\n",
1027 smb_fname
->base_name
, client_fname
->base_name
));
1028 smb_fname
->st
= client_fname
->st
;
1031 TALLOC_FREE(client_fname
);
1032 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1033 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
))));
1037 static int um_lstat(vfs_handle_struct
*handle
,
1038 struct smb_filename
*smb_fname
)
1041 struct smb_filename
*client_fname
= NULL
;
1043 DEBUG(10, ("Entering with smb_fname->base_name '%s'\n",
1044 smb_fname
->base_name
));
1046 if (!is_in_media_files(smb_fname
->base_name
)) {
1047 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1050 client_fname
= NULL
;
1052 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1058 status
= SMB_VFS_NEXT_LSTAT(handle
, client_fname
);
1063 smb_fname
->st
= client_fname
->st
;
1066 TALLOC_FREE(client_fname
);
1067 DEBUG(10, ("Leaving with smb_fname->st.st_ex_mtime %s",
1068 ctime(&(smb_fname
->st
.st_ex_mtime
.tv_sec
))));
1072 static int um_fstat(vfs_handle_struct
*handle
,
1073 files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
1077 DEBUG(10, ("Entering with fsp->fsp_name->base_name "
1078 "'%s'\n", fsp_str_dbg(fsp
)));
1080 status
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1085 if ((fsp
->fsp_name
== NULL
) ||
1086 !is_in_media_files(fsp
->fsp_name
->base_name
)) {
1090 status
= um_stat(handle
, fsp
->fsp_name
);
1095 *sbuf
= fsp
->fsp_name
->st
;
1098 DEBUG(10, ("Leaving with fsp->fsp_name->st.st_ex_mtime %s\n",
1099 fsp
->fsp_name
!= NULL
?
1100 ctime(&(fsp
->fsp_name
->st
.st_ex_mtime
.tv_sec
)) : "0"));
1104 static int um_unlinkat(vfs_handle_struct
*handle
,
1105 struct files_struct
*dirfsp
,
1106 const struct smb_filename
*smb_fname
,
1110 struct smb_filename
*client_fname
= NULL
;
1112 DEBUG(10, ("Entering um_unlinkat\n"));
1114 if (!is_in_media_files(smb_fname
->base_name
)) {
1115 return SMB_VFS_NEXT_UNLINKAT(handle
,
1121 ret
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1128 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
1134 TALLOC_FREE(client_fname
);
1138 static int um_chmod(vfs_handle_struct
*handle
,
1139 const struct smb_filename
*smb_fname
,
1143 struct smb_filename
*client_fname
= NULL
;
1145 DEBUG(10, ("Entering um_chmod\n"));
1147 if (!is_in_media_files(smb_fname
->base_name
)) {
1148 return SMB_VFS_NEXT_CHMOD(handle
, smb_fname
, mode
);
1151 status
= alloc_get_client_smb_fname(handle
,
1159 status
= SMB_VFS_NEXT_CHMOD(handle
, client_fname
, mode
);
1162 TALLOC_FREE(client_fname
);
1166 static int um_lchown(vfs_handle_struct
*handle
,
1167 const struct smb_filename
*smb_fname
,
1172 struct smb_filename
*client_fname
= NULL
;
1174 DEBUG(10, ("Entering um_lchown\n"));
1175 if (!is_in_media_files(smb_fname
->base_name
)) {
1176 return SMB_VFS_NEXT_LCHOWN(handle
, smb_fname
, uid
, gid
);
1179 status
= alloc_get_client_smb_fname(handle
,
1187 status
= SMB_VFS_NEXT_LCHOWN(handle
, client_fname
, uid
, gid
);
1190 TALLOC_FREE(client_fname
);
1194 static int um_chdir(vfs_handle_struct
*handle
,
1195 const struct smb_filename
*smb_fname
)
1198 struct smb_filename
*client_fname
= NULL
;
1200 DEBUG(10, ("Entering um_chdir\n"));
1202 if (!is_in_media_files(smb_fname
->base_name
)) {
1203 return SMB_VFS_NEXT_CHDIR(handle
, smb_fname
);
1206 status
= alloc_get_client_smb_fname(handle
,
1214 status
= SMB_VFS_NEXT_CHDIR(handle
, client_fname
);
1217 TALLOC_FREE(client_fname
);
1221 static int um_ntimes(vfs_handle_struct
*handle
,
1222 const struct smb_filename
*smb_fname
,
1223 struct smb_file_time
*ft
)
1226 struct smb_filename
*client_fname
= NULL
;
1228 DEBUG(10, ("Entering um_ntimes\n"));
1230 if (!is_in_media_files(smb_fname
->base_name
)) {
1231 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1234 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1235 smb_fname
, &client_fname
);
1240 status
= SMB_VFS_NEXT_NTIMES(handle
, client_fname
, ft
);
1243 TALLOC_FREE(client_fname
);
1247 static int um_symlinkat(vfs_handle_struct
*handle
,
1248 const struct smb_filename
*link_contents
,
1249 struct files_struct
*dirfsp
,
1250 const struct smb_filename
*new_smb_fname
)
1253 struct smb_filename
*new_link_target
= NULL
;
1254 struct smb_filename
*new_client_fname
= NULL
;
1256 DEBUG(10, ("Entering um_symlinkat\n"));
1258 if (!is_in_media_files(link_contents
->base_name
) &&
1259 !is_in_media_files(new_smb_fname
->base_name
)) {
1260 return SMB_VFS_NEXT_SYMLINKAT(handle
,
1266 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1267 link_contents
, &new_link_target
);
1271 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1272 new_smb_fname
, &new_client_fname
);
1277 status
= SMB_VFS_NEXT_SYMLINKAT(handle
,
1283 TALLOC_FREE(new_link_target
);
1284 TALLOC_FREE(new_client_fname
);
1288 static int um_readlinkat(vfs_handle_struct
*handle
,
1289 files_struct
*dirfsp
,
1290 const struct smb_filename
*smb_fname
,
1295 struct smb_filename
*client_fname
= NULL
;
1297 DEBUG(10, ("Entering um_readlinkat\n"));
1299 if (!is_in_media_files(smb_fname
->base_name
)) {
1300 return SMB_VFS_NEXT_READLINKAT(handle
,
1307 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1308 smb_fname
, &client_fname
);
1313 status
= SMB_VFS_NEXT_READLINKAT(handle
,
1320 TALLOC_FREE(client_fname
);
1324 static int um_linkat(vfs_handle_struct
*handle
,
1325 files_struct
*srcfsp
,
1326 const struct smb_filename
*old_smb_fname
,
1327 files_struct
*dstfsp
,
1328 const struct smb_filename
*new_smb_fname
,
1332 struct smb_filename
*old_client_fname
= NULL
;
1333 struct smb_filename
*new_client_fname
= NULL
;
1335 DEBUG(10, ("Entering um_linkat\n"));
1336 if (!is_in_media_files(old_smb_fname
->base_name
) &&
1337 !is_in_media_files(new_smb_fname
->base_name
)) {
1338 return SMB_VFS_NEXT_LINKAT(handle
,
1346 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1347 old_smb_fname
, &old_client_fname
);
1351 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1352 new_smb_fname
, &new_client_fname
);
1357 status
= SMB_VFS_NEXT_LINKAT(handle
,
1365 TALLOC_FREE(old_client_fname
);
1366 TALLOC_FREE(new_client_fname
);
1370 static int um_mknodat(vfs_handle_struct
*handle
,
1371 files_struct
*dirfsp
,
1372 const struct smb_filename
*smb_fname
,
1377 struct smb_filename
*client_fname
= NULL
;
1379 DEBUG(10, ("Entering um_mknodat\n"));
1380 if (!is_in_media_files(smb_fname
->base_name
)) {
1381 return SMB_VFS_NEXT_MKNODAT(handle
,
1388 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1389 smb_fname
, &client_fname
);
1394 status
= SMB_VFS_NEXT_MKNODAT(handle
,
1401 TALLOC_FREE(client_fname
);
1405 static struct smb_filename
*um_realpath(vfs_handle_struct
*handle
,
1407 const struct smb_filename
*smb_fname
)
1409 struct smb_filename
*client_fname
= NULL
;
1410 struct smb_filename
*result_fname
= NULL
;
1413 DEBUG(10, ("Entering um_realpath\n"));
1415 if (!is_in_media_files(smb_fname
->base_name
)) {
1416 return SMB_VFS_NEXT_REALPATH(handle
, ctx
, smb_fname
);
1419 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1420 smb_fname
, &client_fname
);
1425 result_fname
= SMB_VFS_NEXT_REALPATH(handle
, ctx
, client_fname
);
1428 TALLOC_FREE(client_fname
);
1429 return result_fname
;
1432 static int um_chflags(vfs_handle_struct
*handle
,
1433 const struct smb_filename
*smb_fname
,
1437 struct smb_filename
*client_fname
= NULL
;
1439 DEBUG(10, ("Entering um_mknod\n"));
1440 if (!is_in_media_files(smb_fname
->base_name
)) {
1441 return SMB_VFS_NEXT_CHFLAGS(handle
, smb_fname
, flags
);
1444 status
= alloc_get_client_smb_fname(handle
, talloc_tos(),
1445 smb_fname
, &client_fname
);
1450 status
= SMB_VFS_NEXT_CHFLAGS(handle
, client_fname
, flags
);
1453 TALLOC_FREE(client_fname
);
1457 static NTSTATUS
um_streaminfo(struct vfs_handle_struct
*handle
,
1458 struct files_struct
*fsp
,
1459 const struct smb_filename
*smb_fname
,
1461 unsigned int *num_streams
,
1462 struct stream_struct
**streams
)
1466 struct smb_filename
*client_fname
= NULL
;
1468 DEBUG(10, ("Entering um_streaminfo\n"));
1470 if (!is_in_media_files(smb_fname
->base_name
)) {
1471 return SMB_VFS_NEXT_STREAMINFO(handle
, fsp
, smb_fname
,
1472 ctx
, num_streams
, streams
);
1475 ret
= alloc_get_client_smb_fname(handle
,
1480 status
= NT_STATUS_NO_MEMORY
;
1485 * This only works on files, so we don't have to worry about
1486 * our fake directory stat'ing here. But what does this
1487 * function do, exactly? Does it need extra modifications for
1490 status
= SMB_VFS_NEXT_STREAMINFO(handle
, fsp
, client_fname
,
1491 ctx
, num_streams
, streams
);
1493 TALLOC_FREE(client_fname
);
1498 * Ignoring get_real_filename function because the default doesn't do
1502 static NTSTATUS
um_get_nt_acl_at(vfs_handle_struct
*handle
,
1503 struct files_struct
*dirfsp
,
1504 const struct smb_filename
*smb_fname
,
1505 uint32_t security_info
,
1506 TALLOC_CTX
*mem_ctx
,
1507 struct security_descriptor
**ppdesc
)
1510 char *client_path
= NULL
;
1511 struct smb_filename
*client_smb_fname
= NULL
;
1515 DBG_DEBUG("Entering um_get_nt_acl_at\n");
1517 ok
= is_in_media_files(smb_fname
->base_name
);
1519 return SMB_VFS_NEXT_GET_NT_ACL_AT(handle
,
1527 ret
= alloc_get_client_path(handle
,
1529 smb_fname
->base_name
,
1532 status
= map_nt_error_from_unix(errno
);
1536 client_smb_fname
= synthetic_smb_fname(talloc_tos(),
1542 if (client_smb_fname
== NULL
) {
1543 TALLOC_FREE(client_path
);
1544 return NT_STATUS_NO_MEMORY
;
1547 status
= SMB_VFS_NEXT_GET_NT_ACL_AT(handle
,
1554 TALLOC_FREE(client_smb_fname
);
1555 TALLOC_FREE(client_path
);
1559 static SMB_ACL_T
um_sys_acl_get_file(vfs_handle_struct
*handle
,
1560 const struct smb_filename
*smb_fname
,
1561 SMB_ACL_TYPE_T type
,
1562 TALLOC_CTX
*mem_ctx
)
1565 int saved_errno
= 0;
1566 struct smb_filename
*client_fname
= NULL
;
1569 DEBUG(10, ("Entering um_sys_acl_get_file\n"));
1571 if (!is_in_media_files(smb_fname
->base_name
)) {
1572 return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle
, smb_fname
,
1576 status
= alloc_get_client_smb_fname(handle
,
1581 ret
= (SMB_ACL_T
)NULL
;
1585 ret
= SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle
, client_fname
,
1589 if (ret
== (SMB_ACL_T
)NULL
) {
1590 saved_errno
= errno
;
1592 TALLOC_FREE(client_fname
);
1593 if (saved_errno
!= 0) {
1594 errno
= saved_errno
;
1599 static int um_sys_acl_set_file(vfs_handle_struct
*handle
,
1600 const struct smb_filename
*smb_fname
,
1601 SMB_ACL_TYPE_T acltype
,
1605 int saved_errno
= 0;
1606 struct smb_filename
*client_fname
= NULL
;
1608 DEBUG(10, ("Entering um_sys_acl_set_file\n"));
1610 if (!is_in_media_files(smb_fname
->base_name
)) {
1611 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle
, smb_fname
,
1615 status
= alloc_get_client_smb_fname(handle
,
1623 status
= SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle
, client_fname
,
1628 saved_errno
= errno
;
1630 TALLOC_FREE(client_fname
);
1631 if (saved_errno
!= 0) {
1632 errno
= saved_errno
;
1637 static int um_sys_acl_delete_def_file(vfs_handle_struct
*handle
,
1638 const struct smb_filename
*smb_fname
)
1641 int saved_errno
= 0;
1642 struct smb_filename
*client_fname
= NULL
;
1644 DEBUG(10, ("Entering um_sys_acl_delete_def_file\n"));
1646 if (!is_in_media_files(smb_fname
->base_name
)) {
1647 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle
,
1651 status
= alloc_get_client_smb_fname(handle
,
1659 status
= SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle
, client_fname
);
1663 saved_errno
= errno
;
1665 TALLOC_FREE(client_fname
);
1666 if (saved_errno
!= 0) {
1667 errno
= saved_errno
;
1672 static ssize_t
um_getxattr(struct vfs_handle_struct
*handle
,
1673 const struct smb_filename
*smb_fname
,
1679 struct smb_filename
*client_fname
= NULL
;
1682 DEBUG(10, ("Entering um_getxattr\n"));
1683 if (!is_in_media_files(smb_fname
->base_name
)) {
1684 return SMB_VFS_NEXT_GETXATTR(handle
, smb_fname
,
1688 status
= alloc_get_client_smb_fname(handle
,
1697 ret
= SMB_VFS_NEXT_GETXATTR(handle
, client_fname
, name
, value
, size
);
1699 TALLOC_FREE(client_fname
);
1703 static ssize_t
um_listxattr(struct vfs_handle_struct
*handle
,
1704 const struct smb_filename
*smb_fname
,
1709 struct smb_filename
*client_fname
= NULL
;
1712 DEBUG(10, ("Entering um_listxattr\n"));
1714 if (!is_in_media_files(smb_fname
->base_name
)) {
1715 return SMB_VFS_NEXT_LISTXATTR(handle
, smb_fname
, list
, size
);
1718 status
= alloc_get_client_smb_fname(handle
,
1727 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, client_fname
, list
, size
);
1730 TALLOC_FREE(client_fname
);
1734 static int um_removexattr(struct vfs_handle_struct
*handle
,
1735 const struct smb_filename
*smb_fname
,
1739 struct smb_filename
*client_fname
= NULL
;
1741 DEBUG(10, ("Entering um_removexattr\n"));
1743 if (!is_in_media_files(smb_fname
->base_name
)) {
1744 return SMB_VFS_NEXT_REMOVEXATTR(handle
, smb_fname
, name
);
1747 status
= alloc_get_client_smb_fname(handle
,
1755 status
= SMB_VFS_NEXT_REMOVEXATTR(handle
, client_fname
, name
);
1758 TALLOC_FREE(client_fname
);
1762 static int um_setxattr(struct vfs_handle_struct
*handle
,
1763 const struct smb_filename
*smb_fname
,
1770 struct smb_filename
*client_fname
= NULL
;
1772 DEBUG(10, ("Entering um_setxattr\n"));
1774 if (!is_in_media_files(smb_fname
->base_name
)) {
1775 return SMB_VFS_NEXT_SETXATTR(handle
, smb_fname
, name
, value
,
1779 status
= alloc_get_client_smb_fname(handle
,
1787 status
= SMB_VFS_NEXT_SETXATTR(handle
, client_fname
, name
, value
,
1791 TALLOC_FREE(client_fname
);
1795 static int um_connect(vfs_handle_struct
*handle
,
1796 const char *service
,
1800 struct um_config_data
*config
;
1803 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1808 config
= talloc_zero(handle
->conn
, struct um_config_data
);
1810 DEBUG(1, ("talloc_zero() failed\n"));
1815 enumval
= lp_parm_enum(SNUM(handle
->conn
), UM_PARAM_TYPE_NAME
,
1816 "clientid", um_clientid
, UM_CLIENTID_NAME
);
1817 if (enumval
== -1) {
1818 DEBUG(1, ("value for %s: type unknown\n",
1819 UM_PARAM_TYPE_NAME
));
1822 config
->clientid
= (enum um_clientid
)enumval
;
1824 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1825 NULL
, struct um_config_data
,
1831 /* VFS operations structure */
1833 static struct vfs_fn_pointers vfs_um_fns
= {
1834 .connect_fn
= um_connect
,
1836 /* Disk operations */
1838 .statvfs_fn
= um_statvfs
,
1840 /* Directory operations */
1842 .fdopendir_fn
= um_fdopendir
,
1843 .readdir_fn
= um_readdir
,
1844 .seekdir_fn
= um_seekdir
,
1845 .telldir_fn
= um_telldir
,
1846 .rewind_dir_fn
= um_rewinddir
,
1847 .mkdirat_fn
= um_mkdirat
,
1848 .closedir_fn
= um_closedir
,
1850 /* File operations */
1852 .openat_fn
= um_openat
,
1853 .create_file_fn
= um_create_file
,
1854 .renameat_fn
= um_renameat
,
1856 .lstat_fn
= um_lstat
,
1857 .fstat_fn
= um_fstat
,
1858 .unlinkat_fn
= um_unlinkat
,
1859 .chmod_fn
= um_chmod
,
1860 .lchown_fn
= um_lchown
,
1861 .chdir_fn
= um_chdir
,
1862 .ntimes_fn
= um_ntimes
,
1863 .symlinkat_fn
= um_symlinkat
,
1864 .readlinkat_fn
= um_readlinkat
,
1865 .linkat_fn
= um_linkat
,
1866 .mknodat_fn
= um_mknodat
,
1867 .realpath_fn
= um_realpath
,
1868 .chflags_fn
= um_chflags
,
1869 .streaminfo_fn
= um_streaminfo
,
1871 /* NT ACL operations. */
1873 .get_nt_acl_at_fn
= um_get_nt_acl_at
,
1875 /* POSIX ACL operations. */
1877 .sys_acl_get_file_fn
= um_sys_acl_get_file
,
1878 .sys_acl_set_file_fn
= um_sys_acl_set_file
,
1879 .sys_acl_delete_def_file_fn
= um_sys_acl_delete_def_file
,
1881 /* EA operations. */
1882 .getxattr_fn
= um_getxattr
,
1883 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
1884 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
1885 .listxattr_fn
= um_listxattr
,
1886 .removexattr_fn
= um_removexattr
,
1887 .setxattr_fn
= um_setxattr
,
1891 NTSTATUS
vfs_unityed_media_init(TALLOC_CTX
*ctx
)
1893 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1894 "unityed_media", &vfs_um_fns
);
1895 if (!NT_STATUS_IS_OK(ret
)) {
1899 vfs_um_debug_level
= debug_add_class("unityed_media");
1901 if (vfs_um_debug_level
== -1) {
1902 vfs_um_debug_level
= DBGC_VFS
;
1903 DEBUG(1, ("unityed_media_init: Couldn't register custom "
1904 "debugging class.\n"));