selftest:Samba4: report when samba is started and ready
[Samba.git] / source3 / modules / vfs_virusfilter.c
blob0b4bc724f3a3f40ee0b7f1892fa79a8d3cad1e47
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 unsigned int mode = 0;
279 sscanf(temp_quarantine_dir_mode, "%o", &mode);
280 config->quarantine_dir_mode = mode;
283 config->quarantine_prefix = lp_parm_const_string(
284 snum, "virusfilter", "quarantine prefix",
285 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
287 config->quarantine_suffix = lp_parm_const_string(
288 snum, "virusfilter", "quarantine suffix",
289 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
292 * Make sure prefixes and suffixes do not contain directory
293 * delimiters
295 sret = strstr(config->quarantine_prefix, "/");
296 if (sret != NULL) {
297 DBG_ERR("quarantine prefix must not contain directory "
298 "delimiter(s) such as '/' (%s replaced with %s)\n",
299 config->quarantine_prefix,
300 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
301 config->quarantine_prefix =
302 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
304 sret = strstr(config->quarantine_suffix, "/");
305 if (sret != NULL) {
306 DBG_ERR("quarantine suffix must not contain directory "
307 "delimiter(s) such as '/' (%s replaced with %s)\n",
308 config->quarantine_suffix,
309 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
310 config->quarantine_suffix =
311 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
314 config->quarantine_keep_tree = lp_parm_bool(
315 snum, "virusfilter", "quarantine keep tree", true);
317 config->quarantine_keep_name = lp_parm_bool(
318 snum, "virusfilter", "quarantine keep name", true);
320 config->rename_prefix = lp_parm_const_string(
321 snum, "virusfilter", "rename prefix",
322 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
324 config->rename_suffix = lp_parm_const_string(
325 snum, "virusfilter", "rename suffix",
326 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
329 * Make sure prefixes and suffixes do not contain directory
330 * delimiters
332 sret = strstr(config->rename_prefix, "/");
333 if (sret != NULL) {
334 DBG_ERR("rename prefix must not contain directory "
335 "delimiter(s) such as '/' (%s replaced with %s)\n",
336 config->rename_prefix,
337 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
338 config->rename_prefix =
339 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
341 sret = strstr(config->rename_suffix, "/");
342 if (sret != NULL) {
343 DBG_ERR("rename suffix must not contain directory "
344 "delimiter(s) such as '/' (%s replaced with %s)\n",
345 config->rename_suffix,
346 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
347 config->rename_suffix =
348 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
351 config->infected_open_errno = lp_parm_int(
352 snum, "virusfilter", "infected file errno on open", EACCES);
354 config->infected_close_errno = lp_parm_int(
355 snum, "virusfilter", "infected file errno on close", 0);
357 config->scan_error_open_errno = lp_parm_int(
358 snum, "virusfilter", "scan error errno on open", EACCES);
360 config->scan_error_close_errno = lp_parm_int(
361 snum, "virusfilter", "scan error errno on close", 0);
363 config->socket_path = lp_parm_const_string(
364 snum, "virusfilter", "socket path", NULL);
366 /* canonicalize socket_path */
367 if (config->socket_path != NULL && config->socket_path[0] != '/') {
368 DBG_ERR("socket path must be an absolute path. "
369 "Using backend default\n");
370 config->socket_path = NULL;
372 if (config->socket_path != NULL) {
373 canonicalize_absolute_path(handle,
374 config->socket_path);
377 connect_timeout = lp_parm_int(snum, "virusfilter",
378 "connect timeout", 30000);
380 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
382 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
383 if (config->io_h == NULL) {
384 DBG_ERR("virusfilter_io_new failed");
385 return -1;
388 if (config->cache_entry_limit > 0) {
389 config->cache = virusfilter_cache_new(handle,
390 config->cache_entry_limit,
391 config->cache_time_limit);
392 if (config->cache == NULL) {
393 DBG_ERR("Initializing cache failed: Cache disabled\n");
394 return -1;
399 * Check quarantine directory now to save processing
400 * and becoming root over and over.
402 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
403 bool ok = true;
404 bool dir_exists;
407 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
408 * hierarchy
410 become_root();
411 dir_exists = quarantine_directory_exist(handle,
412 config->quarantine_dir);
413 if (!dir_exists) {
414 DBG_DEBUG("Creating quarantine directory: %s\n",
415 config->quarantine_dir);
416 ok = quarantine_create_dir(handle, config,
417 config->quarantine_dir);
419 unbecome_root();
420 if (!ok) {
421 DBG_ERR("Creating quarantine directory %s "
422 "failed with %s\n",
423 config->quarantine_dir,
424 strerror(errno));
425 return -1;
430 * Now that the frontend options are initialized, load the configured
431 * backend.
434 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
435 "virusfilter",
436 "scanner",
437 scanner_list,
438 -1);
439 if (backend == (enum virusfilter_scanner_enum)-1) {
440 DBG_ERR("No AV-Scanner configured, "
441 "please set \"virusfilter:scanner\"\n");
442 return -1;
445 switch (backend) {
446 case VIRUSFILTER_SCANNER_SOPHOS:
447 ret = virusfilter_sophos_init(config);
448 break;
449 case VIRUSFILTER_SCANNER_FSAV:
450 ret = virusfilter_fsav_init(config);
451 break;
452 case VIRUSFILTER_SCANNER_CLAMAV:
453 ret = virusfilter_clamav_init(config);
454 break;
455 default:
456 DBG_ERR("Unhandled scanner %d\n", backend);
457 return -1;
459 if (ret != 0) {
460 DBG_ERR("Scanner backend init failed\n");
461 return -1;
464 if (config->backend->fns->connect != NULL) {
465 ret = config->backend->fns->connect(handle, config, svc, user);
466 if (ret == -1) {
467 return -1;
471 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
474 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
476 struct virusfilter_config *config = NULL;
478 SMB_VFS_HANDLE_GET_DATA(handle, config,
479 struct virusfilter_config, return);
481 if (config->backend->fns->disconnect != NULL) {
482 config->backend->fns->disconnect(handle);
485 free_namearray(config->exclude_files);
486 virusfilter_io_disconnect(config->io_h);
488 SMB_VFS_NEXT_DISCONNECT(handle);
491 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
492 struct virusfilter_config *config,
493 char **env_list)
495 int ret;
497 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
498 VIRUSFILTER_VERSION);
499 if (ret == -1) {
500 return -1;
502 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
503 config->backend->name);
504 if (ret == -1) {
505 return -1;
508 if (config->backend->version != 0) {
509 char *version = NULL;
511 version = talloc_asprintf(talloc_tos(), "%u",
512 config->backend->version);
513 if (version == NULL) {
514 return -1;
516 ret = virusfilter_env_set(mem_ctx, env_list,
517 "VIRUSFILTER_MODULE_VERSION",
518 version);
519 TALLOC_FREE(version);
520 if (ret == -1) {
521 return -1;
525 return 0;
528 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
529 struct vfs_handle_struct *handle,
530 struct virusfilter_config *config,
531 const struct smb_filename *smb_fname,
532 char *q_dir_in,
533 char *cwd_fname)
535 char *temp_path = NULL;
536 char *q_dir_out = NULL;
537 bool ok;
539 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
540 if (temp_path == NULL) {
541 DBG_ERR("talloc_asprintf failed\n");
542 goto out;
545 become_root();
546 ok = quarantine_directory_exist(handle, temp_path);
547 unbecome_root();
548 if (ok) {
549 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
550 q_dir_out = talloc_move(mem_ctx, &temp_path);
551 goto out;
554 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
556 become_root();
557 ok = quarantine_create_dir(handle, config, temp_path);
558 unbecome_root();
559 if (!ok) {
560 DBG_NOTICE("Could not create quarantine directory [%s], "
561 "ignoring for [%s]\n",
562 temp_path, smb_fname_str_dbg(smb_fname));
563 goto out;
566 q_dir_out = talloc_move(mem_ctx, &temp_path);
568 out:
569 TALLOC_FREE(temp_path);
570 return q_dir_out;
573 static virusfilter_action infected_file_action_quarantine(
574 struct vfs_handle_struct *handle,
575 struct virusfilter_config *config,
576 TALLOC_CTX *mem_ctx,
577 const struct files_struct *fsp,
578 const char **filepath_newp)
580 TALLOC_CTX *frame = talloc_stackframe();
581 connection_struct *conn = handle->conn;
582 char *cwd_fname = fsp->conn->cwd_fname->base_name;
583 char *fname = fsp->fsp_name->base_name;
584 const struct smb_filename *smb_fname = fsp->fsp_name;
585 struct smb_filename *q_smb_fname = NULL;
586 char *q_dir = NULL;
587 char *q_prefix = NULL;
588 char *q_suffix = NULL;
589 char *q_filepath = NULL;
590 char *dir_name = NULL;
591 const char *base_name = NULL;
592 char *rand_filename_component = NULL;
593 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
594 bool ok = false;
595 int ret = -1;
596 int saved_errno = 0;
598 q_dir = virusfilter_string_sub(frame, conn,
599 config->quarantine_dir);
600 q_prefix = virusfilter_string_sub(frame, conn,
601 config->quarantine_prefix);
602 q_suffix = virusfilter_string_sub(frame, conn,
603 config->quarantine_suffix);
604 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
605 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
606 "memory\n", cwd_fname, fname);
607 action = VIRUSFILTER_ACTION_DO_NOTHING;
608 goto out;
611 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
612 ok = parent_dirname(frame, smb_fname->base_name,
613 &dir_name, &base_name);
614 if (!ok) {
615 DBG_ERR("parent_dirname failed\n");
616 action = VIRUSFILTER_ACTION_DO_NOTHING;
617 goto out;
620 if (config->quarantine_keep_tree) {
621 char *tree = NULL;
623 tree = quarantine_check_tree(frame, handle, config,
624 smb_fname, q_dir,
625 cwd_fname);
626 if (tree == NULL) {
628 * If we can't create the tree, just move it
629 * into the toplevel quarantine dir.
631 tree = q_dir;
633 q_dir = tree;
637 /* Get a 16 byte + \0 random filename component. */
638 rand_filename_component = generate_random_str(frame, 16);
639 if (rand_filename_component == NULL) {
640 DBG_ERR("generate_random_str failed\n");
641 action = VIRUSFILTER_ACTION_DO_NOTHING;
642 goto out;
645 if (config->quarantine_keep_name) {
646 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
647 q_dir, q_prefix,
648 base_name, q_suffix,
649 rand_filename_component);
650 } else {
651 q_filepath = talloc_asprintf(frame, "%s/%s%s",
652 q_dir, q_prefix,
653 rand_filename_component);
655 if (q_filepath == NULL) {
656 DBG_ERR("talloc_asprintf failed\n");
657 action = VIRUSFILTER_ACTION_DO_NOTHING;
658 goto out;
661 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
662 smb_fname->stream_name,
663 NULL, smb_fname->flags);
664 if (q_smb_fname == NULL) {
665 action = VIRUSFILTER_ACTION_DO_NOTHING;
666 goto out;
669 become_root();
670 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
671 if (ret == -1) {
672 saved_errno = errno;
674 unbecome_root();
675 if (ret == -1) {
676 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
677 cwd_fname, fname, q_filepath, strerror(saved_errno));
678 errno = saved_errno;
679 action = VIRUSFILTER_ACTION_DO_NOTHING;
680 goto out;
683 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
685 out:
686 TALLOC_FREE(frame);
687 return action;
690 static virusfilter_action infected_file_action_rename(
691 struct vfs_handle_struct *handle,
692 struct virusfilter_config *config,
693 TALLOC_CTX *mem_ctx,
694 const struct files_struct *fsp,
695 const char **filepath_newp)
697 TALLOC_CTX *frame = talloc_stackframe();
698 connection_struct *conn = handle->conn;
699 char *cwd_fname = fsp->conn->cwd_fname->base_name;
700 char *fname = fsp->fsp_name->base_name;
701 const struct smb_filename *smb_fname = fsp->fsp_name;
702 struct smb_filename *q_smb_fname = NULL;
703 char *q_dir = NULL;
704 char *q_prefix = NULL;
705 char *q_suffix = NULL;
706 char *q_filepath = NULL;
707 const char *base_name = NULL;
708 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
709 bool ok = false;
710 int ret = -1;
711 int saved_errno = 0;
713 q_prefix = virusfilter_string_sub(frame, conn,
714 config->rename_prefix);
715 q_suffix = virusfilter_string_sub(frame, conn,
716 config->rename_suffix);
717 if (q_prefix == NULL || q_suffix == NULL) {
718 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
719 "memory\n", cwd_fname, fname);
720 action = VIRUSFILTER_ACTION_DO_NOTHING;
721 goto out;
724 ok = parent_dirname(frame, fname, &q_dir, &base_name);
725 if (!ok) {
726 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
727 "memory\n", cwd_fname, fname);
728 action = VIRUSFILTER_ACTION_DO_NOTHING;
729 goto out;
732 if (q_dir == NULL) {
733 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
734 "memory\n", cwd_fname, fname);
735 action = VIRUSFILTER_ACTION_DO_NOTHING;
736 goto out;
739 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
740 q_prefix, base_name, q_suffix);
742 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
743 smb_fname->stream_name, NULL,
744 smb_fname->flags);
745 if (q_smb_fname == NULL) {
746 action = VIRUSFILTER_ACTION_DO_NOTHING;
747 goto out;
750 become_root();
751 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
752 if (ret == -1) {
753 saved_errno = errno;
755 unbecome_root();
757 if (ret == -1) {
758 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
759 cwd_fname, fname, strerror(saved_errno));
760 errno = saved_errno;
761 action = VIRUSFILTER_ACTION_DO_NOTHING;
762 goto out;
765 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
767 out:
768 TALLOC_FREE(frame);
769 return action;
772 static virusfilter_action infected_file_action_delete(
773 struct vfs_handle_struct *handle,
774 const struct files_struct *fsp)
776 int ret;
777 int saved_errno = 0;
779 become_root();
780 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
781 if (ret == -1) {
782 saved_errno = errno;
784 unbecome_root();
785 if (ret == -1) {
786 DBG_ERR("Delete [%s/%s] failed: %s\n",
787 fsp->conn->cwd_fname->base_name,
788 fsp->fsp_name->base_name,
789 strerror(saved_errno));
790 errno = saved_errno;
791 return VIRUSFILTER_ACTION_DO_NOTHING;
794 return VIRUSFILTER_ACTION_DELETE;
797 static virusfilter_action virusfilter_do_infected_file_action(
798 struct vfs_handle_struct *handle,
799 struct virusfilter_config *config,
800 TALLOC_CTX *mem_ctx,
801 const struct files_struct *fsp,
802 const char **filepath_newp)
804 virusfilter_action action;
806 *filepath_newp = NULL;
808 switch (config->infected_file_action) {
809 case VIRUSFILTER_ACTION_RENAME:
810 action = infected_file_action_rename(handle, config, mem_ctx,
811 fsp, filepath_newp);
812 break;
814 case VIRUSFILTER_ACTION_QUARANTINE:
815 action = infected_file_action_quarantine(handle, config, mem_ctx,
816 fsp, filepath_newp);
817 break;
819 case VIRUSFILTER_ACTION_DELETE:
820 action = infected_file_action_delete(handle, fsp);
821 break;
823 case VIRUSFILTER_ACTION_DO_NOTHING:
824 default:
825 action = VIRUSFILTER_ACTION_DO_NOTHING;
826 break;
829 return action;
832 static virusfilter_action virusfilter_treat_infected_file(
833 struct vfs_handle_struct *handle,
834 struct virusfilter_config *config,
835 const struct files_struct *fsp,
836 const char *report,
837 bool is_cache)
839 connection_struct *conn = handle->conn;
840 char *cwd_fname = fsp->conn->cwd_fname->base_name;
841 char *fname = fsp->fsp_name->base_name;
842 TALLOC_CTX *mem_ctx = talloc_tos();
843 int i;
844 virusfilter_action action;
845 const char *action_name = "UNKNOWN";
846 const char *filepath_q = NULL;
847 char *env_list = NULL;
848 char *command = NULL;
849 int command_result;
850 int ret;
852 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
853 fsp, &filepath_q);
854 for (i=0; virusfilter_actions[i].name; i++) {
855 if (virusfilter_actions[i].value == action) {
856 action_name = virusfilter_actions[i].name;
857 break;
860 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
861 fname, action_name);
863 if (!config->infected_file_command) {
864 return action;
867 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
868 if (ret == -1) {
869 goto done;
871 ret = virusfilter_env_set(mem_ctx, &env_list,
872 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
873 fname);
874 if (ret == -1) {
875 goto done;
877 if (report != NULL) {
878 ret = virusfilter_env_set(mem_ctx, &env_list,
879 "VIRUSFILTER_INFECTED_FILE_REPORT",
880 report);
881 if (ret == -1) {
882 goto done;
885 ret = virusfilter_env_set(mem_ctx, &env_list,
886 "VIRUSFILTER_INFECTED_FILE_ACTION",
887 action_name);
888 if (ret == -1) {
889 goto done;
891 if (filepath_q != NULL) {
892 ret = virusfilter_env_set(mem_ctx, &env_list,
893 "VIRUSFILTER_QUARANTINED_FILE_PATH",
894 filepath_q);
895 if (ret == -1) {
896 goto done;
899 if (is_cache) {
900 ret = virusfilter_env_set(mem_ctx, &env_list,
901 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
902 if (ret == -1) {
903 goto done;
907 command = virusfilter_string_sub(mem_ctx, conn,
908 config->infected_file_command);
909 if (command == NULL) {
910 DBG_ERR("virusfilter_string_sub failed\n");
911 goto done;
914 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
915 fname, command);
917 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
918 conn, true);
919 if (command_result != 0) {
920 DBG_ERR("Infected file command failed: %d\n", command_result);
923 DBG_DEBUG("Infected file command finished: %d\n", command_result);
925 done:
926 TALLOC_FREE(env_list);
927 TALLOC_FREE(command);
929 return action;
932 static void virusfilter_treat_scan_error(
933 struct vfs_handle_struct *handle,
934 struct virusfilter_config *config,
935 const struct files_struct *fsp,
936 const char *report,
937 bool is_cache)
939 connection_struct *conn = handle->conn;
940 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
941 const char *fname = fsp->fsp_name->base_name;
942 TALLOC_CTX *mem_ctx = talloc_tos();
943 char *env_list = NULL;
944 char *command = NULL;
945 int command_result;
946 int ret;
948 if (!config->scan_error_command) {
949 return;
951 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
952 if (ret == -1) {
953 goto done;
955 ret = virusfilter_env_set(mem_ctx, &env_list,
956 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
957 fname);
958 if (ret == -1) {
959 goto done;
961 if (report != NULL) {
962 ret = virusfilter_env_set(mem_ctx, &env_list,
963 "VIRUSFILTER_SCAN_ERROR_REPORT",
964 report);
965 if (ret == -1) {
966 goto done;
969 if (is_cache) {
970 ret = virusfilter_env_set(mem_ctx, &env_list,
971 "VIRUSFILTER_RESULT_IS_CACHE", "1");
972 if (ret == -1) {
973 goto done;
977 command = virusfilter_string_sub(mem_ctx, conn,
978 config->scan_error_command);
979 if (command == NULL) {
980 DBG_ERR("virusfilter_string_sub failed\n");
981 goto done;
984 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
985 fname, command);
987 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
988 conn, true);
989 if (command_result != 0) {
990 DBG_ERR("Scan error command failed: %d\n", command_result);
993 done:
994 TALLOC_FREE(env_list);
995 TALLOC_FREE(command);
998 static virusfilter_result virusfilter_scan(
999 struct vfs_handle_struct *handle,
1000 struct virusfilter_config *config,
1001 const struct files_struct *fsp)
1003 virusfilter_result scan_result;
1004 char *scan_report = NULL;
1005 const char *fname = fsp->fsp_name->base_name;
1006 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1007 struct virusfilter_cache_entry *scan_cache_e = NULL;
1008 bool is_cache = false;
1009 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1010 bool add_scan_cache = true;
1011 bool ok = false;
1013 if (config->cache) {
1014 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1015 scan_cache_e = virusfilter_cache_get(config->cache,
1016 cwd_fname, fname);
1017 if (scan_cache_e != NULL) {
1018 DBG_DEBUG("Cache entry found: cached result: %d\n",
1019 scan_cache_e->result);
1020 is_cache = true;
1021 scan_result = scan_cache_e->result;
1022 scan_report = scan_cache_e->report;
1023 goto virusfilter_scan_result_eval;
1025 DBG_DEBUG("Cache entry not found\n");
1028 if (config->backend->fns->scan_init != NULL) {
1029 scan_result = config->backend->fns->scan_init(config);
1030 if (scan_result != VIRUSFILTER_RESULT_OK) {
1031 scan_result = VIRUSFILTER_RESULT_ERROR;
1032 scan_report = talloc_asprintf(
1033 talloc_tos(),
1034 "Initializing scanner failed");
1035 goto virusfilter_scan_result_eval;
1039 scan_result = config->backend->fns->scan(handle, config, fsp,
1040 &scan_report);
1042 if (config->backend->fns->scan_end != NULL) {
1043 bool scan_end = true;
1045 if (config->scan_request_limit > 0) {
1046 scan_end = false;
1047 config->scan_request_count++;
1048 if (config->scan_request_count >=
1049 config->scan_request_limit)
1051 scan_end = true;
1052 config->scan_request_count = 0;
1055 if (scan_end) {
1056 config->backend->fns->scan_end(config);
1060 virusfilter_scan_result_eval:
1062 switch (scan_result) {
1063 case VIRUSFILTER_RESULT_CLEAN:
1064 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1065 break;
1067 case VIRUSFILTER_RESULT_INFECTED:
1068 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1069 cwd_fname, fname, scan_report ? scan_report :
1070 "infected (memory error on report)");
1071 file_action = virusfilter_treat_infected_file(handle,
1072 config, fsp, scan_report, is_cache);
1073 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1074 add_scan_cache = false;
1076 break;
1078 case VIRUSFILTER_RESULT_SUSPECTED:
1079 if (!config->block_suspected_file) {
1080 break;
1082 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1083 cwd_fname, fname, scan_report ? scan_report :
1084 "suspected infection (memory error on report)");
1085 file_action = virusfilter_treat_infected_file(handle,
1086 config, fsp, scan_report, is_cache);
1087 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1088 add_scan_cache = false;
1090 break;
1092 case VIRUSFILTER_RESULT_ERROR:
1093 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1094 cwd_fname, fname, scan_report ? scan_report :
1095 "error (memory error on report)");
1096 virusfilter_treat_scan_error(handle, config, fsp,
1097 scan_report, is_cache);
1098 add_scan_cache = false;
1099 break;
1101 default:
1102 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1103 scan_result, cwd_fname, fname, scan_report ?
1104 scan_report : "Unknown (memory error on report)");
1105 virusfilter_treat_scan_error(handle, config, fsp,
1106 scan_report, is_cache);
1107 add_scan_cache = false;
1108 break;
1111 if (config->cache) {
1112 if (!is_cache && add_scan_cache) {
1113 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1114 scan_result);
1115 ok = virusfilter_cache_entry_add(
1116 config->cache, cwd_fname, fname,
1117 scan_result, scan_report);
1118 if (!ok) {
1119 DBG_ERR("Cannot create cache entry: "
1120 "virusfilter_cache_entry_new failed");
1121 goto virusfilter_scan_return;
1123 } else if (is_cache) {
1124 virusfilter_cache_entry_free(scan_cache_e);
1128 virusfilter_scan_return:
1129 return scan_result;
1132 static int virusfilter_vfs_open(
1133 struct vfs_handle_struct *handle,
1134 struct smb_filename *smb_fname,
1135 files_struct *fsp,
1136 int flags,
1137 mode_t mode)
1139 TALLOC_CTX *mem_ctx = talloc_tos();
1140 struct virusfilter_config *config;
1141 const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1142 virusfilter_result scan_result;
1143 const char *fname = fsp->fsp_name->base_name;
1144 char *dir_name = NULL;
1145 const char *base_name = NULL;
1146 int scan_errno = 0;
1147 size_t test_prefix;
1148 size_t test_suffix;
1149 int rename_trap_count = 0;
1150 int ret;
1151 bool ok1, ok2;
1152 char *sret = NULL;
1154 SMB_VFS_HANDLE_GET_DATA(handle, config,
1155 struct virusfilter_config, return -1);
1157 if (fsp->is_directory) {
1158 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1159 goto virusfilter_vfs_open_next;
1162 test_prefix = strlen(config->rename_prefix);
1163 test_suffix = strlen(config->rename_suffix);
1164 if (test_prefix > 0) {
1165 rename_trap_count++;
1167 if (test_suffix > 0) {
1168 rename_trap_count++;
1171 ok1 = is_ntfs_stream_smb_fname(smb_fname);
1172 ok2 = is_ntfs_default_stream_smb_fname(smb_fname);
1173 if (ok1 && !ok2) {
1174 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1175 " %s/%s\n", cwd_fname, fname);
1176 goto virusfilter_vfs_open_next;
1179 if (!config->scan_on_open) {
1180 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1181 cwd_fname, fname);
1182 goto virusfilter_vfs_open_next;
1185 if (flags & O_TRUNC) {
1186 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1187 cwd_fname, fname);
1188 goto virusfilter_vfs_open_next;
1191 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1192 if (ret != 0) {
1195 * Do not return immediately if !(flags & O_CREAT) &&
1196 * errno != ENOENT.
1197 * Do not do this here or anywhere else. The module is
1198 * stackable and there may be modules below, such as audit
1199 * modules, which should be handled.
1201 goto virusfilter_vfs_open_next;
1203 ret = S_ISREG(smb_fname->st.st_ex_mode);
1204 if (ret == 0) {
1205 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1206 cwd_fname, fname);
1207 goto virusfilter_vfs_open_next;
1209 if (config->max_file_size > 0 &&
1210 smb_fname->st.st_ex_size > config->max_file_size)
1212 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1213 cwd_fname, fname);
1214 goto virusfilter_vfs_open_next;
1216 if (config->min_file_size > 0 &&
1217 smb_fname->st.st_ex_size < config->min_file_size)
1219 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1220 cwd_fname, fname);
1221 goto virusfilter_vfs_open_next;
1224 ok1 = is_in_path(fname, config->exclude_files, false);
1225 if (config->exclude_files && ok1)
1227 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1228 cwd_fname, fname);
1229 goto virusfilter_vfs_open_next;
1232 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1233 sret = strstr_m(fname, config->quarantine_dir);
1234 if (sret != NULL) {
1235 scan_errno = config->infected_open_errno;
1236 goto virusfilter_vfs_open_fail;
1240 if (test_prefix > 0 || test_suffix > 0) {
1241 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1242 if (ok1)
1244 if (test_prefix > 0) {
1245 ret = strncmp(base_name,
1246 config->rename_prefix, test_prefix);
1247 if (ret != 0) {
1248 test_prefix = 0;
1251 if (test_suffix > 0) {
1252 ret = strcmp(base_name + (strlen(base_name)
1253 - test_suffix),
1254 config->rename_suffix);
1255 if (ret != 0) {
1256 test_suffix = 0;
1260 TALLOC_FREE(dir_name);
1262 if ((rename_trap_count == 2 && test_prefix &&
1263 test_suffix) || (rename_trap_count == 1 &&
1264 (test_prefix || test_suffix)))
1266 scan_errno =
1267 config->infected_open_errno;
1268 goto virusfilter_vfs_open_fail;
1273 scan_result = virusfilter_scan(handle, config, fsp);
1275 switch (scan_result) {
1276 case VIRUSFILTER_RESULT_CLEAN:
1277 break;
1278 case VIRUSFILTER_RESULT_INFECTED:
1279 scan_errno = config->infected_open_errno;
1280 goto virusfilter_vfs_open_fail;
1281 case VIRUSFILTER_RESULT_ERROR:
1282 if (config->block_access_on_error) {
1283 DBG_INFO("Block access\n");
1284 scan_errno = config->scan_error_open_errno;
1285 goto virusfilter_vfs_open_fail;
1287 break;
1288 default:
1289 scan_errno = config->scan_error_open_errno;
1290 goto virusfilter_vfs_open_fail;
1293 virusfilter_vfs_open_next:
1294 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1296 virusfilter_vfs_open_fail:
1297 errno = (scan_errno != 0) ? scan_errno : EACCES;
1298 return -1;
1301 static int virusfilter_vfs_close(
1302 struct vfs_handle_struct *handle,
1303 files_struct *fsp)
1306 * The name of this variable is for consistency. If API changes to
1307 * match _open change to cwd_fname as in virusfilter_vfs_open.
1309 const char *cwd_fname = handle->conn->connectpath;
1311 struct virusfilter_config *config = NULL;
1312 char *fname = fsp->fsp_name->base_name;
1313 int close_result = -1;
1314 int close_errno = 0;
1315 virusfilter_result scan_result;
1316 int scan_errno = 0;
1317 bool ok1, ok2;
1319 SMB_VFS_HANDLE_GET_DATA(handle, config,
1320 struct virusfilter_config, return -1);
1323 * Must close after scan? It appears not as the scanners are not
1324 * internal and other modules such as greyhole seem to do
1325 * SMB_VFS_NEXT_* functions before processing.
1327 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1328 if (close_result == -1) {
1329 close_errno = errno;
1333 * Return immediately if close_result == -1, and close_errno == EBADF.
1334 * If close failed, file likely doesn't exist, do not try to scan.
1336 if (close_result == -1 && close_errno == EBADF) {
1337 if (fsp->modified) {
1338 DBG_DEBUG("Removing cache entry (if existent): "
1339 "fname: %s\n", fname);
1340 virusfilter_cache_remove(config->cache,
1341 cwd_fname, fname);
1343 goto virusfilter_vfs_close_fail;
1346 if (fsp->is_directory) {
1347 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1348 return close_result;
1351 ok1 = is_ntfs_stream_smb_fname(fsp->fsp_name);
1352 ok2 = is_ntfs_default_stream_smb_fname(fsp->fsp_name);
1353 if (ok1 && !ok2) {
1354 if (config->scan_on_open && fsp->modified) {
1355 if (config->cache) {
1356 DBG_DEBUG("Removing cache entry (if existent)"
1357 ": fname: %s\n", fname);
1358 virusfilter_cache_remove(
1359 config->cache,
1360 cwd_fname, fname);
1363 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1364 " %s/%s\n", cwd_fname, fname);
1365 return close_result;
1368 if (!config->scan_on_close) {
1369 if (config->scan_on_open && fsp->modified) {
1370 if (config->cache) {
1371 DBG_DEBUG("Removing cache entry (if existent)"
1372 ": fname: %s\n", fname);
1373 virusfilter_cache_remove(
1374 config->cache,
1375 cwd_fname, fname);
1378 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1379 cwd_fname, fname);
1380 return close_result;
1383 if (!fsp->modified) {
1384 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1385 cwd_fname, fname);
1387 return close_result;
1390 if (config->exclude_files && is_in_path(fname,
1391 config->exclude_files, false))
1393 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1394 cwd_fname, fname);
1395 return close_result;
1398 scan_result = virusfilter_scan(handle, config, fsp);
1400 switch (scan_result) {
1401 case VIRUSFILTER_RESULT_CLEAN:
1402 break;
1403 case VIRUSFILTER_RESULT_INFECTED:
1404 scan_errno = config->infected_close_errno;
1405 goto virusfilter_vfs_close_fail;
1406 case VIRUSFILTER_RESULT_ERROR:
1407 if (config->block_access_on_error) {
1408 DBG_INFO("Block access\n");
1409 scan_errno = config->scan_error_close_errno;
1410 goto virusfilter_vfs_close_fail;
1412 break;
1413 default:
1414 scan_errno = config->scan_error_close_errno;
1415 goto virusfilter_vfs_close_fail;
1418 if (close_errno != 0) {
1419 errno = close_errno;
1422 return close_result;
1424 virusfilter_vfs_close_fail:
1426 errno = (scan_errno != 0) ? scan_errno : close_errno;
1428 return close_result;
1431 static int virusfilter_vfs_unlink(
1432 struct vfs_handle_struct *handle,
1433 const struct smb_filename *smb_fname)
1435 int ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1436 struct virusfilter_config *config = NULL;
1437 char *fname = NULL;
1438 char *cwd_fname = handle->conn->cwd_fname->base_name;
1440 if (ret != 0 && errno != ENOENT) {
1441 return ret;
1444 SMB_VFS_HANDLE_GET_DATA(handle, config,
1445 struct virusfilter_config, return -1);
1447 if (config->cache == NULL) {
1448 return 0;
1451 fname = smb_fname->base_name;
1453 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1454 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1456 return 0;
1459 static int virusfilter_vfs_rename(
1460 struct vfs_handle_struct *handle,
1461 const struct smb_filename *smb_fname_src,
1462 const struct smb_filename *smb_fname_dst)
1464 int ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1465 struct virusfilter_config *config = NULL;
1466 char *fname = NULL;
1467 char *dst_fname = NULL;
1468 char *cwd_fname = handle->conn->cwd_fname->base_name;
1470 if (ret != 0) {
1471 return ret;
1474 SMB_VFS_HANDLE_GET_DATA(handle, config,
1475 struct virusfilter_config, return -1);
1477 if (config->cache == NULL) {
1478 return 0;
1481 fname = smb_fname_src->base_name;
1482 dst_fname = smb_fname_dst->base_name;
1484 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1485 fname, dst_fname);
1486 virusfilter_cache_entry_rename(config->cache,
1487 cwd_fname, fname,
1488 dst_fname);
1490 return 0;
1493 /* VFS operations */
1494 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1495 .connect_fn = virusfilter_vfs_connect,
1496 .disconnect_fn = virusfilter_vfs_disconnect,
1497 .open_fn = virusfilter_vfs_open,
1498 .close_fn = virusfilter_vfs_close,
1499 .unlink_fn = virusfilter_vfs_unlink,
1500 .rename_fn = virusfilter_vfs_rename,
1503 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1504 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1506 NTSTATUS status;
1508 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1509 "virusfilter",
1510 &vfs_virusfilter_fns);
1511 if (!NT_STATUS_IS_OK(status)) {
1512 return status;
1515 virusfilter_debug_class = debug_add_class("virusfilter");
1516 if (virusfilter_debug_class == -1) {
1517 virusfilter_debug_class = DBGC_VFS;
1518 DBG_ERR("Couldn't register custom debugging class!\n");
1519 } else {
1520 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1523 DBG_INFO("registered\n");
1525 return status;