return -1;
}
+static int virusfilter_vfs_openat(struct vfs_handle_struct *handle,
+ const struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname_in,
+ struct files_struct *fsp,
+ int flags,
+ mode_t mode)
+{
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ struct virusfilter_config *config = NULL;
+ const char *cwd_fname = dirfsp->fsp_name->base_name;
+ virusfilter_result scan_result;
+ const char *fname = fsp->fsp_name->base_name;
+ char *dir_name = NULL;
+ const char *base_name = NULL;
+ int scan_errno = 0;
+ size_t test_prefix;
+ size_t test_suffix;
+ int rename_trap_count = 0;
+ int ret;
+ bool ok1;
+ char *sret = NULL;
+ struct smb_filename *smb_fname = NULL;
+
+ /*
+ * For now assert this, so SMB_VFS_NEXT_STAT() below works.
+ */
+ SMB_ASSERT(dirfsp->fh->fd == AT_FDCWD);
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct virusfilter_config, return -1);
+
+ if (fsp->fsp_flags.is_directory) {
+ DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ test_prefix = strlen(config->rename_prefix);
+ test_suffix = strlen(config->rename_suffix);
+ if (test_prefix > 0) {
+ rename_trap_count++;
+ }
+ if (test_suffix > 0) {
+ rename_trap_count++;
+ }
+
+ smb_fname = cp_smb_filename(mem_ctx, smb_fname_in);
+ if (smb_fname == NULL) {
+ goto virusfilter_vfs_open_fail;
+ }
+
+ if (is_named_stream(smb_fname)) {
+ DBG_INFO("Not scanned: only file backed streams can be scanned:"
+ " %s/%s\n", cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ if (!config->scan_on_open) {
+ DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ if (flags & O_TRUNC) {
+ DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+ if (ret != 0) {
+
+ /*
+ * Do not return immediately if !(flags & O_CREAT) &&
+ * errno != ENOENT.
+ * Do not do this here or anywhere else. The module is
+ * stackable and there may be modules below, such as audit
+ * modules, which should be handled.
+ */
+ goto virusfilter_vfs_open_next;
+ }
+ ret = S_ISREG(smb_fname->st.st_ex_mode);
+ if (ret == 0) {
+ DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+ if (config->max_file_size > 0 &&
+ smb_fname->st.st_ex_size > config->max_file_size)
+ {
+ DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+ if (config->min_file_size > 0 &&
+ smb_fname->st.st_ex_size < config->min_file_size)
+ {
+ DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ ok1 = is_in_path(fname, config->exclude_files, false);
+ if (config->exclude_files && ok1)
+ {
+ DBG_INFO("Not scanned: exclude files: %s/%s\n",
+ cwd_fname, fname);
+ goto virusfilter_vfs_open_next;
+ }
+
+ if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
+ sret = strstr_m(fname, config->quarantine_dir);
+ if (sret != NULL) {
+ scan_errno = config->infected_open_errno;
+ goto virusfilter_vfs_open_fail;
+ }
+ }
+
+ if (test_prefix > 0 || test_suffix > 0) {
+ ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
+ if (ok1)
+ {
+ if (test_prefix > 0) {
+ ret = strncmp(base_name,
+ config->rename_prefix, test_prefix);
+ if (ret != 0) {
+ test_prefix = 0;
+ }
+ }
+ if (test_suffix > 0) {
+ ret = strcmp(base_name + (strlen(base_name)
+ - test_suffix),
+ config->rename_suffix);
+ if (ret != 0) {
+ test_suffix = 0;
+ }
+ }
+
+ TALLOC_FREE(dir_name);
+
+ if ((rename_trap_count == 2 && test_prefix &&
+ test_suffix) || (rename_trap_count == 1 &&
+ (test_prefix || test_suffix)))
+ {
+ scan_errno =
+ config->infected_open_errno;
+ goto virusfilter_vfs_open_fail;
+ }
+ }
+ }
+
+ scan_result = virusfilter_scan(handle, config, fsp);
+
+ switch (scan_result) {
+ case VIRUSFILTER_RESULT_CLEAN:
+ break;
+ case VIRUSFILTER_RESULT_INFECTED:
+ scan_errno = config->infected_open_errno;
+ goto virusfilter_vfs_open_fail;
+ case VIRUSFILTER_RESULT_ERROR:
+ if (config->block_access_on_error) {
+ DBG_INFO("Block access\n");
+ scan_errno = config->scan_error_open_errno;
+ goto virusfilter_vfs_open_fail;
+ }
+ break;
+ default:
+ scan_errno = config->scan_error_open_errno;
+ goto virusfilter_vfs_open_fail;
+ }
+
+ TALLOC_FREE(smb_fname);
+
+virusfilter_vfs_open_next:
+ return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, flags, mode);
+
+virusfilter_vfs_open_fail:
+ TALLOC_FREE(smb_fname);
+ errno = (scan_errno != 0) ? scan_errno : EACCES;
+ return -1;
+}
+
static int virusfilter_vfs_close(
struct vfs_handle_struct *handle,
files_struct *fsp)
.connect_fn = virusfilter_vfs_connect,
.disconnect_fn = virusfilter_vfs_disconnect,
.open_fn = virusfilter_vfs_open,
+ .openat_fn = virusfilter_vfs_openat,
.close_fn = virusfilter_vfs_close,
.unlinkat_fn = virusfilter_vfs_unlinkat,
.renameat_fn = virusfilter_vfs_renameat,