vfs_glusterfs: Use glfs_readlinkat() for SMB_VFS_READLINKAT
[Samba.git] / source3 / modules / vfs_unityed_media.c
blob3c1a263c25a8bf835f40b85d31b915994d320675
1 /*
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
22 * 02110-1301, USA.
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
30 * configuration.
31 * eg.
33 * [avid_win]
34 * path = /video
35 * vfs objects = unityed_media
36 * ...
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
47 * the list.
49 * Notes:
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
53 * general use.
57 #include "includes.h"
58 #include "system/filesys.h"
59 #include "smbd/smbd.h"
60 #include "../smbd/globals.h"
61 #include "auth.h"
62 #include "../lib/tsocket/tsocket.h"
63 #include "lib/util/smb_strtox.h"
64 #include <libgen.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"},
87 {-1, NULL}
90 /* supplements the directory list stream */
91 typedef struct um_dirinfo_struct {
92 DIR* dirstream;
93 char *dirpath;
94 char *clientPath;
95 bool isInMediaFiles;
96 char *clientSubDirname;
97 } um_dirinfo_struct;
99 /**
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;
105 codepoint_t cp;
106 size_t size;
107 int error = 0;
109 DEBUG(10, ("get_digit_group entering with path '%s'\n",
110 path));
113 * Delibiretly initialize to 0 because callers use this result
114 * even though the string doesn't contain any number and we
115 * returned false
117 *digit = 0;
119 while (*p) {
120 cp = next_codepoint(p, &size);
121 if (cp == -1) {
122 return false;
124 if ((size == 1) && (isdigit(cp))) {
125 *digit = (uintmax_t)smb_strtoul(p,
126 NULL,
128 &error,
129 SMB_STR_STANDARD);
130 if (error != 0) {
131 return false;
133 DEBUG(10, ("num_suffix = '%ju'\n",
134 *digit));
135 return true;
137 p += size;
140 return false;
143 /* Add "_<remote_name>.<number>" suffix to path or filename.
145 * Success: return 0
146 * Failure: set errno, path NULL, return -1
149 static int alloc_append_client_suffix(vfs_handle_struct *handle,
150 char **path)
152 int status = 0;
153 uintmax_t number;
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,
161 return -1);
163 (void)get_digit_group(*path, &number);
165 switch (config->clientid) {
167 case UM_CLIENTID_IP:
168 clientid = tsocket_address_inet_addr_string(
169 handle->conn->sconn->remote_address, talloc_tos());
170 if (clientid == NULL) {
171 errno = ENOMEM;
172 status = -1;
173 goto err;
175 break;
177 case UM_CLIENTID_HOSTNAME:
178 clientid = get_remote_machine_name();
179 break;
181 case UM_CLIENTID_NAME:
182 default:
183 clientid = get_current_username();
184 break;
187 *path = talloc_asprintf_append(*path, "_%s.%ju",
188 clientid, number);
189 if (*path == NULL) {
190 DEBUG(1, ("alloc_append_client_suffix "
191 "out of memory\n"));
192 errno = ENOMEM;
193 status = -1;
194 goto err;
196 DEBUG(10, ("Leaving with *path '%s'\n", *path));
197 err:
198 return status;
201 /* Returns true if the file or directory begins with the appledouble
202 * prefix.
204 static bool is_apple_double(const char* fname)
206 bool ret = false;
208 DEBUG(10, ("Entering with fname '%s'\n", fname));
210 if (strnequal(APPLE_DOUBLE_PREFIX, fname, APPLE_DOUBLE_PREFIX_LEN)) {
211 ret = true;
213 DEBUG(10, ("Leaving with ret '%s'\n",
214 ret == true ? "true" : "false"));
215 return ret;
218 static bool starts_with_media_dir(const char* media_dirname,
219 size_t media_dirname_len,
220 const char *path)
222 bool ret = false;
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)) {
230 path_start += 2;
233 if (strnequal(media_dirname, path_start, media_dirname_len)
235 ((path_start[media_dirname_len] == '\0') ||
236 (path_start[media_dirname_len] == '/'))) {
237 ret = true;
240 DEBUG(10, ("Leaving with ret '%s'\n",
241 ret == true ? "true" : "false"));
242 return ret;
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
248 * directory
250 static bool is_in_media_dir(const char *path)
252 int transition_count = 0;
253 const char *path_start = path;
254 const char *p;
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)) {
262 path_start += 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;
273 } else {
274 return false;
277 if (path_start[media_dirname_len] == '\0') {
278 goto out;
281 p = path_start + media_dirname_len + 1;
283 while (true) {
284 if (*p == '\0' || *p == '/') {
285 if (strnequal(p - 3, "/..", 3)) {
286 transition_count--;
287 } else if ((p[-1] != '/') || !strnequal(p - 2, "/.", 2)) {
288 transition_count++;
291 if (*p == '\0') {
292 break;
294 p++;
297 out:
298 DEBUG(10, ("Going out with transition_count '%i'\n",
299 transition_count));
300 if (((transition_count == 1) && (media_dirname == AVID_MXF_DIRNAME))
302 ((transition_count == 0) && (media_dirname == OMFI_MEDIAFILES_DIRNAME))) {
303 return true;
305 else return false;
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)
317 bool ret = false;
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)) {
325 ret = true;
327 DEBUG(10, ("Leaving with ret '%s'\n",
328 ret == true ? "true" : "false"));
329 return ret;
333 /* Add client suffix to "pure-number" path.
335 * Caller must free newPath.
337 * Success: return 0
338 * Failure: set errno, newPath NULL, return -1
340 static int alloc_get_client_path(vfs_handle_struct *handle,
341 TALLOC_CTX *ctx,
342 const char *path_in,
343 char **path_out)
345 int status = 0;
346 char *p;
347 char *digits;
348 size_t digits_len;
349 uintmax_t number;
351 *path_out = talloc_strdup(ctx, path_in);
352 if (*path_out == NULL) {
353 DEBUG(1, ("alloc_get_client_path ENOMEM\n"));
354 return -1;
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"));
362 return -1;
364 digits_len = strlen(digits);
366 p = strstr_m(path_in, digits);
367 if ((p)
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);
382 if (status != 0) {
383 goto 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"));
389 status = -1;
390 goto out;
393 out:
394 /* path_out must be freed in caller. */
395 DEBUG(10, ("Result:'%s'\n", *path_out));
396 return status;
400 * Success: return 0
401 * Failure: set errno, return -1
403 static int alloc_get_client_smb_fname(struct vfs_handle_struct *handle,
404 TALLOC_CTX *ctx,
405 const struct smb_filename *smb_fname,
406 struct smb_filename **client_fname)
408 int status ;
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"));
416 return -1;
418 status = alloc_get_client_path(handle, ctx,
419 smb_fname->base_name,
420 &(*client_fname)->base_name);
421 if (status != 0) {
422 return -1;
425 DEBUG(10, ("Leaving with (*client_fname)->base_name "
426 "'%s'\n", (*client_fname)->base_name));
428 return 0;
433 * Success: return 0
434 * Failure: set errno, return -1
436 static int alloc_set_client_dirinfo_path(struct vfs_handle_struct *handle,
437 TALLOC_CTX *ctx,
438 char **path,
439 const char *suffix_number)
441 int status;
443 DEBUG(10, ("Entering with suffix_number '%s'\n",
444 suffix_number));
446 *path = talloc_strdup(ctx, suffix_number);
447 if (*path == NULL) {
448 DEBUG(1, ("alloc_set_client_dirinfo_path ENOMEM\n"));
449 return -1;
451 status = alloc_append_client_suffix(handle, path);
452 if (status != 0) {
453 return -1;
456 DEBUG(10, ("Leaving with *path '%s'\n", *path));
458 return 0;
461 static int alloc_set_client_dirinfo(vfs_handle_struct *handle,
462 const char *fname,
463 struct um_dirinfo_struct **di_result)
465 int status = 0;
466 char *digits;
467 uintmax_t number;
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) {
474 goto err;
476 dip = *di_result;
478 dip->dirpath = talloc_strdup(dip, fname);
479 if (dip->dirpath == NULL) {
480 goto err;
483 if (!is_in_media_files(fname)) {
484 dip->isInMediaFiles = false;
485 dip->clientPath = NULL;
486 dip->clientSubDirname = NULL;
487 goto out;
490 dip->isInMediaFiles = true;
492 (void)get_digit_group(fname, &number);
493 digits = talloc_asprintf(talloc_tos(), "%ju", number);
494 if (digits == NULL) {
495 goto err;
498 status = alloc_set_client_dirinfo_path(handle, dip,
499 &dip->clientSubDirname,
500 digits);
501 if (status != 0) {
502 goto err;
505 status = alloc_get_client_path(handle, dip, fname,
506 &dip->clientPath);
507 if (status != 0 || dip->clientPath == NULL) {
508 goto err;
511 out:
512 DEBUG(10, ("Leaving with (*dirInfo)->dirpath '%s', "
513 "(*dirInfo)->clientPath '%s'\n",
514 dip->dirpath, dip->clientPath));
515 return status;
517 err:
518 DEBUG(1, ("Failing with fname '%s'\n", fname));
519 TALLOC_FREE(*di_result);
520 status = -1;
521 errno = ENOMEM;
522 return status;
525 /**********************************************************************
526 * VFS functions
527 **********************************************************************/
530 * Success: return 0
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)
537 int status;
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,
547 talloc_tos(),
548 smb_fname,
549 &client_fname);
550 if (status != 0) {
551 goto err;
554 status = SMB_VFS_NEXT_STATVFS(handle, client_fname, statbuf);
555 err:
556 TALLOC_FREE(client_fname);
557 DEBUG(10, ("Leaving with path '%s'\n", smb_fname->base_name));
558 return status;
561 static DIR *um_fdopendir(vfs_handle_struct *handle,
562 files_struct *fsp,
563 const char *mask,
564 uint32_t attr)
566 struct um_dirinfo_struct *dirInfo = NULL;
567 DIR *dirstream;
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);
573 if (!dirstream) {
574 goto err;
577 if (alloc_set_client_dirinfo(handle,
578 fsp->fsp_name->base_name,
579 &dirInfo)) {
580 goto err;
583 dirInfo->dirstream = dirstream;
585 if (!dirInfo->isInMediaFiles) {
587 * FIXME: this is the original code, something must be
588 * missing here, but what? -slow
590 goto out;
593 out:
594 DEBUG(10, ("Leaving with dirInfo->dirpath '%s', "
595 "dirInfo->clientPath '%s', "
596 "fsp->fsp_name->st.st_ex_mtime %s",
597 dirInfo->dirpath,
598 dirInfo->clientPath,
599 ctime(&(fsp->fsp_name->st.st_ex_mtime.tv_sec))));
600 return (DIR *) dirInfo;
602 err:
603 DEBUG(1, ("Failing with fsp->fsp_name->base_name '%s'\n",
604 fsp->fsp_name->base_name));
605 TALLOC_FREE(dirInfo);
606 return NULL;
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,
619 DIR *dirp,
620 SMB_STRUCT_STAT *sbuf)
622 um_dirinfo_struct* dirInfo = (um_dirinfo_struct*)dirp;
623 struct dirent *d = NULL;
624 int skip;
626 DEBUG(10, ("dirInfo->dirpath '%s', "
627 "dirInfo->clientPath '%s', "
628 "dirInfo->isInMediaFiles '%s', "
629 "dirInfo->clientSubDirname '%s'\n",
630 dirInfo->dirpath,
631 dirInfo->clientPath,
632 dirInfo->isInMediaFiles ? "true" : "false",
633 dirInfo->clientSubDirname));
635 if (!dirInfo->isInMediaFiles) {
636 return SMB_VFS_NEXT_READDIR(handle, dirfsp, dirInfo->dirstream, sbuf);
639 do {
640 const char* dname;
641 bool isAppleDouble;
642 char *digits;
643 size_t digits_len;
644 uintmax_t number;
646 skip = false;
647 d = SMB_VFS_NEXT_READDIR(handle, dirfsp, dirInfo->dirstream, sbuf);
649 if (d == NULL) {
650 break;
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;
657 } else {
658 dname = d->d_name;
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"));
668 goto err;
670 digits_len = strlen(digits);
672 if (alloc_set_client_dirinfo_path(handle,
673 dirInfo,
674 &((dirInfo)->clientSubDirname),
675 digits)) {
676 goto err;
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
686 * bit confusing.
688 if (strequal(dname, digits)) {
689 skip = false;
690 } else if (strequal(dname, dirInfo->clientSubDirname)) {
692 * Remove suffix of this client's suffixed
693 * subdirectories
695 if (isAppleDouble) {
696 d->d_name[digits_len + APPLE_DOUBLE_PREFIX_LEN] = '\0';
697 } else {
698 d->d_name[digits_len] = '\0';
700 } else if (strnequal(digits, dname, digits_len)) {
702 * Set to false to see another clients subdirectories
704 skip = false;
706 } while (skip);
708 DEBUG(10, ("Leaving um_readdir\n"));
709 return d;
710 err:
711 TALLOC_FREE(dirInfo);
712 return NULL;
715 static void um_seekdir(vfs_handle_struct *handle,
716 DIR *dirp,
717 long offset)
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,
725 DIR *dirp)
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,
733 DIR *dirp)
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,
743 mode_t mode)
745 int status;
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(),
751 dirfsp,
752 smb_fname);
753 if (full_fname == NULL) {
754 return -1;
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,
763 dirfsp,
764 smb_fname,
765 mode);
768 status = alloc_get_client_smb_fname(handle,
769 talloc_tos(),
770 full_fname,
771 &client_fname);
772 if (status != 0) {
773 goto err;
776 status = SMB_VFS_NEXT_MKDIRAT(handle,
777 handle->conn->cwd_fsp,
778 client_fname,
779 mode);
780 err:
781 DEBUG(10, ("Leaving with path '%s'\n", path));
782 TALLOC_FREE(client_fname);
783 TALLOC_FREE(full_fname);
784 return status;
787 static int um_closedir(vfs_handle_struct *handle,
788 DIR *dirp)
790 DIR *realdirp = ((um_dirinfo_struct*)dirp)->dirstream;
792 TALLOC_FREE(dirp);
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;
804 int ret;
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,
811 dirfsp,
812 smb_fname,
813 fsp,
814 how);
817 if (alloc_get_client_smb_fname(handle, talloc_tos(),
818 smb_fname,
819 &client_fname)) {
820 ret = -1;
821 goto err;
825 * FIXME:
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,
838 dirfsp,
839 client_fname,
840 fsp,
841 how);
842 err:
843 TALLOC_FREE(client_fname);
844 DEBUG(10, ("Leaving with smb_fname->base_name '%s'\n",
845 smb_fname->base_name));
846 return ret;
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,
865 int *pinfo,
866 const struct smb2_create_blobs *in_context_blobs,
867 struct smb2_create_blobs *out_context_blobs)
869 NTSTATUS status;
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(
877 handle,
878 req,
879 dirfsp,
880 smb_fname,
881 access_mask,
882 share_access,
883 create_disposition,
884 create_options,
885 file_attributes,
886 oplock_request,
887 lease,
888 allocation_size,
889 private_flags,
891 ea_list,
892 result_fsp,
893 pinfo,
894 in_context_blobs,
895 out_context_blobs);
898 if (alloc_get_client_smb_fname(handle, talloc_tos(),
899 smb_fname,
900 &client_fname)) {
901 status = map_nt_error_from_unix(errno);
902 goto err;
906 * FIXME:
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(
912 handle,
913 req,
914 dirfsp,
915 client_fname,
916 access_mask,
917 share_access,
918 create_disposition,
919 create_options,
920 file_attributes,
921 oplock_request,
922 lease,
923 allocation_size,
924 private_flags,
926 ea_list,
927 result_fsp,
928 pinfo,
929 in_context_blobs,
930 out_context_blobs);
931 err:
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)) :
940 "No fsp time\n"));
941 return status;
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)
950 int status;
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(),
957 srcfsp,
958 smb_fname_src);
959 if (src_full_fname == NULL) {
960 errno = ENOMEM;
961 return -1;
963 dst_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
964 dstfsp,
965 smb_fname_dst);
966 if (dst_full_fname == NULL) {
967 TALLOC_FREE(src_full_fname);
968 errno = ENOMEM;
969 return -1;
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,
984 srcfsp,
985 smb_fname_src,
986 dstfsp,
987 smb_fname_dst);
990 status = alloc_get_client_smb_fname(handle, talloc_tos(),
991 src_full_fname,
992 &src_client_fname);
993 if (status != 0) {
994 goto err;
997 status = alloc_get_client_smb_fname(handle, talloc_tos(),
998 dst_full_fname,
999 &dst_client_fname);
1001 if (status != 0) {
1002 goto err;
1005 status = SMB_VFS_NEXT_RENAMEAT(handle,
1006 handle->conn->cwd_fsp,
1007 src_client_fname,
1008 handle->conn->cwd_fsp,
1009 dst_client_fname);
1011 err:
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);
1020 return status;
1025 * Success: return 0
1026 * Failure: set errno, return -1
1028 static int um_stat(vfs_handle_struct *handle,
1029 struct smb_filename *smb_fname)
1031 int status = 0;
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(),
1042 smb_fname,
1043 &client_fname);
1044 if (status != 0) {
1045 goto err;
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);
1051 if (status != 0) {
1052 goto err;
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;
1063 err:
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))));
1067 return status;
1070 static int um_lstat(vfs_handle_struct *handle,
1071 struct smb_filename *smb_fname)
1073 int status = 0;
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(),
1086 smb_fname,
1087 &client_fname);
1088 if (status != 0) {
1089 goto err;
1091 status = SMB_VFS_NEXT_LSTAT(handle, client_fname);
1092 if (status != 0) {
1093 goto err;
1096 smb_fname->st = client_fname->st;
1098 err:
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))));
1102 return status;
1105 static int um_fstat(vfs_handle_struct *handle,
1106 files_struct *fsp, SMB_STRUCT_STAT *sbuf)
1108 int status = 0;
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);
1114 if (status != 0) {
1115 goto out;
1118 if ((fsp->fsp_name == NULL) ||
1119 !is_in_media_files(fsp->fsp_name->base_name)) {
1120 goto out;
1123 status = um_stat(handle, fsp->fsp_name);
1124 if (status != 0) {
1125 goto out;
1128 *sbuf = fsp->fsp_name->st;
1130 out:
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"));
1134 return status;
1137 static int um_unlinkat(vfs_handle_struct *handle,
1138 struct files_struct *dirfsp,
1139 const struct smb_filename *smb_fname,
1140 int flags)
1142 int ret;
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,
1150 dirfsp,
1151 smb_fname,
1152 flags);
1155 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1156 dirfsp,
1157 smb_fname);
1158 if (full_fname == NULL) {
1159 return -1;
1162 ret = alloc_get_client_smb_fname(handle, talloc_tos(),
1163 full_fname,
1164 &client_fname);
1165 if (ret != 0) {
1166 goto err;
1169 ret = SMB_VFS_NEXT_UNLINKAT(handle,
1170 dirfsp->conn->cwd_fsp,
1171 client_fname,
1172 flags);
1174 err:
1175 TALLOC_FREE(full_fname);
1176 TALLOC_FREE(client_fname);
1177 return ret;
1180 static int um_lchown(vfs_handle_struct *handle,
1181 const struct smb_filename *smb_fname,
1182 uid_t uid,
1183 gid_t gid)
1185 int status;
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,
1194 talloc_tos(),
1195 smb_fname,
1196 &client_fname);
1197 if (status != 0) {
1198 goto err;
1201 status = SMB_VFS_NEXT_LCHOWN(handle, client_fname, uid, gid);
1203 err:
1204 TALLOC_FREE(client_fname);
1205 return status;
1208 static int um_chdir(vfs_handle_struct *handle,
1209 const struct smb_filename *smb_fname)
1211 int status;
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,
1221 talloc_tos(),
1222 smb_fname,
1223 &client_fname);
1224 if (status != 0) {
1225 goto err;
1228 status = SMB_VFS_NEXT_CHDIR(handle, client_fname);
1230 err:
1231 TALLOC_FREE(client_fname);
1232 return status;
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)
1240 int status;
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(),
1248 dirfsp,
1249 new_smb_fname);
1250 if (full_fname == NULL) {
1251 return -1;
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,
1258 link_contents,
1259 dirfsp,
1260 new_smb_fname);
1263 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1264 link_contents, &new_link_target);
1265 if (status != 0) {
1266 goto err;
1268 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1269 full_fname, &new_client_fname);
1270 if (status != 0) {
1271 goto err;
1274 status = SMB_VFS_NEXT_SYMLINKAT(handle,
1275 new_link_target,
1276 handle->conn->cwd_fsp,
1277 new_client_fname);
1279 err:
1280 TALLOC_FREE(new_link_target);
1281 TALLOC_FREE(new_client_fname);
1282 TALLOC_FREE(full_fname);
1283 return status;
1286 static int um_readlinkat(vfs_handle_struct *handle,
1287 const struct files_struct *dirfsp,
1288 const struct smb_filename *smb_fname,
1289 char *buf,
1290 size_t bufsiz)
1292 int status;
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(),
1299 dirfsp,
1300 smb_fname);
1301 if (full_fname == NULL) {
1302 return -1;
1305 if (!is_in_media_files(full_fname->base_name)) {
1306 TALLOC_FREE(full_fname);
1307 return SMB_VFS_NEXT_READLINKAT(handle,
1308 dirfsp,
1309 smb_fname,
1310 buf,
1311 bufsiz);
1314 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1315 full_fname, &client_fname);
1316 if (status != 0) {
1317 goto err;
1320 status = SMB_VFS_NEXT_READLINKAT(handle,
1321 handle->conn->cwd_fsp,
1322 client_fname,
1323 buf,
1324 bufsiz);
1326 err:
1327 TALLOC_FREE(full_fname);
1328 TALLOC_FREE(client_fname);
1329 return status;
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,
1337 int flags)
1339 int status;
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(),
1346 srcfsp,
1347 old_smb_fname);
1348 if (old_full_fname == NULL) {
1349 return -1;
1351 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1352 dstfsp,
1353 new_smb_fname);
1354 if (new_full_fname == NULL) {
1355 TALLOC_FREE(old_full_fname);
1356 return -1;
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,
1365 srcfsp,
1366 old_smb_fname,
1367 dstfsp,
1368 new_smb_fname,
1369 flags);
1372 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1373 old_full_fname, &old_client_fname);
1374 if (status != 0) {
1375 goto err;
1377 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1378 new_full_fname, &new_client_fname);
1379 if (status != 0) {
1380 goto err;
1383 status = SMB_VFS_NEXT_LINKAT(handle,
1384 handle->conn->cwd_fsp,
1385 old_client_fname,
1386 handle->conn->cwd_fsp,
1387 new_client_fname,
1388 flags);
1390 err:
1391 TALLOC_FREE(old_full_fname);
1392 TALLOC_FREE(new_full_fname);
1393 TALLOC_FREE(old_client_fname);
1394 TALLOC_FREE(new_client_fname);
1395 return status;
1398 static int um_mknodat(vfs_handle_struct *handle,
1399 files_struct *dirfsp,
1400 const struct smb_filename *smb_fname,
1401 mode_t mode,
1402 SMB_DEV_T dev)
1404 int status;
1405 struct smb_filename *client_fname = NULL;
1406 struct smb_filename *full_fname = NULL;
1408 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1409 dirfsp,
1410 smb_fname);
1411 if (full_fname == NULL) {
1412 return -1;
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,
1419 dirfsp,
1420 smb_fname,
1421 mode,
1422 dev);
1425 status = alloc_get_client_smb_fname(handle, talloc_tos(),
1426 full_fname, &client_fname);
1427 if (status != 0) {
1428 goto err;
1431 status = SMB_VFS_NEXT_MKNODAT(handle,
1432 handle->conn->cwd_fsp,
1433 client_fname,
1434 mode,
1435 dev);
1437 err:
1438 TALLOC_FREE(client_fname);
1439 TALLOC_FREE(full_fname);
1440 return status;
1443 static struct smb_filename *um_realpath(vfs_handle_struct *handle,
1444 TALLOC_CTX *ctx,
1445 const struct smb_filename *smb_fname)
1447 struct smb_filename *client_fname = NULL;
1448 struct smb_filename *result_fname = NULL;
1449 int status;
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);
1459 if (status != 0) {
1460 goto err;
1463 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, client_fname);
1465 err:
1466 TALLOC_FREE(client_fname);
1467 return result_fname;
1470 static int um_connect(vfs_handle_struct *handle,
1471 const char *service,
1472 const char *user)
1474 int rc;
1475 struct um_config_data *config;
1476 int enumval;
1478 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1479 if (rc != 0) {
1480 return rc;
1483 config = talloc_zero(handle->conn, struct um_config_data);
1484 if (!config) {
1485 DEBUG(1, ("talloc_zero() failed\n"));
1486 errno = ENOMEM;
1487 return -1;
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));
1495 return -1;
1497 config->clientid = (enum um_clientid)enumval;
1499 SMB_VFS_HANDLE_SET_DATA(handle, config,
1500 NULL, struct um_config_data,
1501 return -1);
1503 return 0;
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,
1530 .stat_fn = um_stat,
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,
1547 static_decl_vfs;
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)) {
1553 return 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"));
1564 return ret;