s4:selftest: explicitly set NSS/RESOLV_WAPPER_* in wait_for_start
[Samba.git] / source3 / modules / vfs_virusfilter.c
blobef9dc78263be45dcd326b09b39cdad7d2e490680
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 neccessary */
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_MKDIR(handle,
163 smb_fname,
164 config->quarantine_dir_mode);
165 if (ret != 0) {
166 TALLOC_FREE(smb_fname);
168 DBG_WARNING("quarantine: mkdir failed for %s "
169 "with error: %s\n", new_dir,
170 strerror(errno));
171 status = false;
172 goto done;
174 TALLOC_FREE(smb_fname);
176 cat_len = strlcat(new_dir, "/", len + 1);
177 if (cat_len >= len + 1) {
178 goto done;
182 status = true;
183 done:
184 TALLOC_FREE(tmp_str);
185 TALLOC_FREE(new_dir);
186 return status;
189 static int virusfilter_vfs_connect(
190 struct vfs_handle_struct *handle,
191 const char *svc,
192 const char *user)
194 int snum = SNUM(handle->conn);
195 struct virusfilter_config *config = NULL;
196 const char *exclude_files = NULL;
197 const char *temp_quarantine_dir_mode = NULL;
198 char *sret = NULL;
199 char *tmp = NULL;
200 enum virusfilter_scanner_enum backend;
201 int connect_timeout = 0;
202 int io_timeout = 0;
203 int ret = -1;
205 config = talloc_zero(handle, struct virusfilter_config);
206 if (config == NULL) {
207 DBG_ERR("talloc_zero failed\n");
208 return -1;
210 talloc_set_destructor(config, virusfilter_config_destructor);
212 SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
213 struct virusfilter_config, return -1);
215 config->scan_request_limit = lp_parm_int(
216 snum, "virusfilter", "scan request limit", 0);
218 config->scan_on_open = lp_parm_bool(
219 snum, "virusfilter", "scan on open", true);
221 config->scan_on_close = lp_parm_bool(
222 snum, "virusfilter", "scan on close", false);
224 config->max_nested_scan_archive = lp_parm_int(
225 snum, "virusfilter", "max nested scan archive", 1);
227 config->scan_archive = lp_parm_bool(
228 snum, "virusfilter", "scan archive", false);
230 config->scan_mime = lp_parm_bool(
231 snum, "virusfilter", "scan mime", false);
233 config->max_file_size = (ssize_t)lp_parm_ulong(
234 snum, "virusfilter", "max file size", 100000000L);
236 config->min_file_size = (ssize_t)lp_parm_ulong(
237 snum, "virusfilter", "min file size", 10);
239 exclude_files = lp_parm_const_string(
240 snum, "virusfilter", "exclude files", NULL);
241 if (exclude_files != NULL) {
242 set_namearray(&config->exclude_files, exclude_files);
245 config->cache_entry_limit = lp_parm_int(
246 snum, "virusfilter", "cache entry limit", 100);
248 config->cache_time_limit = lp_parm_int(
249 snum, "virusfilter", "cache time limit", 10);
251 config->infected_file_action = lp_parm_enum(
252 snum, "virusfilter", "infected file action",
253 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
255 config->infected_file_command = lp_parm_const_string(
256 snum, "virusfilter", "infected file command", NULL);
258 config->scan_error_command = lp_parm_const_string(
259 snum, "virusfilter", "scan error command", NULL);
261 config->block_access_on_error = lp_parm_bool(
262 snum, "virusfilter", "block access on error", false);
264 tmp = talloc_asprintf(config, "%s/.quarantine",
265 handle->conn->connectpath);
267 config->quarantine_dir = lp_parm_const_string(
268 snum, "virusfilter", "quarantine directory",
269 tmp ? tmp : "/tmp/.quarantine");
271 if (tmp != config->quarantine_dir) {
272 TALLOC_FREE(tmp);
275 temp_quarantine_dir_mode = lp_parm_const_string(
276 snum, "virusfilter", "quarantine directory mode", "0755");
277 if (temp_quarantine_dir_mode != NULL) {
278 sscanf(temp_quarantine_dir_mode, "%o",
279 &config->quarantine_dir_mode);
282 config->quarantine_prefix = lp_parm_const_string(
283 snum, "virusfilter", "quarantine prefix",
284 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
286 config->quarantine_suffix = lp_parm_const_string(
287 snum, "virusfilter", "quarantine suffix",
288 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
291 * Make sure prefixes and suffixes do not contain directory
292 * delimiters
294 sret = strstr(config->quarantine_prefix, "/");
295 if (sret != NULL) {
296 DBG_ERR("quarantine prefix must not contain directory "
297 "delimiter(s) such as '/' (%s replaced with %s)\n",
298 config->quarantine_prefix,
299 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
300 config->quarantine_prefix =
301 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
303 sret = strstr(config->quarantine_suffix, "/");
304 if (sret != NULL) {
305 DBG_ERR("quarantine suffix must not contain directory "
306 "delimiter(s) such as '/' (%s replaced with %s)\n",
307 config->quarantine_suffix,
308 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
309 config->quarantine_suffix =
310 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
313 config->quarantine_keep_tree = lp_parm_bool(
314 snum, "virusfilter", "quarantine keep tree", true);
316 config->quarantine_keep_name = lp_parm_bool(
317 snum, "virusfilter", "quarantine keep name", true);
319 config->rename_prefix = lp_parm_const_string(
320 snum, "virusfilter", "rename prefix",
321 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
323 config->rename_suffix = lp_parm_const_string(
324 snum, "virusfilter", "rename suffix",
325 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
328 * Make sure prefixes and suffixes do not contain directory
329 * delimiters
331 sret = strstr(config->rename_prefix, "/");
332 if (sret != NULL) {
333 DBG_ERR("rename prefix must not contain directory "
334 "delimiter(s) such as '/' (%s replaced with %s)\n",
335 config->rename_prefix,
336 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
337 config->rename_prefix =
338 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
340 sret = strstr(config->rename_suffix, "/");
341 if (sret != NULL) {
342 DBG_ERR("rename suffix must not contain directory "
343 "delimiter(s) such as '/' (%s replaced with %s)\n",
344 config->rename_suffix,
345 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
346 config->rename_suffix =
347 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
350 config->infected_open_errno = lp_parm_int(
351 snum, "virusfilter", "infected file errno on open", EACCES);
353 config->infected_close_errno = lp_parm_int(
354 snum, "virusfilter", "infected file errno on close", 0);
356 config->scan_error_open_errno = lp_parm_int(
357 snum, "virusfilter", "scan error errno on open", EACCES);
359 config->scan_error_close_errno = lp_parm_int(
360 snum, "virusfilter", "scan error errno on close", 0);
362 config->socket_path = lp_parm_const_string(
363 snum, "virusfilter", "socket path", NULL);
365 /* canonicalize socket_path */
366 if (config->socket_path != NULL && config->socket_path[0] != '/') {
367 DBG_ERR("socket path must be an absolute path. "
368 "Using backend default\n");
369 config->socket_path = NULL;
371 if (config->socket_path != NULL) {
372 canonicalize_absolute_path(handle,
373 config->socket_path);
376 connect_timeout = lp_parm_int(snum, "virusfilter",
377 "connect timeout", 30000);
379 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
381 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
382 if (config->io_h == NULL) {
383 DBG_ERR("virusfilter_io_new failed");
384 return -1;
387 if (config->cache_entry_limit > 0) {
388 config->cache = virusfilter_cache_new(handle,
389 config->cache_entry_limit,
390 config->cache_time_limit);
391 if (config->cache == NULL) {
392 DBG_ERR("Initializing cache failed: Cache disabled\n");
393 return -1;
398 * Check quarantine directory now to save processing
399 * and becoming root over and over.
401 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
402 bool ok = true;
403 bool dir_exists;
406 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
407 * hierarchy
409 become_root();
410 dir_exists = quarantine_directory_exist(handle,
411 config->quarantine_dir);
412 if (!dir_exists) {
413 DBG_DEBUG("Creating quarantine directory: %s\n",
414 config->quarantine_dir);
415 ok = quarantine_create_dir(handle, config,
416 config->quarantine_dir);
418 unbecome_root();
419 if (!ok) {
420 DBG_ERR("Creating quarantine directory %s "
421 "failed with %s\n",
422 config->quarantine_dir,
423 strerror(errno));
424 return -1;
429 * Now that the frontend options are initialized, load the configured
430 * backend.
433 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
434 "virusfilter",
435 "scanner",
436 scanner_list,
437 -1);
438 if (backend == (enum virusfilter_scanner_enum)-1) {
439 DBG_ERR("No AV-Scanner configured, "
440 "please set \"virusfilter:scanner\"\n");
441 return -1;
444 switch (backend) {
445 case VIRUSFILTER_SCANNER_SOPHOS:
446 ret = virusfilter_sophos_init(config);
447 break;
448 case VIRUSFILTER_SCANNER_FSAV:
449 ret = virusfilter_fsav_init(config);
450 break;
451 case VIRUSFILTER_SCANNER_CLAMAV:
452 ret = virusfilter_clamav_init(config);
453 break;
454 default:
455 DBG_ERR("Unhandled scanner %d\n", backend);
456 return -1;
458 if (ret != 0) {
459 DBG_ERR("Scanner backend init failed\n");
460 return -1;
463 if (config->backend->fns->connect != NULL) {
464 ret = config->backend->fns->connect(handle, config, svc, user);
465 if (ret == -1) {
466 return -1;
470 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
473 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
475 struct virusfilter_config *config = NULL;
477 SMB_VFS_HANDLE_GET_DATA(handle, config,
478 struct virusfilter_config, return);
480 if (config->backend->fns->disconnect != NULL) {
481 config->backend->fns->disconnect(handle);
484 free_namearray(config->exclude_files);
485 virusfilter_io_disconnect(config->io_h);
487 SMB_VFS_NEXT_DISCONNECT(handle);
490 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
491 struct virusfilter_config *config,
492 char **env_list)
494 int ret;
496 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
497 VIRUSFILTER_VERSION);
498 if (ret == -1) {
499 return -1;
501 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
502 config->backend->name);
503 if (ret == -1) {
504 return -1;
507 if (config->backend->version != 0) {
508 char *version = NULL;
510 version = talloc_asprintf(talloc_tos(), "%u",
511 config->backend->version);
512 if (version == NULL) {
513 return -1;
515 ret = virusfilter_env_set(mem_ctx, env_list,
516 "VIRUSFILTER_MODULE_VERSION",
517 version);
518 TALLOC_FREE(version);
519 if (ret == -1) {
520 return -1;
524 return 0;
527 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
528 struct vfs_handle_struct *handle,
529 struct virusfilter_config *config,
530 const struct smb_filename *smb_fname,
531 char *q_dir_in,
532 char *cwd_fname)
534 char *temp_path = NULL;
535 char *q_dir_out = NULL;
536 bool ok;
538 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
539 if (temp_path == NULL) {
540 DBG_ERR("talloc_asprintf failed\n");
541 goto out;
544 become_root();
545 ok = quarantine_directory_exist(handle, temp_path);
546 unbecome_root();
547 if (ok) {
548 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
549 q_dir_out = talloc_move(mem_ctx, &temp_path);
550 goto out;
553 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
555 become_root();
556 ok = quarantine_create_dir(handle, config, temp_path);
557 unbecome_root();
558 if (!ok) {
559 DBG_NOTICE("Could not create quarantine directory [%s], "
560 "ignoring for [%s]\n",
561 temp_path, smb_fname_str_dbg(smb_fname));
562 goto out;
565 q_dir_out = talloc_move(mem_ctx, &temp_path);
567 out:
568 TALLOC_FREE(temp_path);
569 return q_dir_out;
572 static virusfilter_action infected_file_action_quarantine(
573 struct vfs_handle_struct *handle,
574 struct virusfilter_config *config,
575 TALLOC_CTX *mem_ctx,
576 const struct files_struct *fsp,
577 const char **filepath_newp)
579 TALLOC_CTX *frame = talloc_stackframe();
580 connection_struct *conn = handle->conn;
581 char *cwd_fname = fsp->conn->cwd_fname->base_name;
582 char *fname = fsp->fsp_name->base_name;
583 const struct smb_filename *smb_fname = fsp->fsp_name;
584 struct smb_filename *q_smb_fname = NULL;
585 char *q_dir = NULL;
586 char *q_prefix = NULL;
587 char *q_suffix = NULL;
588 char *q_filepath = NULL;
589 char *dir_name = NULL;
590 const char *base_name = NULL;
591 char *rand_filename_component = NULL;
592 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
593 bool ok = false;
594 int ret = -1;
595 int saved_errno = 0;
597 q_dir = virusfilter_string_sub(frame, conn,
598 config->quarantine_dir);
599 q_prefix = virusfilter_string_sub(frame, conn,
600 config->quarantine_prefix);
601 q_suffix = virusfilter_string_sub(frame, conn,
602 config->quarantine_suffix);
603 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
604 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
605 "memory\n", cwd_fname, fname);
606 action = VIRUSFILTER_ACTION_DO_NOTHING;
607 goto out;
610 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
611 ok = parent_dirname(frame, smb_fname->base_name,
612 &dir_name, &base_name);
613 if (!ok) {
614 DBG_ERR("parent_dirname failed\n");
615 action = VIRUSFILTER_ACTION_DO_NOTHING;
616 goto out;
619 if (config->quarantine_keep_tree) {
620 char *tree = NULL;
622 tree = quarantine_check_tree(frame, handle, config,
623 smb_fname, q_dir,
624 cwd_fname);
625 if (tree == NULL) {
627 * If we can't create the tree, just move it
628 * into the toplevel quarantine dir.
630 tree = q_dir;
632 q_dir = tree;
636 /* Get a 16 byte + \0 random filename component. */
637 rand_filename_component = generate_random_str(frame, 16);
638 if (rand_filename_component == NULL) {
639 DBG_ERR("generate_random_str failed\n");
640 action = VIRUSFILTER_ACTION_DO_NOTHING;
641 goto out;
644 if (config->quarantine_keep_name) {
645 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
646 q_dir, q_prefix,
647 base_name, q_suffix,
648 rand_filename_component);
649 } else {
650 q_filepath = talloc_asprintf(frame, "%s/%s%s",
651 q_dir, q_prefix,
652 rand_filename_component);
654 if (q_filepath == NULL) {
655 DBG_ERR("talloc_asprintf failed\n");
656 action = VIRUSFILTER_ACTION_DO_NOTHING;
657 goto out;
660 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
661 smb_fname->stream_name,
662 NULL, smb_fname->flags);
663 if (q_smb_fname == NULL) {
664 action = VIRUSFILTER_ACTION_DO_NOTHING;
665 goto out;
668 become_root();
669 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
670 if (ret == -1) {
671 saved_errno = errno;
673 unbecome_root();
674 if (ret == -1) {
675 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
676 cwd_fname, fname, q_filepath, strerror(saved_errno));
677 errno = saved_errno;
678 action = VIRUSFILTER_ACTION_DO_NOTHING;
679 goto out;
682 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
684 out:
685 TALLOC_FREE(frame);
686 return action;
689 static virusfilter_action infected_file_action_rename(
690 struct vfs_handle_struct *handle,
691 struct virusfilter_config *config,
692 TALLOC_CTX *mem_ctx,
693 const struct files_struct *fsp,
694 const char **filepath_newp)
696 TALLOC_CTX *frame = talloc_stackframe();
697 connection_struct *conn = handle->conn;
698 char *cwd_fname = fsp->conn->cwd_fname->base_name;
699 char *fname = fsp->fsp_name->base_name;
700 const struct smb_filename *smb_fname = fsp->fsp_name;
701 struct smb_filename *q_smb_fname = NULL;
702 char *q_dir = NULL;
703 char *q_prefix = NULL;
704 char *q_suffix = NULL;
705 char *q_filepath = NULL;
706 const char *base_name = NULL;
707 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
708 bool ok = false;
709 int ret = -1;
710 int saved_errno = 0;
712 q_prefix = virusfilter_string_sub(frame, conn,
713 config->rename_prefix);
714 q_suffix = virusfilter_string_sub(frame, conn,
715 config->rename_suffix);
716 if (q_prefix == NULL || q_suffix == NULL) {
717 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
718 "memory\n", cwd_fname, fname);
719 action = VIRUSFILTER_ACTION_DO_NOTHING;
720 goto out;
723 ok = parent_dirname(frame, fname, &q_dir, &base_name);
724 if (!ok) {
725 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
726 "memory\n", cwd_fname, fname);
727 action = VIRUSFILTER_ACTION_DO_NOTHING;
728 goto out;
731 if (q_dir == NULL) {
732 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
733 "memory\n", cwd_fname, fname);
734 action = VIRUSFILTER_ACTION_DO_NOTHING;
735 goto out;
738 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
739 q_prefix, base_name, q_suffix);
741 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
742 smb_fname->stream_name, NULL,
743 smb_fname->flags);
744 if (q_smb_fname == NULL) {
745 action = VIRUSFILTER_ACTION_DO_NOTHING;
746 goto out;
749 become_root();
750 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
751 if (ret == -1) {
752 saved_errno = errno;
754 unbecome_root();
756 if (ret == -1) {
757 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
758 cwd_fname, fname, strerror(saved_errno));
759 errno = saved_errno;
760 action = VIRUSFILTER_ACTION_DO_NOTHING;
761 goto out;
764 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
766 out:
767 TALLOC_FREE(frame);
768 return action;
771 static virusfilter_action infected_file_action_delete(
772 struct vfs_handle_struct *handle,
773 const struct files_struct *fsp)
775 int ret;
776 int saved_errno = 0;
778 become_root();
779 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
780 if (ret == -1) {
781 saved_errno = errno;
783 unbecome_root();
784 if (ret == -1) {
785 DBG_ERR("Delete [%s/%s] failed: %s\n",
786 fsp->conn->cwd_fname->base_name,
787 fsp->fsp_name->base_name,
788 strerror(saved_errno));
789 errno = saved_errno;
790 return VIRUSFILTER_ACTION_DO_NOTHING;
793 return VIRUSFILTER_ACTION_DELETE;
796 static virusfilter_action virusfilter_do_infected_file_action(
797 struct vfs_handle_struct *handle,
798 struct virusfilter_config *config,
799 TALLOC_CTX *mem_ctx,
800 const struct files_struct *fsp,
801 const char **filepath_newp)
803 virusfilter_action action;
805 *filepath_newp = NULL;
807 switch (config->infected_file_action) {
808 case VIRUSFILTER_ACTION_RENAME:
809 action = infected_file_action_rename(handle, config, mem_ctx,
810 fsp, filepath_newp);
811 break;
813 case VIRUSFILTER_ACTION_QUARANTINE:
814 action = infected_file_action_quarantine(handle, config, mem_ctx,
815 fsp, filepath_newp);
816 break;
818 case VIRUSFILTER_ACTION_DELETE:
819 action = infected_file_action_delete(handle, fsp);
820 break;
822 case VIRUSFILTER_ACTION_DO_NOTHING:
823 default:
824 action = VIRUSFILTER_ACTION_DO_NOTHING;
825 break;
828 return action;
831 static virusfilter_action virusfilter_treat_infected_file(
832 struct vfs_handle_struct *handle,
833 struct virusfilter_config *config,
834 const struct files_struct *fsp,
835 const char *report,
836 bool is_cache)
838 connection_struct *conn = handle->conn;
839 char *cwd_fname = fsp->conn->cwd_fname->base_name;
840 char *fname = fsp->fsp_name->base_name;
841 TALLOC_CTX *mem_ctx = talloc_tos();
842 int i;
843 virusfilter_action action;
844 const char *action_name = "UNKNOWN";
845 const char *filepath_q = NULL;
846 char *env_list = NULL;
847 char *command = NULL;
848 int command_result;
849 int ret;
851 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
852 fsp, &filepath_q);
853 for (i=0; virusfilter_actions[i].name; i++) {
854 if (virusfilter_actions[i].value == action) {
855 action_name = virusfilter_actions[i].name;
856 break;
859 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
860 fname, action_name);
862 if (!config->infected_file_command) {
863 return action;
866 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
867 if (ret == -1) {
868 goto done;
870 ret = virusfilter_env_set(mem_ctx, &env_list,
871 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
872 fname);
873 if (ret == -1) {
874 goto done;
876 if (report != NULL) {
877 ret = virusfilter_env_set(mem_ctx, &env_list,
878 "VIRUSFILTER_INFECTED_FILE_REPORT",
879 report);
880 if (ret == -1) {
881 goto done;
884 ret = virusfilter_env_set(mem_ctx, &env_list,
885 "VIRUSFILTER_INFECTED_FILE_ACTION",
886 action_name);
887 if (ret == -1) {
888 goto done;
890 if (filepath_q != NULL) {
891 ret = virusfilter_env_set(mem_ctx, &env_list,
892 "VIRUSFILTER_QUARANTINED_FILE_PATH",
893 filepath_q);
894 if (ret == -1) {
895 goto done;
898 if (is_cache) {
899 ret = virusfilter_env_set(mem_ctx, &env_list,
900 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
901 if (ret == -1) {
902 goto done;
906 command = virusfilter_string_sub(mem_ctx, conn,
907 config->infected_file_command);
908 if (command == NULL) {
909 DBG_ERR("virusfilter_string_sub failed\n");
910 goto done;
913 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
914 fname, command);
916 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
917 conn, true);
918 if (command_result != 0) {
919 DBG_ERR("Infected file command failed: %d\n", command_result);
922 DBG_DEBUG("Infected file command finished: %d\n", command_result);
924 done:
925 TALLOC_FREE(env_list);
926 TALLOC_FREE(command);
928 return action;
931 static void virusfilter_treat_scan_error(
932 struct vfs_handle_struct *handle,
933 struct virusfilter_config *config,
934 const struct files_struct *fsp,
935 const char *report,
936 bool is_cache)
938 connection_struct *conn = handle->conn;
939 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
940 const char *fname = fsp->fsp_name->base_name;
941 TALLOC_CTX *mem_ctx = talloc_tos();
942 char *env_list = NULL;
943 char *command = NULL;
944 int command_result;
945 int ret;
947 if (!config->scan_error_command) {
948 return;
950 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
951 if (ret == -1) {
952 goto done;
954 ret = virusfilter_env_set(mem_ctx, &env_list,
955 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
956 fname);
957 if (ret == -1) {
958 goto done;
960 if (report != NULL) {
961 ret = virusfilter_env_set(mem_ctx, &env_list,
962 "VIRUSFILTER_SCAN_ERROR_REPORT",
963 report);
964 if (ret == -1) {
965 goto done;
968 if (is_cache) {
969 ret = virusfilter_env_set(mem_ctx, &env_list,
970 "VIRUSFILTER_RESULT_IS_CACHE", "1");
971 if (ret == -1) {
972 goto done;
976 command = virusfilter_string_sub(mem_ctx, conn,
977 config->scan_error_command);
978 if (command == NULL) {
979 DBG_ERR("virusfilter_string_sub failed\n");
980 goto done;
983 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
984 fname, command);
986 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
987 conn, true);
988 if (command_result != 0) {
989 DBG_ERR("Scan error command failed: %d\n", command_result);
992 done:
993 TALLOC_FREE(env_list);
994 TALLOC_FREE(command);
997 static virusfilter_result virusfilter_scan(
998 struct vfs_handle_struct *handle,
999 struct virusfilter_config *config,
1000 const struct files_struct *fsp)
1002 virusfilter_result scan_result;
1003 char *scan_report = NULL;
1004 const char *fname = fsp->fsp_name->base_name;
1005 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1006 struct virusfilter_cache_entry *scan_cache_e = NULL;
1007 bool is_cache = false;
1008 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1009 bool add_scan_cache = true;
1010 bool ok = false;
1012 if (config->cache) {
1013 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1014 scan_cache_e = virusfilter_cache_get(config->cache,
1015 cwd_fname, fname);
1016 if (scan_cache_e != NULL) {
1017 DBG_DEBUG("Cache entry found: cached result: %d\n",
1018 scan_cache_e->result);
1019 is_cache = true;
1020 scan_result = scan_cache_e->result;
1021 scan_report = scan_cache_e->report;
1022 goto virusfilter_scan_result_eval;
1024 DBG_DEBUG("Cache entry not found\n");
1027 if (config->backend->fns->scan_init != NULL) {
1028 scan_result = config->backend->fns->scan_init(config);
1029 if (scan_result != VIRUSFILTER_RESULT_OK) {
1030 scan_result = VIRUSFILTER_RESULT_ERROR;
1031 scan_report = talloc_asprintf(
1032 talloc_tos(),
1033 "Initializing scanner failed");
1034 goto virusfilter_scan_result_eval;
1038 scan_result = config->backend->fns->scan(handle, config, fsp,
1039 &scan_report);
1041 if (config->backend->fns->scan_end != NULL) {
1042 bool scan_end = true;
1044 if (config->scan_request_limit > 0) {
1045 scan_end = false;
1046 config->scan_request_count++;
1047 if (config->scan_request_count >=
1048 config->scan_request_limit)
1050 scan_end = true;
1051 config->scan_request_count = 0;
1054 if (scan_end) {
1055 config->backend->fns->scan_end(config);
1059 virusfilter_scan_result_eval:
1061 switch (scan_result) {
1062 case VIRUSFILTER_RESULT_CLEAN:
1063 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1064 break;
1066 case VIRUSFILTER_RESULT_INFECTED:
1067 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1068 cwd_fname, fname, scan_report ? scan_report :
1069 "infected (memory error on report)");
1070 file_action = virusfilter_treat_infected_file(handle,
1071 config, fsp, scan_report, is_cache);
1072 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1073 add_scan_cache = false;
1075 break;
1077 case VIRUSFILTER_RESULT_SUSPECTED:
1078 if (!config->block_suspected_file) {
1079 break;
1081 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1082 cwd_fname, fname, scan_report ? scan_report :
1083 "suspected infection (memory error on report)");
1084 file_action = virusfilter_treat_infected_file(handle,
1085 config, fsp, scan_report, is_cache);
1086 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1087 add_scan_cache = false;
1089 break;
1091 case VIRUSFILTER_RESULT_ERROR:
1092 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1093 cwd_fname, fname, scan_report ? scan_report :
1094 "error (memory error on report)");
1095 virusfilter_treat_scan_error(handle, config, fsp,
1096 scan_report, is_cache);
1097 add_scan_cache = false;
1098 break;
1100 default:
1101 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1102 scan_result, cwd_fname, fname, scan_report ?
1103 scan_report : "Unknown (memory error on report)");
1104 virusfilter_treat_scan_error(handle, config, fsp,
1105 scan_report, is_cache);
1106 add_scan_cache = false;
1107 break;
1110 if (config->cache) {
1111 if (!is_cache && add_scan_cache) {
1112 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1113 scan_result);
1114 ok = virusfilter_cache_entry_add(
1115 config->cache, cwd_fname, fname,
1116 scan_result, scan_report);
1117 if (!ok) {
1118 DBG_ERR("Cannot create cache entry: "
1119 "virusfilter_cache_entry_new failed");
1120 goto virusfilter_scan_return;
1122 } else if (is_cache) {
1123 virusfilter_cache_entry_free(scan_cache_e);
1127 virusfilter_scan_return:
1128 return scan_result;
1131 static int virusfilter_vfs_open(
1132 struct vfs_handle_struct *handle,
1133 struct smb_filename *smb_fname,
1134 files_struct *fsp,
1135 int flags,
1136 mode_t mode)
1138 TALLOC_CTX *mem_ctx = talloc_tos();
1139 struct virusfilter_config *config;
1140 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1141 virusfilter_result scan_result;
1142 const char *fname = fsp->fsp_name->base_name;
1143 char *dir_name = NULL;
1144 const char *base_name = NULL;
1145 int scan_errno = 0;
1146 size_t test_prefix;
1147 size_t test_suffix;
1148 int rename_trap_count = 0;
1149 int ret;
1150 bool ok1, ok2;
1151 char *sret = NULL;
1153 SMB_VFS_HANDLE_GET_DATA(handle, config,
1154 struct virusfilter_config, return -1);
1156 if (fsp->is_directory) {
1157 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1158 goto virusfilter_vfs_open_next;
1161 test_prefix = strlen(config->rename_prefix);
1162 test_suffix = strlen(config->rename_suffix);
1163 if (test_prefix > 0) {
1164 rename_trap_count++;
1166 if (test_suffix > 0) {
1167 rename_trap_count++;
1170 ok1 = is_ntfs_stream_smb_fname(smb_fname);
1171 ok2 = is_ntfs_default_stream_smb_fname(smb_fname);
1172 if (ok1 && !ok2) {
1173 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1174 " %s/%s\n", cwd_fname, fname);
1175 goto virusfilter_vfs_open_next;
1178 if (!config->scan_on_open) {
1179 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1180 cwd_fname, fname);
1181 goto virusfilter_vfs_open_next;
1184 if (flags & O_TRUNC) {
1185 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1186 cwd_fname, fname);
1187 goto virusfilter_vfs_open_next;
1190 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1191 if (ret != 0) {
1194 * Do not return immediately if !(flags & O_CREAT) &&
1195 * errno != ENOENT.
1196 * Do not do this here or anywhere else. The module is
1197 * stackable and there may be modules below, such as audit
1198 * modules, which should be handled.
1200 goto virusfilter_vfs_open_next;
1202 ret = S_ISREG(smb_fname->st.st_ex_mode);
1203 if (ret == 0) {
1204 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1205 cwd_fname, fname);
1206 goto virusfilter_vfs_open_next;
1208 if (config->max_file_size > 0 &&
1209 smb_fname->st.st_ex_size > config->max_file_size)
1211 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1212 cwd_fname, fname);
1213 goto virusfilter_vfs_open_next;
1215 if (config->min_file_size > 0 &&
1216 smb_fname->st.st_ex_size < config->min_file_size)
1218 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1219 cwd_fname, fname);
1220 goto virusfilter_vfs_open_next;
1223 ok1 = is_in_path(fname, config->exclude_files, false);
1224 if (config->exclude_files && ok1)
1226 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1227 cwd_fname, fname);
1228 goto virusfilter_vfs_open_next;
1231 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1232 sret = strstr_m(fname, config->quarantine_dir);
1233 if (sret != NULL) {
1234 scan_errno = config->infected_open_errno;
1235 goto virusfilter_vfs_open_fail;
1239 if (test_prefix > 0 || test_suffix > 0) {
1240 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1241 if (ok1)
1243 if (test_prefix > 0) {
1244 ret = strncmp(base_name,
1245 config->rename_prefix, test_prefix);
1246 if (ret != 0) {
1247 test_prefix = 0;
1250 if (test_suffix > 0) {
1251 ret = strcmp(base_name + (strlen(base_name)
1252 - test_suffix),
1253 config->rename_suffix);
1254 if (ret != 0) {
1255 test_suffix = 0;
1259 TALLOC_FREE(dir_name);
1261 if ((rename_trap_count == 2 && test_prefix &&
1262 test_suffix) || (rename_trap_count == 1 &&
1263 (test_prefix || test_suffix)))
1265 scan_errno =
1266 config->infected_open_errno;
1267 goto virusfilter_vfs_open_fail;
1272 scan_result = virusfilter_scan(handle, config, fsp);
1274 switch (scan_result) {
1275 case VIRUSFILTER_RESULT_CLEAN:
1276 break;
1277 case VIRUSFILTER_RESULT_INFECTED:
1278 scan_errno = config->infected_open_errno;
1279 goto virusfilter_vfs_open_fail;
1280 case VIRUSFILTER_RESULT_ERROR:
1281 if (config->block_access_on_error) {
1282 DBG_INFO("Block access\n");
1283 scan_errno = config->scan_error_open_errno;
1284 goto virusfilter_vfs_open_fail;
1286 break;
1287 default:
1288 scan_errno = config->scan_error_open_errno;
1289 goto virusfilter_vfs_open_fail;
1292 virusfilter_vfs_open_next:
1293 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1295 virusfilter_vfs_open_fail:
1296 errno = (scan_errno != 0) ? scan_errno : EACCES;
1297 return -1;
1300 static int virusfilter_vfs_close(
1301 struct vfs_handle_struct *handle,
1302 files_struct *fsp)
1305 * The name of this variable is for consistency. If API changes to
1306 * match _open change to cwd_fname as in virusfilter_vfs_open.
1308 const char *cwd_fname = handle->conn->connectpath;
1310 struct virusfilter_config *config = NULL;
1311 char *fname = fsp->fsp_name->base_name;
1312 int close_result = -1;
1313 int close_errno = 0;
1314 virusfilter_result scan_result;
1315 int scan_errno = 0;
1316 bool ok1, ok2;
1318 SMB_VFS_HANDLE_GET_DATA(handle, config,
1319 struct virusfilter_config, return -1);
1322 * Must close after scan? It appears not as the scanners are not
1323 * internal and other modules such as greyhole seem to do
1324 * SMB_VFS_NEXT_* functions before processing.
1326 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1327 if (close_result == -1) {
1328 close_errno = errno;
1332 * Return immediately if close_result == -1, and close_errno == EBADF.
1333 * If close failed, file likely doesn't exist, do not try to scan.
1335 if (close_result == -1 && close_errno == EBADF) {
1336 if (fsp->modified) {
1337 DBG_DEBUG("Removing cache entry (if existent): "
1338 "fname: %s\n", fname);
1339 virusfilter_cache_remove(config->cache,
1340 cwd_fname, fname);
1342 goto virusfilter_vfs_close_fail;
1345 if (fsp->is_directory) {
1346 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1347 return close_result;
1350 ok1 = is_ntfs_stream_smb_fname(fsp->fsp_name);
1351 ok2 = is_ntfs_default_stream_smb_fname(fsp->fsp_name);
1352 if (ok1 && !ok2) {
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_unlink(
1431 struct vfs_handle_struct *handle,
1432 const struct smb_filename *smb_fname)
1434 int ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1435 struct virusfilter_config *config = NULL;
1436 char *fname = NULL;
1437 char *cwd_fname = handle->conn->cwd_fname->base_name;
1439 if (ret != 0 && errno != ENOENT) {
1440 return ret;
1443 SMB_VFS_HANDLE_GET_DATA(handle, config,
1444 struct virusfilter_config, return -1);
1446 if (config->cache == NULL) {
1447 return 0;
1450 fname = smb_fname->base_name;
1452 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1453 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1455 return 0;
1458 static int virusfilter_vfs_rename(
1459 struct vfs_handle_struct *handle,
1460 const struct smb_filename *smb_fname_src,
1461 const struct smb_filename *smb_fname_dst)
1463 int ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1464 struct virusfilter_config *config = NULL;
1465 char *fname = NULL;
1466 char *dst_fname = NULL;
1467 char *cwd_fname = handle->conn->cwd_fname->base_name;
1469 if (ret != 0) {
1470 return ret;
1473 SMB_VFS_HANDLE_GET_DATA(handle, config,
1474 struct virusfilter_config, return -1);
1476 if (config->cache == NULL) {
1477 return 0;
1480 fname = smb_fname_src->base_name;
1481 dst_fname = smb_fname_dst->base_name;
1483 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1484 fname, dst_fname);
1485 virusfilter_cache_entry_rename(config->cache,
1486 cwd_fname, fname,
1487 dst_fname);
1489 return 0;
1492 /* VFS operations */
1493 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1494 .connect_fn = virusfilter_vfs_connect,
1495 .disconnect_fn = virusfilter_vfs_disconnect,
1496 .open_fn = virusfilter_vfs_open,
1497 .close_fn = virusfilter_vfs_close,
1498 .unlink_fn = virusfilter_vfs_unlink,
1499 .rename_fn = virusfilter_vfs_rename,
1502 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1503 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1505 NTSTATUS status;
1507 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1508 "virusfilter",
1509 &vfs_virusfilter_fns);
1510 if (!NT_STATUS_IS_OK(status)) {
1511 return status;
1514 virusfilter_debug_class = debug_add_class("virusfilter");
1515 if (virusfilter_debug_class == -1) {
1516 virusfilter_debug_class = DBGC_VFS;
1517 DBG_ERR("Couldn't register custom debugging class!\n");
1518 } else {
1519 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1522 DBG_INFO("registered\n");
1524 return status;