/* Unix SMB/CIFS implementation. Copyright (C) Andrew Tridgell 2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* test offline files */ #include "includes.h" #include "torture/torture.h" #include "libcli/raw/libcliraw.h" #include "system/time.h" #include "system/filesys.h" #include "libcli/libcli.h" #include "torture/util.h" #include "lib/events/events.h" #include "lib/cmdline/popt_common.h" #include "libcli/composite/composite.h" #include "libcli/smb_composite/smb_composite.h" #include "libcli/resolve/resolve.h" #include "param/param.h" #define BASEDIR "\\testoffline" static int nconnections; static int numstates; static int num_connected; static int test_failed; extern int torture_numops; extern int torture_entries; static bool test_finished; enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST}; static double latencies[OP_ENDOFLIST]; static double worst_latencies[OP_ENDOFLIST]; #define FILE_SIZE 8192 struct offline_state { struct torture_context *tctx; struct event_context *ev; struct smbcli_tree *tree; TALLOC_CTX *mem_ctx; int client; int fnum; uint32_t count; uint32_t lastcount; uint32_t fnumber; uint32_t offline_count; uint32_t online_count; char *fname; struct smb_composite_loadfile *loadfile; struct smb_composite_savefile *savefile; struct smbcli_request *req; enum offline_op op; struct timeval tv_start; }; static void test_offline(struct offline_state *state); static char *filename(TALLOC_CTX *ctx, int i) { char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i); return s; } /* called when a loadfile completes */ static void loadfile_callback(struct composite_context *ctx) { struct offline_state *state = ctx->async.private_data; NTSTATUS status; int i; status = smb_composite_loadfile_recv(ctx, state->mem_ctx); if (!NT_STATUS_IS_OK(status)) { printf("Failed to read file '%s' - %s\n", state->loadfile->in.fname, nt_errstr(status)); test_failed++; } /* check the data is correct */ if (state->loadfile->out.size != FILE_SIZE) { printf("Wrong file size %u - expected %u\n", state->loadfile->out.size, FILE_SIZE); test_failed++; return; } for (i=0;iloadfile->out.data[i] != state->fnumber % 256) { printf("Bad data in file %u\n", state->fnumber); test_failed++; return; } } talloc_steal(state->loadfile, state->loadfile->out.data); state->count++; talloc_free(state->loadfile); state->loadfile = NULL; if (!test_finished) { test_offline(state); } } /* called when a savefile completes */ static void savefile_callback(struct composite_context *ctx) { struct offline_state *state = ctx->async.private_data; NTSTATUS status; status = smb_composite_savefile_recv(ctx); if (!NT_STATUS_IS_OK(status)) { printf("Failed to save file '%s' - %s\n", state->savefile->in.fname, nt_errstr(status)); test_failed++; } state->count++; talloc_free(state->savefile); state->savefile = NULL; if (!test_finished) { test_offline(state); } } /* called when a setoffline completes */ static void setoffline_callback(struct smbcli_request *req) { struct offline_state *state = req->async.private; NTSTATUS status; status = smbcli_request_simple_recv(req); if (!NT_STATUS_IS_OK(status)) { printf("Failed to set offline file '%s' - %s\n", state->fname, nt_errstr(status)); test_failed++; } state->req = NULL; state->count++; if (!test_finished) { test_offline(state); } } /* called when a getoffline completes */ static void getoffline_callback(struct smbcli_request *req) { struct offline_state *state = req->async.private; NTSTATUS status; union smb_fileinfo io; io.getattr.level = RAW_FILEINFO_GETATTR; status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io); if (!NT_STATUS_IS_OK(status)) { printf("Failed to get offline file '%s' - %s\n", state->fname, nt_errstr(status)); test_failed++; } if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) { state->offline_count++; } else { state->online_count++; } state->req = NULL; state->count++; if (!test_finished) { test_offline(state); } } /* send the next offline file fetch request */ static void test_offline(struct offline_state *state) { struct composite_context *ctx; double lat; lat = timeval_elapsed(&state->tv_start); if (latencies[state->op] < lat) { latencies[state->op] = lat; } state->op = (enum offline_op) (random() % OP_ENDOFLIST); state->fnumber = random() % torture_numops; talloc_free(state->fname); state->fname = filename(state->mem_ctx, state->fnumber); state->tv_start = timeval_current(); switch (state->op) { case OP_LOADFILE: state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile); state->loadfile->in.fname = state->fname; ctx = smb_composite_loadfile_send(state->tree, state->loadfile); if (ctx == NULL) { printf("Failed to setup loadfile for %s\n", state->fname); test_failed = true; } talloc_steal(state->loadfile, ctx); ctx->async.fn = loadfile_callback; ctx->async.private_data = state; break; case OP_SAVEFILE: state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile); state->savefile->in.fname = state->fname; state->savefile->in.data = talloc_size(state->savefile, FILE_SIZE); state->savefile->in.size = FILE_SIZE; memset(state->savefile->in.data, state->fnumber, FILE_SIZE); ctx = smb_composite_savefile_send(state->tree, state->savefile); if (ctx == NULL) { printf("Failed to setup savefile for %s\n", state->fname); test_failed = true; } talloc_steal(state->savefile, ctx); ctx->async.fn = savefile_callback; ctx->async.private_data = state; break; case OP_SETOFFLINE: { union smb_setfileinfo io; ZERO_STRUCT(io); io.setattr.level = RAW_SFILEINFO_SETATTR; io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE; io.setattr.in.file.path = state->fname; /* make the file 1 hour old, to get past mininum age restrictions for HSM systems */ io.setattr.in.write_time = time(NULL) - 60*60; state->req = smb_raw_setpathinfo_send(state->tree, &io); if (state->req == NULL) { printf("Failed to setup setoffline for %s\n", state->fname); test_failed = true; } state->req->async.fn = setoffline_callback; state->req->async.private = state; break; } case OP_GETOFFLINE: { union smb_fileinfo io; ZERO_STRUCT(io); io.getattr.level = RAW_FILEINFO_GETATTR; io.getattr.in.file.path = state->fname; state->req = smb_raw_pathinfo_send(state->tree, &io); if (state->req == NULL) { printf("Failed to setup getoffline for %s\n", state->fname); test_failed = true; } state->req->async.fn = getoffline_callback; state->req->async.private = state; break; } default: printf("bad operation??\n"); break; } } static void echo_completion(struct smbcli_request *req) { struct offline_state *state = (struct offline_state *)req->async.private; NTSTATUS status = smbcli_request_simple_recv(req); if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) || NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) { talloc_free(state->tree); state->tree = NULL; num_connected--; DEBUG(0,("lost connection\n")); test_failed++; } } static void report_rate(struct event_context *ev, struct timed_event *te, struct timeval t, void *private_data) { struct offline_state *state = talloc_get_type(private_data, struct offline_state); int i; uint32_t total=0, total_offline=0, total_online=0; for (i=0;i latencies[state[i].op]) { latencies[state[i].op] = timeval_elapsed(&state[i].tv_start); } state[i].lastcount = state[i].count; total_online += state[i].online_count; total_offline += state[i].offline_count; } printf("ops/s=%4u offline=%5u online=%4u set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\r", total, total_offline, total_online, latencies[OP_SETOFFLINE], latencies[OP_GETOFFLINE], latencies[OP_SAVEFILE], latencies[OP_LOADFILE]); fflush(stdout); event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state); for (i=0;i worst_latencies[i]) { worst_latencies[i] = latencies[i]; } latencies[i] = 0; } /* send an echo on each interface to ensure it stays alive - this helps with IP takeover */ for (i=0;isession->transport, &p); req->async.private = &state[i]; req->async.fn = echo_completion; } } /* test offline file handling */ bool torture_test_offline(struct torture_context *torture) { bool ret = true; TALLOC_CTX *mem_ctx = talloc_new(torture); int i; int timelimit = torture_setting_int(torture, "timelimit", 10); struct timeval tv; struct offline_state *state; struct smbcli_state *cli; bool progress; progress = torture_setting_bool(torture, "progress", true); nconnections = torture_setting_int(torture, "nprocs", 4); numstates = nconnections * torture_entries; state = talloc_zero_array(mem_ctx, struct offline_state, numstates); printf("Opening %d connections with %d simultaneous operations and %u files\n", nconnections, numstates, torture_numops); for (i=0;iev; if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) { return false; } state[i].tree = cli->tree; state[i].client = i; /* allow more time for offline files */ state[i].tree->session->transport->options.request_timeout = 200; } /* the others are repeats on the earlier connections */ for (i=nconnections;iev; state[i].tree = state[i % nconnections].tree; state[i].client = i; } num_connected = i; if (!torture_setup_dir(cli, BASEDIR)) { goto failed; } /* pre-create files */ printf("Pre-creating %u files ....\n", torture_numops); for (i=0;iev); if (test_failed) { DEBUG(0,("test failed\n")); goto failed; } } printf("\nWaiting for completion\n"); test_finished = true; for (i=0;iev); } } printf("worst latencies: set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\n", worst_latencies[OP_SETOFFLINE], worst_latencies[OP_GETOFFLINE], worst_latencies[OP_SAVEFILE], worst_latencies[OP_LOADFILE]); smbcli_deltree(state[0].tree, BASEDIR); talloc_free(mem_ctx); printf("\n"); return ret; failed: talloc_free(mem_ctx); return false; }