1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 | In-memory cache
|
---|
4 | Copyright (C) Volker Lendecke 2007
|
---|
5 |
|
---|
6 | This program is free software; you can redistribute it and/or modify
|
---|
7 | it under the terms of the GNU General Public License as published by
|
---|
8 | the Free Software Foundation; either version 3 of the License, or
|
---|
9 | (at your option) any later version.
|
---|
10 |
|
---|
11 | This program is distributed in the hope that it will be useful,
|
---|
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | GNU General Public License for more details.
|
---|
15 |
|
---|
16 | You should have received a copy of the GNU General Public License
|
---|
17 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
18 | */
|
---|
19 |
|
---|
20 | #include "includes.h"
|
---|
21 | #include "torture/proto.h"
|
---|
22 | #include "libsmb/libsmb.h"
|
---|
23 | #include "libsmb/clirap.h"
|
---|
24 | #include "../lib/util/tevent_ntstatus.h"
|
---|
25 |
|
---|
26 | static long long int ival(const char *str)
|
---|
27 | {
|
---|
28 | return strtoll(str, NULL, 0);
|
---|
29 | }
|
---|
30 |
|
---|
31 | struct nbench_state {
|
---|
32 | struct tevent_context *ev;
|
---|
33 | struct cli_state *cli;
|
---|
34 | const char *cliname;
|
---|
35 | FILE *loadfile;
|
---|
36 | struct ftable *ftable;
|
---|
37 | void (*bw_report)(size_t nread,
|
---|
38 | size_t nwritten,
|
---|
39 | void *private_data);
|
---|
40 | void *bw_report_private;
|
---|
41 | };
|
---|
42 |
|
---|
43 | struct lock_info {
|
---|
44 | struct lock_info *next, *prev;
|
---|
45 | off_t offset;
|
---|
46 | int size;
|
---|
47 | };
|
---|
48 |
|
---|
49 | struct createx_params {
|
---|
50 | char *fname;
|
---|
51 | unsigned int cr_options;
|
---|
52 | unsigned int cr_disposition;
|
---|
53 | int handle;
|
---|
54 | };
|
---|
55 |
|
---|
56 | struct ftable {
|
---|
57 | struct ftable *next, *prev;
|
---|
58 | struct createx_params cp;
|
---|
59 | struct lock_info *locks;
|
---|
60 | uint16_t fnum; /* the fd that we got back from the server */
|
---|
61 | };
|
---|
62 |
|
---|
63 | enum nbench_cmd {
|
---|
64 | NBENCH_CMD_NTCREATEX,
|
---|
65 | NBENCH_CMD_CLOSE,
|
---|
66 | NBENCH_CMD_RENAME,
|
---|
67 | NBENCH_CMD_UNLINK,
|
---|
68 | NBENCH_CMD_DELTREE,
|
---|
69 | NBENCH_CMD_RMDIR,
|
---|
70 | NBENCH_CMD_MKDIR,
|
---|
71 | NBENCH_CMD_QUERY_PATH_INFORMATION,
|
---|
72 | NBENCH_CMD_QUERY_FILE_INFORMATION,
|
---|
73 | NBENCH_CMD_QUERY_FS_INFORMATION,
|
---|
74 | NBENCH_CMD_SET_FILE_INFORMATION,
|
---|
75 | NBENCH_CMD_FIND_FIRST,
|
---|
76 | NBENCH_CMD_WRITEX,
|
---|
77 | NBENCH_CMD_WRITE,
|
---|
78 | NBENCH_CMD_LOCKX,
|
---|
79 | NBENCH_CMD_UNLOCKX,
|
---|
80 | NBENCH_CMD_READX,
|
---|
81 | NBENCH_CMD_FLUSH,
|
---|
82 | NBENCH_CMD_SLEEP,
|
---|
83 | };
|
---|
84 |
|
---|
85 | struct nbench_cmd_struct {
|
---|
86 | char **params;
|
---|
87 | int num_params;
|
---|
88 | NTSTATUS status;
|
---|
89 | enum nbench_cmd cmd;
|
---|
90 | };
|
---|
91 |
|
---|
92 | static struct nbench_cmd_struct *nbench_parse(TALLOC_CTX *mem_ctx,
|
---|
93 | const char *line)
|
---|
94 | {
|
---|
95 | struct nbench_cmd_struct *result;
|
---|
96 | char *cmd;
|
---|
97 | char *status;
|
---|
98 |
|
---|
99 | result = TALLOC_P(mem_ctx, struct nbench_cmd_struct);
|
---|
100 | if (result == NULL) {
|
---|
101 | return NULL;
|
---|
102 | }
|
---|
103 | result->params = str_list_make_shell(mem_ctx, line, " ");
|
---|
104 | if (result->params == NULL) {
|
---|
105 | goto fail;
|
---|
106 | }
|
---|
107 | result->num_params = talloc_array_length(result->params) - 1;
|
---|
108 | if (result->num_params < 2) {
|
---|
109 | goto fail;
|
---|
110 | }
|
---|
111 | status = result->params[result->num_params-1];
|
---|
112 | if (strncmp(status, "NT_STATUS_", 10) != 0 &&
|
---|
113 | strncmp(status, "0x", 2) != 0) {
|
---|
114 | goto fail;
|
---|
115 | }
|
---|
116 | /* accept numeric or string status codes */
|
---|
117 | if (strncmp(status, "0x", 2) == 0) {
|
---|
118 | result->status = NT_STATUS(strtoul(status, NULL, 16));
|
---|
119 | } else {
|
---|
120 | result->status = nt_status_string_to_code(status);
|
---|
121 | }
|
---|
122 |
|
---|
123 | cmd = result->params[0];
|
---|
124 |
|
---|
125 | if (!strcmp(cmd, "NTCreateX")) {
|
---|
126 | result->cmd = NBENCH_CMD_NTCREATEX;
|
---|
127 | } else if (!strcmp(cmd, "Close")) {
|
---|
128 | result->cmd = NBENCH_CMD_CLOSE;
|
---|
129 | } else if (!strcmp(cmd, "Rename")) {
|
---|
130 | result->cmd = NBENCH_CMD_RENAME;
|
---|
131 | } else if (!strcmp(cmd, "Unlink")) {
|
---|
132 | result->cmd = NBENCH_CMD_UNLINK;
|
---|
133 | } else if (!strcmp(cmd, "Deltree")) {
|
---|
134 | result->cmd = NBENCH_CMD_DELTREE;
|
---|
135 | } else if (!strcmp(cmd, "Rmdir")) {
|
---|
136 | result->cmd = NBENCH_CMD_RMDIR;
|
---|
137 | } else if (!strcmp(cmd, "Mkdir")) {
|
---|
138 | result->cmd = NBENCH_CMD_MKDIR;
|
---|
139 | } else if (!strcmp(cmd, "QUERY_PATH_INFORMATION")) {
|
---|
140 | result->cmd = NBENCH_CMD_QUERY_PATH_INFORMATION;
|
---|
141 | } else if (!strcmp(cmd, "QUERY_FILE_INFORMATION")) {
|
---|
142 | result->cmd = NBENCH_CMD_QUERY_FILE_INFORMATION;
|
---|
143 | } else if (!strcmp(cmd, "QUERY_FS_INFORMATION")) {
|
---|
144 | result->cmd = NBENCH_CMD_QUERY_FS_INFORMATION;
|
---|
145 | } else if (!strcmp(cmd, "SET_FILE_INFORMATION")) {
|
---|
146 | result->cmd = NBENCH_CMD_SET_FILE_INFORMATION;
|
---|
147 | } else if (!strcmp(cmd, "FIND_FIRST")) {
|
---|
148 | result->cmd = NBENCH_CMD_FIND_FIRST;
|
---|
149 | } else if (!strcmp(cmd, "WriteX")) {
|
---|
150 | result->cmd = NBENCH_CMD_WRITEX;
|
---|
151 | } else if (!strcmp(cmd, "Write")) {
|
---|
152 | result->cmd = NBENCH_CMD_WRITE;
|
---|
153 | } else if (!strcmp(cmd, "LockX")) {
|
---|
154 | result->cmd = NBENCH_CMD_LOCKX;
|
---|
155 | } else if (!strcmp(cmd, "UnlockX")) {
|
---|
156 | result->cmd = NBENCH_CMD_UNLOCKX;
|
---|
157 | } else if (!strcmp(cmd, "ReadX")) {
|
---|
158 | result->cmd = NBENCH_CMD_READX;
|
---|
159 | } else if (!strcmp(cmd, "Flush")) {
|
---|
160 | result->cmd = NBENCH_CMD_FLUSH;
|
---|
161 | } else if (!strcmp(cmd, "Sleep")) {
|
---|
162 | result->cmd = NBENCH_CMD_SLEEP;
|
---|
163 | } else {
|
---|
164 | goto fail;
|
---|
165 | }
|
---|
166 | return result;
|
---|
167 | fail:
|
---|
168 | TALLOC_FREE(result);
|
---|
169 | return NULL;
|
---|
170 | }
|
---|
171 |
|
---|
172 | static struct ftable *ft_find(struct ftable *ftlist, int handle)
|
---|
173 | {
|
---|
174 | while (ftlist != NULL) {
|
---|
175 | if (ftlist->cp.handle == handle) {
|
---|
176 | return ftlist;
|
---|
177 | }
|
---|
178 | ftlist = ftlist->next;
|
---|
179 | }
|
---|
180 | return NULL;
|
---|
181 | }
|
---|
182 |
|
---|
183 | struct nbench_cmd_state {
|
---|
184 | struct tevent_context *ev;
|
---|
185 | struct nbench_state *state;
|
---|
186 | struct nbench_cmd_struct *cmd;
|
---|
187 | struct ftable *ft;
|
---|
188 | bool eof;
|
---|
189 | };
|
---|
190 |
|
---|
191 | static void nbench_cmd_done(struct tevent_req *subreq);
|
---|
192 |
|
---|
193 | static struct tevent_req *nbench_cmd_send(TALLOC_CTX *mem_ctx,
|
---|
194 | struct tevent_context *ev,
|
---|
195 | struct nbench_state *nb_state)
|
---|
196 | {
|
---|
197 | struct tevent_req *req, *subreq;
|
---|
198 | struct nbench_cmd_state *state;
|
---|
199 | char line[1024];
|
---|
200 | size_t len;
|
---|
201 |
|
---|
202 | req = tevent_req_create(mem_ctx, &state, struct nbench_cmd_state);
|
---|
203 | if (req == NULL) {
|
---|
204 | return NULL;
|
---|
205 | }
|
---|
206 | state->ev = ev;
|
---|
207 | state->state = nb_state;
|
---|
208 |
|
---|
209 | if (fgets(line, sizeof(line), nb_state->loadfile) == NULL) {
|
---|
210 | tevent_req_nterror(req, NT_STATUS_END_OF_FILE);
|
---|
211 | return tevent_req_post(req, ev);
|
---|
212 | }
|
---|
213 | len = strlen(line);
|
---|
214 | if (len == 0) {
|
---|
215 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
---|
216 | return tevent_req_post(req, ev);
|
---|
217 | }
|
---|
218 | if (line[len-1] == '\n') {
|
---|
219 | line[len-1] = '\0';
|
---|
220 | }
|
---|
221 |
|
---|
222 | state->cmd = nbench_parse(state, line);
|
---|
223 | if (state->cmd == NULL) {
|
---|
224 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
---|
225 | return tevent_req_post(req, ev);
|
---|
226 | }
|
---|
227 |
|
---|
228 | switch (state->cmd->cmd) {
|
---|
229 | case NBENCH_CMD_NTCREATEX: {
|
---|
230 | uint32_t desired_access;
|
---|
231 | uint32_t share_mode;
|
---|
232 | unsigned int flags = 0;
|
---|
233 |
|
---|
234 | state->ft = talloc(state, struct ftable);
|
---|
235 | if (tevent_req_nomem(state->ft, req)) {
|
---|
236 | return tevent_req_post(req, ev);
|
---|
237 | }
|
---|
238 |
|
---|
239 | state->ft->cp.fname = talloc_all_string_sub(
|
---|
240 | state->ft, state->cmd->params[1], "client1",
|
---|
241 | nb_state->cliname);
|
---|
242 | if (tevent_req_nomem(state->ft->cp.fname, req)) {
|
---|
243 | return tevent_req_post(req, ev);
|
---|
244 | }
|
---|
245 | state->ft->cp.cr_options = ival(state->cmd->params[2]);
|
---|
246 | state->ft->cp.cr_disposition = ival(state->cmd->params[3]);
|
---|
247 | state->ft->cp.handle = ival(state->cmd->params[4]);
|
---|
248 |
|
---|
249 | if (state->ft->cp.cr_options & FILE_DIRECTORY_FILE) {
|
---|
250 | desired_access = SEC_FILE_READ_DATA;
|
---|
251 | } else {
|
---|
252 | desired_access =
|
---|
253 | SEC_FILE_READ_DATA |
|
---|
254 | SEC_FILE_WRITE_DATA |
|
---|
255 | SEC_FILE_READ_ATTRIBUTE |
|
---|
256 | SEC_FILE_WRITE_ATTRIBUTE;
|
---|
257 | flags = EXTENDED_RESPONSE_REQUIRED
|
---|
258 | | REQUEST_OPLOCK | REQUEST_BATCH_OPLOCK;
|
---|
259 | }
|
---|
260 | share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
---|
261 |
|
---|
262 | subreq = cli_ntcreate_send(
|
---|
263 | state, ev, nb_state->cli, state->ft->cp.fname, flags,
|
---|
264 | desired_access, 0, share_mode,
|
---|
265 | state->ft->cp.cr_disposition,
|
---|
266 | state->ft->cp.cr_options, 0);
|
---|
267 | break;
|
---|
268 | }
|
---|
269 | case NBENCH_CMD_CLOSE: {
|
---|
270 | state->ft = ft_find(state->state->ftable,
|
---|
271 | ival(state->cmd->params[1]));
|
---|
272 | if (state->ft == NULL) {
|
---|
273 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
---|
274 | return tevent_req_post(req, ev);
|
---|
275 | }
|
---|
276 | subreq = cli_close_send(
|
---|
277 | state, ev, nb_state->cli, state->ft->fnum);
|
---|
278 | break;
|
---|
279 | }
|
---|
280 | case NBENCH_CMD_MKDIR: {
|
---|
281 | char *fname;
|
---|
282 | fname = talloc_all_string_sub(
|
---|
283 | state, state->cmd->params[1], "client1",
|
---|
284 | nb_state->cliname);
|
---|
285 | if (tevent_req_nomem(state->ft->cp.fname, req)) {
|
---|
286 | return tevent_req_post(req, ev);
|
---|
287 | }
|
---|
288 | subreq = cli_mkdir_send(state, ev, nb_state->cli, fname);
|
---|
289 | break;
|
---|
290 | }
|
---|
291 | case NBENCH_CMD_QUERY_PATH_INFORMATION: {
|
---|
292 | char *fname;
|
---|
293 | fname = talloc_all_string_sub(
|
---|
294 | state, state->cmd->params[1], "client1",
|
---|
295 | nb_state->cliname);
|
---|
296 | if (tevent_req_nomem(state->ft->cp.fname, req)) {
|
---|
297 | return tevent_req_post(req, ev);
|
---|
298 | }
|
---|
299 | subreq = cli_qpathinfo_send(state, ev, nb_state->cli, fname,
|
---|
300 | ival(state->cmd->params[2]),
|
---|
301 | 0, nb_state->cli->max_xmit);
|
---|
302 | break;
|
---|
303 | }
|
---|
304 | default:
|
---|
305 | tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
|
---|
306 | return tevent_req_post(req, ev);
|
---|
307 | }
|
---|
308 |
|
---|
309 | if (tevent_req_nomem(subreq, req)) {
|
---|
310 | return tevent_req_post(req, ev);
|
---|
311 | }
|
---|
312 | tevent_req_set_callback(subreq, nbench_cmd_done, req);
|
---|
313 | return req;
|
---|
314 | }
|
---|
315 |
|
---|
316 | static bool status_wrong(struct tevent_req *req, NTSTATUS expected,
|
---|
317 | NTSTATUS status)
|
---|
318 | {
|
---|
319 | if (NT_STATUS_EQUAL(expected, status)) {
|
---|
320 | return false;
|
---|
321 | }
|
---|
322 | if (NT_STATUS_IS_OK(status)) {
|
---|
323 | status = NT_STATUS_INVALID_NETWORK_RESPONSE;
|
---|
324 | }
|
---|
325 | tevent_req_nterror(req, status);
|
---|
326 | return true;
|
---|
327 | }
|
---|
328 |
|
---|
329 | static void nbench_cmd_done(struct tevent_req *subreq)
|
---|
330 | {
|
---|
331 | struct tevent_req *req = tevent_req_callback_data(
|
---|
332 | subreq, struct tevent_req);
|
---|
333 | struct nbench_cmd_state *state = tevent_req_data(
|
---|
334 | req, struct nbench_cmd_state);
|
---|
335 | struct nbench_state *nbstate = state->state;
|
---|
336 | NTSTATUS status;
|
---|
337 |
|
---|
338 | switch (state->cmd->cmd) {
|
---|
339 | case NBENCH_CMD_NTCREATEX: {
|
---|
340 | struct ftable *ft;
|
---|
341 | status = cli_ntcreate_recv(subreq, &state->ft->fnum);
|
---|
342 | TALLOC_FREE(subreq);
|
---|
343 | if (status_wrong(req, state->cmd->status, status)) {
|
---|
344 | return;
|
---|
345 | }
|
---|
346 | if (!NT_STATUS_IS_OK(status)) {
|
---|
347 | tevent_req_done(req);
|
---|
348 | return;
|
---|
349 | }
|
---|
350 | ft = talloc_move(nbstate, &state->ft);
|
---|
351 | DLIST_ADD(nbstate->ftable, ft);
|
---|
352 | break;
|
---|
353 | }
|
---|
354 | case NBENCH_CMD_CLOSE: {
|
---|
355 | status = cli_close_recv(subreq);
|
---|
356 | TALLOC_FREE(subreq);
|
---|
357 | if (status_wrong(req, state->cmd->status, status)) {
|
---|
358 | return;
|
---|
359 | }
|
---|
360 | DLIST_REMOVE(state->state->ftable, state->ft);
|
---|
361 | TALLOC_FREE(state->ft);
|
---|
362 | break;
|
---|
363 | }
|
---|
364 | case NBENCH_CMD_MKDIR: {
|
---|
365 | status = cli_mkdir_recv(subreq);
|
---|
366 | TALLOC_FREE(subreq);
|
---|
367 | if (status_wrong(req, state->cmd->status, status)) {
|
---|
368 | return;
|
---|
369 | }
|
---|
370 | break;
|
---|
371 | }
|
---|
372 | case NBENCH_CMD_QUERY_PATH_INFORMATION: {
|
---|
373 | status = cli_qpathinfo_recv(subreq, NULL, NULL, NULL);
|
---|
374 | TALLOC_FREE(subreq);
|
---|
375 | if (status_wrong(req, state->cmd->status, status)) {
|
---|
376 | return;
|
---|
377 | }
|
---|
378 | break;
|
---|
379 | }
|
---|
380 | default:
|
---|
381 | break;
|
---|
382 | }
|
---|
383 | tevent_req_done(req);
|
---|
384 | }
|
---|
385 |
|
---|
386 | static NTSTATUS nbench_cmd_recv(struct tevent_req *req)
|
---|
387 | {
|
---|
388 | return tevent_req_simple_recv_ntstatus(req);
|
---|
389 | }
|
---|
390 |
|
---|
391 | static void nbench_done(struct tevent_req *subreq);
|
---|
392 |
|
---|
393 | static struct tevent_req *nbench_send(
|
---|
394 | TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
|
---|
395 | const char *cliname, FILE *loadfile,
|
---|
396 | void (*bw_report)(size_t nread, size_t nwritten, void *private_data),
|
---|
397 | void *bw_report_private)
|
---|
398 | {
|
---|
399 | struct tevent_req *req, *subreq;
|
---|
400 | struct nbench_state *state;
|
---|
401 |
|
---|
402 | req = tevent_req_create(mem_ctx, &state, struct nbench_state);
|
---|
403 | if (req == NULL) {
|
---|
404 | return NULL;
|
---|
405 | }
|
---|
406 | state->ev = ev;
|
---|
407 | state->cli = cli;
|
---|
408 | state->cliname = cliname;
|
---|
409 | state->loadfile = loadfile;
|
---|
410 | state->bw_report = bw_report;
|
---|
411 | state->bw_report_private = bw_report_private;
|
---|
412 |
|
---|
413 | subreq = nbench_cmd_send(state, ev, state);
|
---|
414 | if (tevent_req_nomem(subreq, req)) {
|
---|
415 | return tevent_req_post(req, ev);
|
---|
416 | }
|
---|
417 | tevent_req_set_callback(subreq, nbench_done, req);
|
---|
418 | return req;
|
---|
419 | }
|
---|
420 |
|
---|
421 | static void nbench_done(struct tevent_req *subreq)
|
---|
422 | {
|
---|
423 | struct tevent_req *req = tevent_req_callback_data(
|
---|
424 | subreq, struct tevent_req);
|
---|
425 | struct nbench_state *state = tevent_req_data(
|
---|
426 | req, struct nbench_state);
|
---|
427 | NTSTATUS status;
|
---|
428 |
|
---|
429 | status = nbench_cmd_recv(subreq);
|
---|
430 | TALLOC_FREE(subreq);
|
---|
431 |
|
---|
432 | if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
|
---|
433 | tevent_req_done(req);
|
---|
434 | return;
|
---|
435 | }
|
---|
436 | if (!NT_STATUS_IS_OK(status)) {
|
---|
437 | tevent_req_nterror(req, status);
|
---|
438 | return;
|
---|
439 | }
|
---|
440 | subreq = nbench_cmd_send(state, state->ev, state);
|
---|
441 | if (tevent_req_nomem(subreq, req)) {
|
---|
442 | return;
|
---|
443 | }
|
---|
444 | tevent_req_set_callback(subreq, nbench_done, req);
|
---|
445 | }
|
---|
446 |
|
---|
447 | static NTSTATUS nbench_recv(struct tevent_req *req)
|
---|
448 | {
|
---|
449 | return tevent_req_simple_recv_ntstatus(req);
|
---|
450 | }
|
---|
451 |
|
---|
452 | bool run_nbench2(int dummy)
|
---|
453 | {
|
---|
454 | TALLOC_CTX *frame = talloc_stackframe();
|
---|
455 | struct tevent_context *ev;
|
---|
456 | struct cli_state *cli = NULL;
|
---|
457 | FILE *loadfile;
|
---|
458 | bool ret = false;
|
---|
459 | struct tevent_req *req;
|
---|
460 | NTSTATUS status;
|
---|
461 |
|
---|
462 | loadfile = fopen("client.txt", "r");
|
---|
463 | if (loadfile == NULL) {
|
---|
464 | fprintf(stderr, "Could not open \"client.txt\": %s\n",
|
---|
465 | strerror(errno));
|
---|
466 | return false;
|
---|
467 | }
|
---|
468 | ev = tevent_context_init(talloc_tos());
|
---|
469 | if (ev == NULL) {
|
---|
470 | goto fail;
|
---|
471 | }
|
---|
472 | if (!torture_open_connection(&cli, 0)) {
|
---|
473 | goto fail;
|
---|
474 | }
|
---|
475 |
|
---|
476 | req = nbench_send(talloc_tos(), ev, cli, "client1", loadfile,
|
---|
477 | NULL, NULL);
|
---|
478 | if (req == NULL) {
|
---|
479 | goto fail;
|
---|
480 | }
|
---|
481 | if (!tevent_req_poll(req, ev)) {
|
---|
482 | goto fail;
|
---|
483 | }
|
---|
484 | status = nbench_recv(req);
|
---|
485 | TALLOC_FREE(req);
|
---|
486 | printf("nbench returned %s\n", nt_errstr(status));
|
---|
487 |
|
---|
488 | ret = true;
|
---|
489 | fail:
|
---|
490 | if (cli != NULL) {
|
---|
491 | torture_close_connection(cli);
|
---|
492 | }
|
---|
493 | TALLOC_FREE(ev);
|
---|
494 | if (loadfile != NULL) {
|
---|
495 | fclose(loadfile);
|
---|
496 | loadfile = NULL;
|
---|
497 | }
|
---|
498 | TALLOC_FREE(frame);
|
---|
499 | return ret;
|
---|
500 | }
|
---|