vfs_io_uring: move error handling out of vfs_io_uring_pread_recv()
[Samba.git] / source3 / modules / vfs_virusfilter.c
blob12f0364d2a7b6e99cd0fe227b57f0e5b47089ce8
1 /*
2 * Copyright (C) 2010-2016 SATOH Fumiyasu @ OSS Technology Corp., Japan
3 * Copyright (C) 2016-2017 Trever L. Adams
4 * Copyright (C) 2017 Ralph Boehme <slow@samba.org>
5 * Copyright (C) 2017 Jeremy Allison <jra@samba.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "vfs_virusfilter_common.h"
22 #include "vfs_virusfilter_utils.h"
25 * Default configuration values
26 * ======================================================================
29 #define VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX "virusfilter."
30 #define VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX ".infected"
31 #define VIRUSFILTER_DEFAULT_RENAME_PREFIX "virusfilter."
32 #define VIRUSFILTER_DEFAULT_RENAME_SUFFIX ".infected"
34 /* ====================================================================== */
36 enum virusfilter_scanner_enum {
37 VIRUSFILTER_SCANNER_CLAMAV,
38 VIRUSFILTER_SCANNER_FSAV,
39 VIRUSFILTER_SCANNER_SOPHOS
42 static const struct enum_list scanner_list[] = {
43 { VIRUSFILTER_SCANNER_CLAMAV, "clamav" },
44 { VIRUSFILTER_SCANNER_FSAV, "fsav" },
45 { VIRUSFILTER_SCANNER_SOPHOS, "sophos" },
46 { -1, NULL }
49 static const struct enum_list virusfilter_actions[] = {
50 { VIRUSFILTER_ACTION_QUARANTINE, "quarantine" },
51 { VIRUSFILTER_ACTION_RENAME, "rename" },
52 { VIRUSFILTER_ACTION_DELETE, "delete" },
54 /* alias for "delete" */
55 { VIRUSFILTER_ACTION_DELETE, "remove" },
57 /* alias for "delete" */
58 { VIRUSFILTER_ACTION_DELETE, "unlink" },
59 { VIRUSFILTER_ACTION_DO_NOTHING, "nothing" },
60 { -1, NULL}
63 static int virusfilter_config_destructor(struct virusfilter_config *config)
65 TALLOC_FREE(config->backend);
66 return 0;
70 * This is adapted from vfs_recycle module.
71 * Caller must have become_root();
73 static bool quarantine_directory_exist(
74 struct vfs_handle_struct *handle,
75 const char *dname)
77 int ret = -1;
78 struct smb_filename smb_fname = {
79 .base_name = discard_const_p(char, dname)
82 ret = SMB_VFS_STAT(handle->conn, &smb_fname);
83 if (ret == 0) {
84 return S_ISDIR(smb_fname.st.st_ex_mode);
87 return false;
90 /**
91 * Create directory tree
92 * @param conn connection
93 * @param dname Directory tree to be created
94 * @return Returns true for success
95 * This is adapted from vfs_recycle module.
96 * Caller must have become_root();
98 static bool quarantine_create_dir(
99 struct vfs_handle_struct *handle,
100 struct virusfilter_config *config,
101 const char *dname)
103 size_t len = 0;
104 size_t cat_len = 0;
105 char *new_dir = NULL;
106 char *tmp_str = NULL;
107 char *token = NULL;
108 char *tok_str = NULL;
109 bool status = false;
110 bool ok = false;
111 int ret = -1;
112 char *saveptr = NULL;
114 tmp_str = talloc_strdup(talloc_tos(), dname);
115 if (tmp_str == NULL) {
116 DBG_ERR("virusfilter-vfs: out of memory!\n");
117 errno = ENOMEM;
118 goto done;
120 tok_str = tmp_str;
122 len = strlen(dname)+1;
123 new_dir = (char *)talloc_size(talloc_tos(), len + 1);
124 if (new_dir == NULL) {
125 DBG_ERR("virusfilter-vfs: out of memory!\n");
126 errno = ENOMEM;
127 goto done;
129 *new_dir = '\0';
130 if (dname[0] == '/') {
131 /* Absolute path. */
132 cat_len = strlcat(new_dir, "/", len + 1);
133 if (cat_len >= len+1) {
134 goto done;
138 /* Create directory tree if necessary */
139 for (token = strtok_r(tok_str, "/", &saveptr);
140 token != NULL;
141 token = strtok_r(NULL, "/", &saveptr))
143 cat_len = strlcat(new_dir, token, len + 1);
144 if (cat_len >= len+1) {
145 goto done;
147 ok = quarantine_directory_exist(handle, new_dir);
148 if (ok == true) {
149 DBG_DEBUG("quarantine: dir %s already exists\n",
150 new_dir);
151 } else {
152 struct smb_filename *smb_fname = NULL;
154 DBG_INFO("quarantine: creating new dir %s\n", new_dir);
156 smb_fname = synthetic_smb_fname(talloc_tos(), new_dir,
157 NULL, NULL, 0);
158 if (smb_fname == NULL) {
159 goto done;
162 ret = SMB_VFS_NEXT_MKDIRAT(handle,
163 handle->conn->cwd_fsp,
164 smb_fname,
165 config->quarantine_dir_mode);
166 if (ret != 0) {
167 TALLOC_FREE(smb_fname);
169 DBG_WARNING("quarantine: mkdirat failed for %s "
170 "with error: %s\n", new_dir,
171 strerror(errno));
172 status = false;
173 goto done;
175 TALLOC_FREE(smb_fname);
177 cat_len = strlcat(new_dir, "/", len + 1);
178 if (cat_len >= len + 1) {
179 goto done;
183 status = true;
184 done:
185 TALLOC_FREE(tmp_str);
186 TALLOC_FREE(new_dir);
187 return status;
190 static int virusfilter_vfs_connect(
191 struct vfs_handle_struct *handle,
192 const char *svc,
193 const char *user)
195 int snum = SNUM(handle->conn);
196 struct virusfilter_config *config = NULL;
197 const char *exclude_files = NULL;
198 const char *temp_quarantine_dir_mode = NULL;
199 char *sret = NULL;
200 char *tmp = NULL;
201 enum virusfilter_scanner_enum backend;
202 int connect_timeout = 0;
203 int io_timeout = 0;
204 int ret = -1;
206 config = talloc_zero(handle, struct virusfilter_config);
207 if (config == NULL) {
208 DBG_ERR("talloc_zero failed\n");
209 return -1;
211 talloc_set_destructor(config, virusfilter_config_destructor);
213 SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
214 struct virusfilter_config, return -1);
216 config->scan_request_limit = lp_parm_int(
217 snum, "virusfilter", "scan request limit", 0);
219 config->scan_on_open = lp_parm_bool(
220 snum, "virusfilter", "scan on open", true);
222 config->scan_on_close = lp_parm_bool(
223 snum, "virusfilter", "scan on close", false);
225 config->max_nested_scan_archive = lp_parm_int(
226 snum, "virusfilter", "max nested scan archive", 1);
228 config->scan_archive = lp_parm_bool(
229 snum, "virusfilter", "scan archive", false);
231 config->scan_mime = lp_parm_bool(
232 snum, "virusfilter", "scan mime", false);
234 config->max_file_size = (ssize_t)lp_parm_ulong(
235 snum, "virusfilter", "max file size", 100000000L);
237 config->min_file_size = (ssize_t)lp_parm_ulong(
238 snum, "virusfilter", "min file size", 10);
240 exclude_files = lp_parm_const_string(
241 snum, "virusfilter", "exclude files", NULL);
242 if (exclude_files != NULL) {
243 set_namearray(&config->exclude_files, exclude_files);
246 config->cache_entry_limit = lp_parm_int(
247 snum, "virusfilter", "cache entry limit", 100);
249 config->cache_time_limit = lp_parm_int(
250 snum, "virusfilter", "cache time limit", 10);
252 config->infected_file_action = lp_parm_enum(
253 snum, "virusfilter", "infected file action",
254 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
256 config->infected_file_command = lp_parm_const_string(
257 snum, "virusfilter", "infected file command", NULL);
259 config->scan_error_command = lp_parm_const_string(
260 snum, "virusfilter", "scan error command", NULL);
262 config->block_access_on_error = lp_parm_bool(
263 snum, "virusfilter", "block access on error", false);
265 tmp = talloc_asprintf(config, "%s/.quarantine",
266 handle->conn->connectpath);
268 config->quarantine_dir = lp_parm_const_string(
269 snum, "virusfilter", "quarantine directory",
270 tmp ? tmp : "/tmp/.quarantine");
272 if (tmp != config->quarantine_dir) {
273 TALLOC_FREE(tmp);
276 temp_quarantine_dir_mode = lp_parm_const_string(
277 snum, "virusfilter", "quarantine directory mode", "0755");
278 if (temp_quarantine_dir_mode != NULL) {
279 unsigned int mode = 0;
280 sscanf(temp_quarantine_dir_mode, "%o", &mode);
281 config->quarantine_dir_mode = mode;
284 config->quarantine_prefix = lp_parm_const_string(
285 snum, "virusfilter", "quarantine prefix",
286 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
288 config->quarantine_suffix = lp_parm_const_string(
289 snum, "virusfilter", "quarantine suffix",
290 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
293 * Make sure prefixes and suffixes do not contain directory
294 * delimiters
296 sret = strstr(config->quarantine_prefix, "/");
297 if (sret != NULL) {
298 DBG_ERR("quarantine prefix must not contain directory "
299 "delimiter(s) such as '/' (%s replaced with %s)\n",
300 config->quarantine_prefix,
301 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
302 config->quarantine_prefix =
303 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
305 sret = strstr(config->quarantine_suffix, "/");
306 if (sret != NULL) {
307 DBG_ERR("quarantine suffix must not contain directory "
308 "delimiter(s) such as '/' (%s replaced with %s)\n",
309 config->quarantine_suffix,
310 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
311 config->quarantine_suffix =
312 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
315 config->quarantine_keep_tree = lp_parm_bool(
316 snum, "virusfilter", "quarantine keep tree", true);
318 config->quarantine_keep_name = lp_parm_bool(
319 snum, "virusfilter", "quarantine keep name", true);
321 config->rename_prefix = lp_parm_const_string(
322 snum, "virusfilter", "rename prefix",
323 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
325 config->rename_suffix = lp_parm_const_string(
326 snum, "virusfilter", "rename suffix",
327 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
330 * Make sure prefixes and suffixes do not contain directory
331 * delimiters
333 sret = strstr(config->rename_prefix, "/");
334 if (sret != NULL) {
335 DBG_ERR("rename prefix must not contain directory "
336 "delimiter(s) such as '/' (%s replaced with %s)\n",
337 config->rename_prefix,
338 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
339 config->rename_prefix =
340 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
342 sret = strstr(config->rename_suffix, "/");
343 if (sret != NULL) {
344 DBG_ERR("rename suffix must not contain directory "
345 "delimiter(s) such as '/' (%s replaced with %s)\n",
346 config->rename_suffix,
347 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
348 config->rename_suffix =
349 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
352 config->infected_open_errno = lp_parm_int(
353 snum, "virusfilter", "infected file errno on open", EACCES);
355 config->infected_close_errno = lp_parm_int(
356 snum, "virusfilter", "infected file errno on close", 0);
358 config->scan_error_open_errno = lp_parm_int(
359 snum, "virusfilter", "scan error errno on open", EACCES);
361 config->scan_error_close_errno = lp_parm_int(
362 snum, "virusfilter", "scan error errno on close", 0);
364 config->socket_path = lp_parm_const_string(
365 snum, "virusfilter", "socket path", NULL);
367 /* canonicalize socket_path */
368 if (config->socket_path != NULL && config->socket_path[0] != '/') {
369 DBG_ERR("socket path must be an absolute path. "
370 "Using backend default\n");
371 config->socket_path = NULL;
373 if (config->socket_path != NULL) {
374 canonicalize_absolute_path(handle,
375 config->socket_path);
378 connect_timeout = lp_parm_int(snum, "virusfilter",
379 "connect timeout", 30000);
381 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
383 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
384 if (config->io_h == NULL) {
385 DBG_ERR("virusfilter_io_new failed");
386 return -1;
389 if (config->cache_entry_limit > 0) {
390 config->cache = virusfilter_cache_new(handle,
391 config->cache_entry_limit,
392 config->cache_time_limit);
393 if (config->cache == NULL) {
394 DBG_ERR("Initializing cache failed: Cache disabled\n");
395 return -1;
400 * Check quarantine directory now to save processing
401 * and becoming root over and over.
403 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
404 bool ok = true;
405 bool dir_exists;
408 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
409 * hierarchy
411 become_root();
412 dir_exists = quarantine_directory_exist(handle,
413 config->quarantine_dir);
414 if (!dir_exists) {
415 DBG_DEBUG("Creating quarantine directory: %s\n",
416 config->quarantine_dir);
417 ok = quarantine_create_dir(handle, config,
418 config->quarantine_dir);
420 unbecome_root();
421 if (!ok) {
422 DBG_ERR("Creating quarantine directory %s "
423 "failed with %s\n",
424 config->quarantine_dir,
425 strerror(errno));
426 return -1;
431 * Now that the frontend options are initialized, load the configured
432 * backend.
435 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
436 "virusfilter",
437 "scanner",
438 scanner_list,
439 -1);
440 if (backend == (enum virusfilter_scanner_enum)-1) {
441 DBG_ERR("No AV-Scanner configured, "
442 "please set \"virusfilter:scanner\"\n");
443 return -1;
446 switch (backend) {
447 case VIRUSFILTER_SCANNER_SOPHOS:
448 ret = virusfilter_sophos_init(config);
449 break;
450 case VIRUSFILTER_SCANNER_FSAV:
451 ret = virusfilter_fsav_init(config);
452 break;
453 case VIRUSFILTER_SCANNER_CLAMAV:
454 ret = virusfilter_clamav_init(config);
455 break;
456 default:
457 DBG_ERR("Unhandled scanner %d\n", backend);
458 return -1;
460 if (ret != 0) {
461 DBG_ERR("Scanner backend init failed\n");
462 return -1;
465 if (config->backend->fns->connect != NULL) {
466 ret = config->backend->fns->connect(handle, config, svc, user);
467 if (ret == -1) {
468 return -1;
472 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
475 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
477 struct virusfilter_config *config = NULL;
479 SMB_VFS_HANDLE_GET_DATA(handle, config,
480 struct virusfilter_config, return);
482 if (config->backend->fns->disconnect != NULL) {
483 config->backend->fns->disconnect(handle);
486 free_namearray(config->exclude_files);
487 virusfilter_io_disconnect(config->io_h);
489 SMB_VFS_NEXT_DISCONNECT(handle);
492 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
493 struct virusfilter_config *config,
494 char **env_list)
496 int ret;
498 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
499 VIRUSFILTER_VERSION);
500 if (ret == -1) {
501 return -1;
503 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
504 config->backend->name);
505 if (ret == -1) {
506 return -1;
509 if (config->backend->version != 0) {
510 char *version = NULL;
512 version = talloc_asprintf(talloc_tos(), "%u",
513 config->backend->version);
514 if (version == NULL) {
515 return -1;
517 ret = virusfilter_env_set(mem_ctx, env_list,
518 "VIRUSFILTER_MODULE_VERSION",
519 version);
520 TALLOC_FREE(version);
521 if (ret == -1) {
522 return -1;
526 return 0;
529 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
530 struct vfs_handle_struct *handle,
531 struct virusfilter_config *config,
532 const struct smb_filename *smb_fname,
533 char *q_dir_in,
534 char *cwd_fname)
536 char *temp_path = NULL;
537 char *q_dir_out = NULL;
538 bool ok;
540 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
541 if (temp_path == NULL) {
542 DBG_ERR("talloc_asprintf failed\n");
543 goto out;
546 become_root();
547 ok = quarantine_directory_exist(handle, temp_path);
548 unbecome_root();
549 if (ok) {
550 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
551 q_dir_out = talloc_move(mem_ctx, &temp_path);
552 goto out;
555 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
557 become_root();
558 ok = quarantine_create_dir(handle, config, temp_path);
559 unbecome_root();
560 if (!ok) {
561 DBG_NOTICE("Could not create quarantine directory [%s], "
562 "ignoring for [%s]\n",
563 temp_path, smb_fname_str_dbg(smb_fname));
564 goto out;
567 q_dir_out = talloc_move(mem_ctx, &temp_path);
569 out:
570 TALLOC_FREE(temp_path);
571 return q_dir_out;
574 static virusfilter_action infected_file_action_quarantine(
575 struct vfs_handle_struct *handle,
576 struct virusfilter_config *config,
577 TALLOC_CTX *mem_ctx,
578 const struct files_struct *fsp,
579 const char **filepath_newp)
581 TALLOC_CTX *frame = talloc_stackframe();
582 connection_struct *conn = handle->conn;
583 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
584 char *fname = fsp->fsp_name->base_name;
585 const struct smb_filename *smb_fname = fsp->fsp_name;
586 struct smb_filename *q_smb_fname = NULL;
587 char *q_dir = NULL;
588 char *q_prefix = NULL;
589 char *q_suffix = NULL;
590 char *q_filepath = NULL;
591 char *dir_name = NULL;
592 const char *base_name = NULL;
593 char *rand_filename_component = NULL;
594 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
595 bool ok = false;
596 int ret = -1;
597 int saved_errno = 0;
599 q_dir = virusfilter_string_sub(frame, conn,
600 config->quarantine_dir);
601 q_prefix = virusfilter_string_sub(frame, conn,
602 config->quarantine_prefix);
603 q_suffix = virusfilter_string_sub(frame, conn,
604 config->quarantine_suffix);
605 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
606 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
607 "memory\n", cwd_fname, fname);
608 action = VIRUSFILTER_ACTION_DO_NOTHING;
609 goto out;
612 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
613 ok = parent_dirname(frame, smb_fname->base_name,
614 &dir_name, &base_name);
615 if (!ok) {
616 DBG_ERR("parent_dirname failed\n");
617 action = VIRUSFILTER_ACTION_DO_NOTHING;
618 goto out;
621 if (config->quarantine_keep_tree) {
622 char *tree = NULL;
624 tree = quarantine_check_tree(frame, handle, config,
625 smb_fname, q_dir,
626 cwd_fname);
627 if (tree == NULL) {
629 * If we can't create the tree, just move it
630 * into the toplevel quarantine dir.
632 tree = q_dir;
634 q_dir = tree;
638 /* Get a 16 byte + \0 random filename component. */
639 rand_filename_component = generate_random_str(frame, 16);
640 if (rand_filename_component == NULL) {
641 DBG_ERR("generate_random_str failed\n");
642 action = VIRUSFILTER_ACTION_DO_NOTHING;
643 goto out;
646 if (config->quarantine_keep_name) {
647 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
648 q_dir, q_prefix,
649 base_name, q_suffix,
650 rand_filename_component);
651 } else {
652 q_filepath = talloc_asprintf(frame, "%s/%s%s",
653 q_dir, q_prefix,
654 rand_filename_component);
656 if (q_filepath == NULL) {
657 DBG_ERR("talloc_asprintf failed\n");
658 action = VIRUSFILTER_ACTION_DO_NOTHING;
659 goto out;
662 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
663 smb_fname->stream_name,
664 NULL, smb_fname->flags);
665 if (q_smb_fname == NULL) {
666 action = VIRUSFILTER_ACTION_DO_NOTHING;
667 goto out;
670 become_root();
671 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
672 if (ret == -1) {
673 saved_errno = errno;
675 unbecome_root();
676 if (ret == -1) {
677 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
678 cwd_fname, fname, q_filepath, strerror(saved_errno));
679 errno = saved_errno;
680 action = VIRUSFILTER_ACTION_DO_NOTHING;
681 goto out;
684 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
686 out:
687 TALLOC_FREE(frame);
688 return action;
691 static virusfilter_action infected_file_action_rename(
692 struct vfs_handle_struct *handle,
693 struct virusfilter_config *config,
694 TALLOC_CTX *mem_ctx,
695 const struct files_struct *fsp,
696 const char **filepath_newp)
698 TALLOC_CTX *frame = talloc_stackframe();
699 connection_struct *conn = handle->conn;
700 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
701 char *fname = fsp->fsp_name->base_name;
702 const struct smb_filename *smb_fname = fsp->fsp_name;
703 struct smb_filename *q_smb_fname = NULL;
704 char *q_dir = NULL;
705 char *q_prefix = NULL;
706 char *q_suffix = NULL;
707 char *q_filepath = NULL;
708 const char *base_name = NULL;
709 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
710 bool ok = false;
711 int ret = -1;
712 int saved_errno = 0;
714 q_prefix = virusfilter_string_sub(frame, conn,
715 config->rename_prefix);
716 q_suffix = virusfilter_string_sub(frame, conn,
717 config->rename_suffix);
718 if (q_prefix == NULL || q_suffix == NULL) {
719 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
720 "memory\n", cwd_fname, fname);
721 action = VIRUSFILTER_ACTION_DO_NOTHING;
722 goto out;
725 ok = parent_dirname(frame, fname, &q_dir, &base_name);
726 if (!ok) {
727 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
728 "memory\n", cwd_fname, fname);
729 action = VIRUSFILTER_ACTION_DO_NOTHING;
730 goto out;
733 if (q_dir == NULL) {
734 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
735 "memory\n", cwd_fname, fname);
736 action = VIRUSFILTER_ACTION_DO_NOTHING;
737 goto out;
740 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
741 q_prefix, base_name, q_suffix);
743 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
744 smb_fname->stream_name, NULL,
745 smb_fname->flags);
746 if (q_smb_fname == NULL) {
747 action = VIRUSFILTER_ACTION_DO_NOTHING;
748 goto out;
751 become_root();
752 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
753 if (ret == -1) {
754 saved_errno = errno;
756 unbecome_root();
758 if (ret == -1) {
759 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
760 cwd_fname, fname, strerror(saved_errno));
761 errno = saved_errno;
762 action = VIRUSFILTER_ACTION_DO_NOTHING;
763 goto out;
766 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
768 out:
769 TALLOC_FREE(frame);
770 return action;
773 static virusfilter_action infected_file_action_delete(
774 struct vfs_handle_struct *handle,
775 const struct files_struct *fsp)
777 int ret;
778 int saved_errno = 0;
780 become_root();
781 ret = SMB_VFS_NEXT_UNLINKAT(handle,
782 handle->conn->cwd_fsp,
783 fsp->fsp_name,
785 if (ret == -1) {
786 saved_errno = errno;
788 unbecome_root();
789 if (ret == -1) {
790 DBG_ERR("Delete [%s/%s] failed: %s\n",
791 fsp->conn->cwd_fsp->fsp_name->base_name,
792 fsp->fsp_name->base_name,
793 strerror(saved_errno));
794 errno = saved_errno;
795 return VIRUSFILTER_ACTION_DO_NOTHING;
798 return VIRUSFILTER_ACTION_DELETE;
801 static virusfilter_action virusfilter_do_infected_file_action(
802 struct vfs_handle_struct *handle,
803 struct virusfilter_config *config,
804 TALLOC_CTX *mem_ctx,
805 const struct files_struct *fsp,
806 const char **filepath_newp)
808 virusfilter_action action;
810 *filepath_newp = NULL;
812 switch (config->infected_file_action) {
813 case VIRUSFILTER_ACTION_RENAME:
814 action = infected_file_action_rename(handle, config, mem_ctx,
815 fsp, filepath_newp);
816 break;
818 case VIRUSFILTER_ACTION_QUARANTINE:
819 action = infected_file_action_quarantine(handle, config, mem_ctx,
820 fsp, filepath_newp);
821 break;
823 case VIRUSFILTER_ACTION_DELETE:
824 action = infected_file_action_delete(handle, fsp);
825 break;
827 case VIRUSFILTER_ACTION_DO_NOTHING:
828 default:
829 action = VIRUSFILTER_ACTION_DO_NOTHING;
830 break;
833 return action;
836 static virusfilter_action virusfilter_treat_infected_file(
837 struct vfs_handle_struct *handle,
838 struct virusfilter_config *config,
839 const struct files_struct *fsp,
840 const char *report,
841 bool is_cache)
843 connection_struct *conn = handle->conn;
844 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
845 char *fname = fsp->fsp_name->base_name;
846 TALLOC_CTX *mem_ctx = talloc_tos();
847 int i;
848 virusfilter_action action;
849 const char *action_name = "UNKNOWN";
850 const char *filepath_q = NULL;
851 char *env_list = NULL;
852 char *command = NULL;
853 int command_result;
854 int ret;
856 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
857 fsp, &filepath_q);
858 for (i=0; virusfilter_actions[i].name; i++) {
859 if (virusfilter_actions[i].value == action) {
860 action_name = virusfilter_actions[i].name;
861 break;
864 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
865 fname, action_name);
867 if (!config->infected_file_command) {
868 return action;
871 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
872 if (ret == -1) {
873 goto done;
875 ret = virusfilter_env_set(mem_ctx, &env_list,
876 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
877 fname);
878 if (ret == -1) {
879 goto done;
881 if (report != NULL) {
882 ret = virusfilter_env_set(mem_ctx, &env_list,
883 "VIRUSFILTER_INFECTED_FILE_REPORT",
884 report);
885 if (ret == -1) {
886 goto done;
889 ret = virusfilter_env_set(mem_ctx, &env_list,
890 "VIRUSFILTER_INFECTED_FILE_ACTION",
891 action_name);
892 if (ret == -1) {
893 goto done;
895 if (filepath_q != NULL) {
896 ret = virusfilter_env_set(mem_ctx, &env_list,
897 "VIRUSFILTER_QUARANTINED_FILE_PATH",
898 filepath_q);
899 if (ret == -1) {
900 goto done;
903 if (is_cache) {
904 ret = virusfilter_env_set(mem_ctx, &env_list,
905 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
906 if (ret == -1) {
907 goto done;
911 command = virusfilter_string_sub(mem_ctx, conn,
912 config->infected_file_command);
913 if (command == NULL) {
914 DBG_ERR("virusfilter_string_sub failed\n");
915 goto done;
918 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
919 fname, command);
921 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
922 conn, true);
923 if (command_result != 0) {
924 DBG_ERR("Infected file command failed: %d\n", command_result);
927 DBG_DEBUG("Infected file command finished: %d\n", command_result);
929 done:
930 TALLOC_FREE(env_list);
931 TALLOC_FREE(command);
933 return action;
936 static void virusfilter_treat_scan_error(
937 struct vfs_handle_struct *handle,
938 struct virusfilter_config *config,
939 const struct files_struct *fsp,
940 const char *report,
941 bool is_cache)
943 connection_struct *conn = handle->conn;
944 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
945 const char *fname = fsp->fsp_name->base_name;
946 TALLOC_CTX *mem_ctx = talloc_tos();
947 char *env_list = NULL;
948 char *command = NULL;
949 int command_result;
950 int ret;
952 if (!config->scan_error_command) {
953 return;
955 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
956 if (ret == -1) {
957 goto done;
959 ret = virusfilter_env_set(mem_ctx, &env_list,
960 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
961 fname);
962 if (ret == -1) {
963 goto done;
965 if (report != NULL) {
966 ret = virusfilter_env_set(mem_ctx, &env_list,
967 "VIRUSFILTER_SCAN_ERROR_REPORT",
968 report);
969 if (ret == -1) {
970 goto done;
973 if (is_cache) {
974 ret = virusfilter_env_set(mem_ctx, &env_list,
975 "VIRUSFILTER_RESULT_IS_CACHE", "1");
976 if (ret == -1) {
977 goto done;
981 command = virusfilter_string_sub(mem_ctx, conn,
982 config->scan_error_command);
983 if (command == NULL) {
984 DBG_ERR("virusfilter_string_sub failed\n");
985 goto done;
988 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
989 fname, command);
991 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
992 conn, true);
993 if (command_result != 0) {
994 DBG_ERR("Scan error command failed: %d\n", command_result);
997 done:
998 TALLOC_FREE(env_list);
999 TALLOC_FREE(command);
1002 static virusfilter_result virusfilter_scan(
1003 struct vfs_handle_struct *handle,
1004 struct virusfilter_config *config,
1005 const struct files_struct *fsp)
1007 virusfilter_result scan_result;
1008 char *scan_report = NULL;
1009 const char *fname = fsp->fsp_name->base_name;
1010 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1011 struct virusfilter_cache_entry *scan_cache_e = NULL;
1012 bool is_cache = false;
1013 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1014 bool add_scan_cache = true;
1015 bool ok = false;
1017 if (config->cache) {
1018 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1019 scan_cache_e = virusfilter_cache_get(config->cache,
1020 cwd_fname, fname);
1021 if (scan_cache_e != NULL) {
1022 DBG_DEBUG("Cache entry found: cached result: %d\n",
1023 scan_cache_e->result);
1024 is_cache = true;
1025 scan_result = scan_cache_e->result;
1026 scan_report = scan_cache_e->report;
1027 goto virusfilter_scan_result_eval;
1029 DBG_DEBUG("Cache entry not found\n");
1032 if (config->backend->fns->scan_init != NULL) {
1033 scan_result = config->backend->fns->scan_init(config);
1034 if (scan_result != VIRUSFILTER_RESULT_OK) {
1035 scan_result = VIRUSFILTER_RESULT_ERROR;
1036 scan_report = talloc_asprintf(
1037 talloc_tos(),
1038 "Initializing scanner failed");
1039 goto virusfilter_scan_result_eval;
1043 scan_result = config->backend->fns->scan(handle, config, fsp,
1044 &scan_report);
1046 if (config->backend->fns->scan_end != NULL) {
1047 bool scan_end = true;
1049 if (config->scan_request_limit > 0) {
1050 scan_end = false;
1051 config->scan_request_count++;
1052 if (config->scan_request_count >=
1053 config->scan_request_limit)
1055 scan_end = true;
1056 config->scan_request_count = 0;
1059 if (scan_end) {
1060 config->backend->fns->scan_end(config);
1064 virusfilter_scan_result_eval:
1066 switch (scan_result) {
1067 case VIRUSFILTER_RESULT_CLEAN:
1068 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1069 break;
1071 case VIRUSFILTER_RESULT_INFECTED:
1072 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1073 cwd_fname, fname, scan_report ? scan_report :
1074 "infected (memory error on report)");
1075 file_action = virusfilter_treat_infected_file(handle,
1076 config, fsp, scan_report, is_cache);
1077 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1078 add_scan_cache = false;
1080 break;
1082 case VIRUSFILTER_RESULT_SUSPECTED:
1083 if (!config->block_suspected_file) {
1084 break;
1086 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1087 cwd_fname, fname, scan_report ? scan_report :
1088 "suspected infection (memory error on report)");
1089 file_action = virusfilter_treat_infected_file(handle,
1090 config, fsp, scan_report, is_cache);
1091 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1092 add_scan_cache = false;
1094 break;
1096 case VIRUSFILTER_RESULT_ERROR:
1097 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1098 cwd_fname, fname, scan_report ? scan_report :
1099 "error (memory error on report)");
1100 virusfilter_treat_scan_error(handle, config, fsp,
1101 scan_report, is_cache);
1102 add_scan_cache = false;
1103 break;
1105 default:
1106 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1107 scan_result, cwd_fname, fname, scan_report ?
1108 scan_report : "Unknown (memory error on report)");
1109 virusfilter_treat_scan_error(handle, config, fsp,
1110 scan_report, is_cache);
1111 add_scan_cache = false;
1112 break;
1115 if (config->cache) {
1116 if (!is_cache && add_scan_cache) {
1117 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1118 scan_result);
1119 ok = virusfilter_cache_entry_add(
1120 config->cache, cwd_fname, fname,
1121 scan_result, scan_report);
1122 if (!ok) {
1123 DBG_ERR("Cannot create cache entry: "
1124 "virusfilter_cache_entry_new failed");
1125 goto virusfilter_scan_return;
1127 } else if (is_cache) {
1128 virusfilter_cache_entry_free(scan_cache_e);
1132 virusfilter_scan_return:
1133 return scan_result;
1136 static int virusfilter_vfs_open(
1137 struct vfs_handle_struct *handle,
1138 struct smb_filename *smb_fname,
1139 files_struct *fsp,
1140 int flags,
1141 mode_t mode)
1143 TALLOC_CTX *mem_ctx = talloc_tos();
1144 struct virusfilter_config *config;
1145 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1146 virusfilter_result scan_result;
1147 const char *fname = fsp->fsp_name->base_name;
1148 char *dir_name = NULL;
1149 const char *base_name = NULL;
1150 int scan_errno = 0;
1151 size_t test_prefix;
1152 size_t test_suffix;
1153 int rename_trap_count = 0;
1154 int ret;
1155 bool ok1;
1156 char *sret = NULL;
1158 SMB_VFS_HANDLE_GET_DATA(handle, config,
1159 struct virusfilter_config, return -1);
1161 if (fsp->is_directory) {
1162 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1163 goto virusfilter_vfs_open_next;
1166 test_prefix = strlen(config->rename_prefix);
1167 test_suffix = strlen(config->rename_suffix);
1168 if (test_prefix > 0) {
1169 rename_trap_count++;
1171 if (test_suffix > 0) {
1172 rename_trap_count++;
1175 if (is_named_stream(smb_fname)) {
1176 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1177 " %s/%s\n", cwd_fname, fname);
1178 goto virusfilter_vfs_open_next;
1181 if (!config->scan_on_open) {
1182 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1183 cwd_fname, fname);
1184 goto virusfilter_vfs_open_next;
1187 if (flags & O_TRUNC) {
1188 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1189 cwd_fname, fname);
1190 goto virusfilter_vfs_open_next;
1193 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1194 if (ret != 0) {
1197 * Do not return immediately if !(flags & O_CREAT) &&
1198 * errno != ENOENT.
1199 * Do not do this here or anywhere else. The module is
1200 * stackable and there may be modules below, such as audit
1201 * modules, which should be handled.
1203 goto virusfilter_vfs_open_next;
1205 ret = S_ISREG(smb_fname->st.st_ex_mode);
1206 if (ret == 0) {
1207 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1208 cwd_fname, fname);
1209 goto virusfilter_vfs_open_next;
1211 if (config->max_file_size > 0 &&
1212 smb_fname->st.st_ex_size > config->max_file_size)
1214 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1215 cwd_fname, fname);
1216 goto virusfilter_vfs_open_next;
1218 if (config->min_file_size > 0 &&
1219 smb_fname->st.st_ex_size < config->min_file_size)
1221 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1222 cwd_fname, fname);
1223 goto virusfilter_vfs_open_next;
1226 ok1 = is_in_path(fname, config->exclude_files, false);
1227 if (config->exclude_files && ok1)
1229 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1230 cwd_fname, fname);
1231 goto virusfilter_vfs_open_next;
1234 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1235 sret = strstr_m(fname, config->quarantine_dir);
1236 if (sret != NULL) {
1237 scan_errno = config->infected_open_errno;
1238 goto virusfilter_vfs_open_fail;
1242 if (test_prefix > 0 || test_suffix > 0) {
1243 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1244 if (ok1)
1246 if (test_prefix > 0) {
1247 ret = strncmp(base_name,
1248 config->rename_prefix, test_prefix);
1249 if (ret != 0) {
1250 test_prefix = 0;
1253 if (test_suffix > 0) {
1254 ret = strcmp(base_name + (strlen(base_name)
1255 - test_suffix),
1256 config->rename_suffix);
1257 if (ret != 0) {
1258 test_suffix = 0;
1262 TALLOC_FREE(dir_name);
1264 if ((rename_trap_count == 2 && test_prefix &&
1265 test_suffix) || (rename_trap_count == 1 &&
1266 (test_prefix || test_suffix)))
1268 scan_errno =
1269 config->infected_open_errno;
1270 goto virusfilter_vfs_open_fail;
1275 scan_result = virusfilter_scan(handle, config, fsp);
1277 switch (scan_result) {
1278 case VIRUSFILTER_RESULT_CLEAN:
1279 break;
1280 case VIRUSFILTER_RESULT_INFECTED:
1281 scan_errno = config->infected_open_errno;
1282 goto virusfilter_vfs_open_fail;
1283 case VIRUSFILTER_RESULT_ERROR:
1284 if (config->block_access_on_error) {
1285 DBG_INFO("Block access\n");
1286 scan_errno = config->scan_error_open_errno;
1287 goto virusfilter_vfs_open_fail;
1289 break;
1290 default:
1291 scan_errno = config->scan_error_open_errno;
1292 goto virusfilter_vfs_open_fail;
1295 virusfilter_vfs_open_next:
1296 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1298 virusfilter_vfs_open_fail:
1299 errno = (scan_errno != 0) ? scan_errno : EACCES;
1300 return -1;
1303 static int virusfilter_vfs_close(
1304 struct vfs_handle_struct *handle,
1305 files_struct *fsp)
1308 * The name of this variable is for consistency. If API changes to
1309 * match _open change to cwd_fname as in virusfilter_vfs_open.
1311 const char *cwd_fname = handle->conn->connectpath;
1313 struct virusfilter_config *config = NULL;
1314 char *fname = fsp->fsp_name->base_name;
1315 int close_result = -1;
1316 int close_errno = 0;
1317 virusfilter_result scan_result;
1318 int scan_errno = 0;
1320 SMB_VFS_HANDLE_GET_DATA(handle, config,
1321 struct virusfilter_config, return -1);
1324 * Must close after scan? It appears not as the scanners are not
1325 * internal and other modules such as greyhole seem to do
1326 * SMB_VFS_NEXT_* functions before processing.
1328 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1329 if (close_result == -1) {
1330 close_errno = errno;
1334 * Return immediately if close_result == -1, and close_errno == EBADF.
1335 * If close failed, file likely doesn't exist, do not try to scan.
1337 if (close_result == -1 && close_errno == EBADF) {
1338 if (fsp->modified) {
1339 DBG_DEBUG("Removing cache entry (if existent): "
1340 "fname: %s\n", fname);
1341 virusfilter_cache_remove(config->cache,
1342 cwd_fname, fname);
1344 goto virusfilter_vfs_close_fail;
1347 if (fsp->is_directory) {
1348 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1349 return close_result;
1352 if (is_named_stream(fsp->fsp_name)) {
1353 if (config->scan_on_open && fsp->modified) {
1354 if (config->cache) {
1355 DBG_DEBUG("Removing cache entry (if existent)"
1356 ": fname: %s\n", fname);
1357 virusfilter_cache_remove(
1358 config->cache,
1359 cwd_fname, fname);
1362 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1363 " %s/%s\n", cwd_fname, fname);
1364 return close_result;
1367 if (!config->scan_on_close) {
1368 if (config->scan_on_open && fsp->modified) {
1369 if (config->cache) {
1370 DBG_DEBUG("Removing cache entry (if existent)"
1371 ": fname: %s\n", fname);
1372 virusfilter_cache_remove(
1373 config->cache,
1374 cwd_fname, fname);
1377 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1378 cwd_fname, fname);
1379 return close_result;
1382 if (!fsp->modified) {
1383 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1384 cwd_fname, fname);
1386 return close_result;
1389 if (config->exclude_files && is_in_path(fname,
1390 config->exclude_files, false))
1392 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1393 cwd_fname, fname);
1394 return close_result;
1397 scan_result = virusfilter_scan(handle, config, fsp);
1399 switch (scan_result) {
1400 case VIRUSFILTER_RESULT_CLEAN:
1401 break;
1402 case VIRUSFILTER_RESULT_INFECTED:
1403 scan_errno = config->infected_close_errno;
1404 goto virusfilter_vfs_close_fail;
1405 case VIRUSFILTER_RESULT_ERROR:
1406 if (config->block_access_on_error) {
1407 DBG_INFO("Block access\n");
1408 scan_errno = config->scan_error_close_errno;
1409 goto virusfilter_vfs_close_fail;
1411 break;
1412 default:
1413 scan_errno = config->scan_error_close_errno;
1414 goto virusfilter_vfs_close_fail;
1417 if (close_errno != 0) {
1418 errno = close_errno;
1421 return close_result;
1423 virusfilter_vfs_close_fail:
1425 errno = (scan_errno != 0) ? scan_errno : close_errno;
1427 return close_result;
1430 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
1431 struct files_struct *dirfsp,
1432 const struct smb_filename *smb_fname,
1433 int flags)
1435 int ret = SMB_VFS_NEXT_UNLINKAT(handle,
1436 dirfsp,
1437 smb_fname,
1438 flags);
1439 struct virusfilter_config *config = NULL;
1440 char *fname = NULL;
1441 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1443 if (ret != 0 && errno != ENOENT) {
1444 return ret;
1447 SMB_VFS_HANDLE_GET_DATA(handle, config,
1448 struct virusfilter_config, return -1);
1450 if (config->cache == NULL) {
1451 return 0;
1454 fname = smb_fname->base_name;
1456 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1457 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1459 return 0;
1462 static int virusfilter_vfs_renameat(
1463 struct vfs_handle_struct *handle,
1464 files_struct *srcfsp,
1465 const struct smb_filename *smb_fname_src,
1466 files_struct *dstfsp,
1467 const struct smb_filename *smb_fname_dst)
1469 int ret = SMB_VFS_NEXT_RENAMEAT(handle,
1470 srcfsp,
1471 smb_fname_src,
1472 dstfsp,
1473 smb_fname_dst);
1474 struct virusfilter_config *config = NULL;
1475 char *fname = NULL;
1476 char *dst_fname = NULL;
1477 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1479 if (ret != 0) {
1480 return ret;
1483 SMB_VFS_HANDLE_GET_DATA(handle, config,
1484 struct virusfilter_config, return -1);
1486 if (config->cache == NULL) {
1487 return 0;
1490 fname = smb_fname_src->base_name;
1491 dst_fname = smb_fname_dst->base_name;
1493 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1494 fname, dst_fname);
1495 virusfilter_cache_entry_rename(config->cache,
1496 cwd_fname, fname,
1497 dst_fname);
1499 return 0;
1503 /* VFS operations */
1504 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1505 .connect_fn = virusfilter_vfs_connect,
1506 .disconnect_fn = virusfilter_vfs_disconnect,
1507 .open_fn = virusfilter_vfs_open,
1508 .close_fn = virusfilter_vfs_close,
1509 .unlinkat_fn = virusfilter_vfs_unlinkat,
1510 .renameat_fn = virusfilter_vfs_renameat,
1513 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1514 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1516 NTSTATUS status;
1518 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1519 "virusfilter",
1520 &vfs_virusfilter_fns);
1521 if (!NT_STATUS_IS_OK(status)) {
1522 return status;
1525 virusfilter_debug_class = debug_add_class("virusfilter");
1526 if (virusfilter_debug_class == -1) {
1527 virusfilter_debug_class = DBGC_VFS;
1528 DBG_ERR("Couldn't register custom debugging class!\n");
1529 } else {
1530 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1533 DBG_INFO("registered\n");
1535 return status;