2 Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2009
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include <sys/types.h>
23 #include <sys/socket.h>
25 #include <libsmbclient.h>
28 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
30 static char *smb_domain;
31 static char *smb_user;
32 static char *smb_password;
33 static char *smb_server;
34 static char *smb_share;
36 typedef struct _data_t {
41 typedef struct _smb_handle_t {
45 /* a tree to store all open handles, indexed by path */
46 typedef struct _tree_t {
49 struct _tree_t *parent;
51 struct _tree_t *right;
54 static tree_t *open_files;
57 static tree_t *find_path(tree_t *tree, const char *path)
65 i = strcmp(path, tree->path.dptr);
70 return find_path(tree->left, path);
73 return find_path(tree->right, path);
76 static smb_handle_t *lookup_path(const char *path)
80 t = find_path(open_files, path);
88 static void free_node(tree_t *t)
90 free(discard_const(t->path.dptr));
94 static void delete_path(const char *path)
98 t = find_path(open_files, path);
103 /* we have a left child */
107 for(tmp_tree=t->left;tmp_tree->right;tmp_tree=tmp_tree->right)
109 tmp_tree->right = t->right;
111 t->right->parent = tmp_tree;
114 if (t->parent == NULL) {
115 open_files = tmp_tree;
116 tmp_tree->parent = NULL;
121 if (t->parent->left == t) {
122 t->parent->left = t->left;
124 t->left->parent = t->parent;
130 t->parent->right = t->left;
132 t->left->parent = t->parent;
138 /* we only have a right child */
142 for(tmp_tree=t->right;tmp_tree->left;tmp_tree=tmp_tree->left)
144 tmp_tree->left = t->left;
146 t->left->parent = tmp_tree;
149 if (t->parent == NULL) {
150 open_files = tmp_tree;
151 tmp_tree->parent = NULL;
156 if (t->parent->left == t) {
157 t->parent->left = t->right;
159 t->right->parent = t->parent;
165 t->parent->right = t->right;
167 t->right->parent = t->parent;
173 /* we are a leaf node */
174 if (t->parent == NULL) {
177 if (t->parent->left == t) {
178 t->parent->left = NULL;
180 t->parent->right = NULL;
187 static void insert_path(const char *path, smb_handle_t *hnd)
193 tmp_t = find_path(open_files, path);
198 t = malloc(sizeof(tree_t));
200 fprintf(stderr, "MALLOC failed to allocate tree_t in insert_fhandle\n");
204 t->path.dptr = strdup(path);
205 if (t->path.dptr == NULL) {
206 fprintf(stderr, "STRDUP failed to allocate key in insert_fhandle\n");
209 t->path.dsize = strlen(path);
217 if (open_files == NULL) {
224 i = strcmp(t->path.dptr, tmp_t->path.dptr);
226 tmp_t->handle = t->handle;
227 free(discard_const(t->path.dptr));
232 if (tmp_t->left == NULL) {
240 if (tmp_t->right == NULL) {
245 tmp_t = tmp_t->right;
257 static void failed(struct child_struct *child)
260 fprintf(stderr, "ERROR: child %d failed at line %d\n", child->id, child->line);
264 static int check_status(int ret, const char *status)
266 if (!strcmp(status, "*")) {
270 if ((!strcmp(status, "SUCCESS")) && (ret == 0)) {
274 if ((!strcmp(status, "ERROR")) && (ret != 0)) {
282 void smb_auth_fn(const char *server, const char *share, char *wrkgrp, int wrkgrplen, char *user, int userlen, char *passwd, int passwdlen)
287 if (smb_domain != NULL) {
288 strncpy(wrkgrp, smb_domain, wrkgrplen - 1); wrkgrp[wrkgrplen - 1] = 0;
290 strncpy(user, smb_user, userlen - 1); user[userlen - 1] = 0;
291 strncpy(passwd, smb_password, passwdlen - 1); passwd[passwdlen - 1] = 0;
294 static int smb_init(void)
301 if (options.smb_share == NULL) {
302 fprintf(stderr, "You must specify --smb-share=<share> with the \"smb\" backend.\n");
305 if (options.smb_share[0] != '/' || options.smb_share[1] != '/') {
306 fprintf(stderr, "--smb-share Must be of the form //SERVER/SHARE[/PATH]\n");
309 smb_server = strdup(options.smb_share+2);
310 tmp = index(smb_server, '/');
312 fprintf(stderr, "--smb-share Must be of the form //SERVER/SHARE[/PATH]\n");
318 if (options.smb_user == NULL) {
319 fprintf(stderr, "You must specify --smb-user=[<domain>/]<user>%%<password> with the \"smb\" backend.\n");
323 smb_domain = strdup(options.smb_user);
324 tmp = index(smb_domain, '/');
326 smb_user = smb_domain;
332 tmp = index(smb_user, '%');
336 smb_password = tmp+1;
340 ctx = smbc_new_context();
342 fprintf(stderr, "Could not allocate SMB Context\n");
346 smbc_setDebug(ctx, 0);
347 smbc_setFunctionAuthData(ctx, smb_auth_fn);
349 if (!smbc_init_context(ctx)) {
350 smbc_free_context(ctx, 0);
351 fprintf(stderr, "failed to initialize context\n");
354 smbc_set_context(ctx);
356 asprintf(&str, "smb://%s/%s", smb_server, smb_share);
357 ret = smbc_opendir(str);
361 fprintf(stderr, "Failed to access //%s/%s\n", smb_server, smb_share);
365 smbc_free_context(ctx, 1);
369 static void smb_setup(struct child_struct *child)
371 struct smb_child *ctx;
375 ctx = malloc(sizeof(struct smb_child));
377 fprintf(stderr, "Failed to malloc child ctx\n");
382 ctx->ctx = smbc_new_context();
383 if (ctx->ctx == NULL) {
384 fprintf(stderr, "Could not allocate SMB Context\n");
388 smbc_setDebug(ctx->ctx, 0);
389 smbc_setFunctionAuthData(ctx->ctx, smb_auth_fn);
391 if (!smbc_init_context(ctx->ctx)) {
392 smbc_free_context(ctx->ctx, 0);
393 fprintf(stderr, "failed to initialize context\n");
396 smbc_setOptionUrlEncodeReaddirEntries(ctx->ctx, True);
397 smbc_set_context(ctx->ctx);
400 /* create clients and /clients/client? */
401 asprintf(&str, "smb://%s/%s/clients", smb_server, smb_share);
402 smbc_mkdir(str, 0777);
404 asprintf(&str, "smb://%s/%s/clients/client%d", smb_server, smb_share, child->id);
405 smbc_mkdir(str, 0777);
409 asprintf(&smb_share, "%s/clients/client%d", smb_share, child->id);
412 static void smb_mkdir(struct dbench_op *op)
420 asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, dir);
422 ret = smbc_mkdir(str, 0777);
425 if (check_status(ret, op->status)) {
426 fprintf(stderr, "[%d] MKDIR \"%s\" failed - expected %s, got %d\n", op->child->line, dir, op->status, ret);
431 static void smb_rmdir(struct dbench_op *op)
438 asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, dir);
439 ret = smbc_rmdir(str);
442 if (check_status(ret, op->status)) {
443 fprintf(stderr, "[%d] RMDIR \"%s\" failed - expected %s, got %d\n", op->child->line, dir, op->status, ret);
448 static void smb_open(struct dbench_op *op)
455 if (op->params[0] & 0x01) {
458 if (op->params[0] & 0x02) {
461 if (op->params[0] & 0x04) {
464 if (op->params[0] & 0x08) {
467 if (op->params[0] & 0x10) {
470 if (op->params[0] & 0x20) {
473 if (op->params[0] & 0x40) {
477 file = op->fname + 2;
478 asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, file);
480 hnd.fd = smbc_open(str, flags, 0777);
483 if (check_status(hnd.fd<0?-1:0, op->status)) {
484 fprintf(stderr, "[%d] OPEN \"%s\" failed\n", op->child->line, file);
489 insert_path(file, &hnd);
493 static void smb_close(struct dbench_op *op)
499 file = op->fname + 2;
501 hnd = lookup_path(file);
503 fprintf(stderr, "[%d] CLOSE \"%s\" failed. This file is not open.\n", op->child->line, file);
508 ret = smbc_close(hnd->fd);
511 if (check_status(ret, op->status)) {
512 fprintf(stderr, "[%d] CLOSE \"%s\" failed\n", op->child->line, file);
518 static void smb_write(struct dbench_op *op)
527 offset = op->params[0];
528 length = op->params[1];
529 if (length > 65536) {
533 file = op->fname + 2;
535 hnd = lookup_path(file);
537 fprintf(stderr, "[%d] WRITE \"%s\" failed. This file is not open.\n", op->child->line, file);
543 smbc_lseek(hnd->fd, offset, SEEK_SET);
544 ret = smbc_write(hnd->fd, garbage, length);
546 if (check_status(ret==(int)length?0:-1, op->status)) {
547 fprintf(stderr, "[%d] WRITE \"%s\" failed\n", op->child->line, file);
551 op->child->bytes += length;
554 static void smb_read(struct dbench_op *op)
563 offset = op->params[0];
564 length = op->params[1];
565 if (length > 65536) {
569 file = op->fname + 2;
571 hnd = lookup_path(file);
573 fprintf(stderr, "[%d] READ \"%s\" failed. This file is not open.\n", op->child->line, file);
579 smbc_lseek(hnd->fd, offset, SEEK_SET);
580 ret = smbc_read(hnd->fd, garbage, length);
582 if (check_status(ret==(int)length?0:-1, op->status)) {
583 fprintf(stderr, "[%d] READ \"%s\" failed\n", op->child->line, file);
587 op->child->bytes += length;
591 static void smb_unlink(struct dbench_op *op)
597 path = op->fname + 2;
598 asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, path);
600 ret = smbc_unlink(str);
603 if (check_status(ret, op->status)) {
604 fprintf(stderr, "[%d] UNLINK \"%s\" failed\n", op->child->line, path);
610 static void recursive_delete_tree(struct dbench_op *op, const char *url)
613 struct smbc_dirent *dirent;
615 dir = smbc_opendir(url);
617 fprintf(stderr, "[%d] Deltree \"%s\" failed\n", op->child->line, url);
620 while((dirent = smbc_readdir(dir))) {
623 asprintf(&path, "%s/%s", url, dirent->name);
624 if (!strcmp(dirent->name, ".")) {
627 if (!strcmp(dirent->name, "..")) {
630 if (dirent->smbc_type == SMBC_DIR) {
631 recursive_delete_tree(op, path);
643 static void smb_readdir(struct dbench_op *op)
649 path = op->fname + 2;
650 asprintf(&url, "smb://%s/%s/%s", smb_server, smb_share, path);
652 dir = smbc_opendir(url);
655 fprintf(stderr, "[%d] READDIR \"%s\" failed\n", op->child->line, url);
661 static void smb_deltree(struct dbench_op *op)
666 path = op->fname + 2;
667 asprintf(&url, "smb://%s/%s/%s", smb_server, smb_share, path);
668 recursive_delete_tree(op, url);
672 static void smb_cleanup(struct child_struct *child)
674 struct smb_child *ctx = child->private;
676 struct dbench_op fake_op;
678 asprintf(&url, "smb://%s/%s", smb_server, smb_share);
679 recursive_delete_tree(&fake_op, url);
682 smbc_free_context(ctx->ctx, 1);
688 static struct backend_op ops[] = {
689 { "Deltree", smb_deltree },
690 { "CLOSE", smb_close },
691 { "MKDIR", smb_mkdir },
692 { "OPEN", smb_open },
693 { "READ", smb_read },
694 { "READDIR", smb_readdir },
695 { "RMDIR", smb_rmdir },
696 { "UNLINK", smb_unlink },
697 { "WRITE", smb_write },
701 struct nb_operations smb_ops = {
702 .backend_name = "smbbench",
705 .cleanup = smb_cleanup,