add a webpage for the smb backend
[tridge/dbench.git] / smb.c
1 /*
2    Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2009
3
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.
8
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.
13
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/>.
16 */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #undef _GNU_SOURCE
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <stdint.h>
25 #include <libsmbclient.h>
26 #include "dbench.h"
27
28 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
29
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;
35
36 typedef struct _data_t {
37         const char *dptr;
38         int dsize;
39 } data_t;
40
41 typedef struct _smb_handle_t {
42         int fd;
43 } smb_handle_t;
44
45 /* a tree to store all open handles, indexed by path */
46 typedef struct _tree_t {
47         data_t path;
48         smb_handle_t handle;
49         struct _tree_t *parent;
50         struct _tree_t *left;
51         struct _tree_t *right;
52 } tree_t;
53
54 static tree_t *open_files;
55
56
57 static tree_t *find_path(tree_t *tree, const char *path)
58 {
59         int i;
60
61         if (tree == NULL) {
62                 return NULL;
63         }
64
65         i = strcmp(path, tree->path.dptr);
66         if (i == 0) {
67                 return tree;
68         }
69         if (i < 0) {
70                 return find_path(tree->left, path);
71         }
72
73         return find_path(tree->right, path);
74 }
75
76 static smb_handle_t *lookup_path(const char *path)
77 {
78         tree_t *t;
79
80         t = find_path(open_files, path);
81         if (t == NULL) {
82                 return NULL;
83         }
84
85         return &t->handle;
86 }
87
88 static void free_node(tree_t *t)
89 {
90         free(discard_const(t->path.dptr));
91         free(t);
92 }
93
94 static void delete_path(const char *path)
95 {
96         tree_t *t;
97
98         t = find_path(open_files, path);
99         if (t == NULL) {
100                 return;
101         }
102
103         /* we have a left child */
104         if (t->left) {
105                 tree_t *tmp_tree;
106
107                 for(tmp_tree=t->left;tmp_tree->right;tmp_tree=tmp_tree->right)
108                         ;
109                 tmp_tree->right = t->right;
110                 if (t->right) {
111                         t->right->parent = tmp_tree;
112                 }
113
114                 if (t->parent == NULL) {
115                         open_files = tmp_tree;
116                         tmp_tree->parent = NULL;
117                         free_node(t);
118                         return;
119                 }
120
121                 if (t->parent->left == t) {
122                         t->parent->left = t->left;
123                         if (t->left) {
124                                 t->left->parent = t->parent;
125                         }
126                         free_node(t);
127                         return;
128                 }
129
130                 t->parent->right = t->left;
131                 if (t->left) {
132                         t->left->parent = t->parent;
133                 }
134                 free_node(t);
135                 return;
136         }
137
138         /* we only have a right child */
139         if (t->right) {
140                 tree_t *tmp_tree;
141
142                 for(tmp_tree=t->right;tmp_tree->left;tmp_tree=tmp_tree->left)
143                         ;
144                 tmp_tree->left = t->left;
145                 if (t->left) {
146                         t->left->parent = tmp_tree;
147                 }
148
149                 if (t->parent == NULL) {
150                         open_files = tmp_tree;
151                         tmp_tree->parent = NULL;
152                         free_node(t);
153                         return;
154                 }
155
156                 if (t->parent->left == t) {
157                         t->parent->left = t->right;
158                         if (t->right) {
159                                 t->right->parent = t->parent;
160                         }
161                         free_node(t);
162                         return;
163                 }
164
165                 t->parent->right = t->right;
166                 if (t->right) {
167                         t->right->parent = t->parent;
168                 }
169                 free_node(t);
170                 return;
171         }
172
173         /* we are a leaf node */
174         if (t->parent == NULL) {
175                 open_files = NULL;
176         } else {
177                 if (t->parent->left == t) {
178                         t->parent->left = NULL;
179                 } else {
180                         t->parent->right = NULL;
181                 }
182         }
183         free_node(t);
184         return;
185 }
186
187 static void insert_path(const char *path, smb_handle_t *hnd)
188 {
189         tree_t *tmp_t;
190         tree_t *t;
191         int i;
192
193         tmp_t = find_path(open_files, path);
194         if (tmp_t != NULL) {
195                 delete_path(path);
196         }
197
198         t = malloc(sizeof(tree_t));
199         if (t == NULL) {
200                 fprintf(stderr, "MALLOC failed to allocate tree_t in insert_fhandle\n");
201                 exit(10);
202         }
203
204         t->path.dptr = strdup(path);
205         if (t->path.dptr == NULL) {
206                 fprintf(stderr, "STRDUP failed to allocate key in insert_fhandle\n");
207                 exit(10);
208         }
209         t->path.dsize = strlen(path);
210
211         t->handle = *hnd;
212
213         t->left   = NULL;
214         t->right  = NULL;
215         t->parent = NULL;
216
217         if (open_files == NULL) {
218                 open_files = t;
219                 return;
220         }
221
222         tmp_t = open_files;
223 again:
224         i = strcmp(t->path.dptr, tmp_t->path.dptr);
225         if (i == 0) {
226                 tmp_t->handle = t->handle;
227                 free(discard_const(t->path.dptr));
228                 free(t);
229                 return;
230         }
231         if (i < 0) {
232                 if (tmp_t->left == NULL) {
233                         tmp_t->left = t;
234                         t->parent = tmp_t;
235                         return;
236                 }
237                 tmp_t = tmp_t->left;
238                 goto again;
239         }
240         if (tmp_t->right == NULL) {
241                 tmp_t->right = t;
242                 t->parent = tmp_t;
243                 return;
244         }
245         tmp_t = tmp_t->right;
246         goto again;
247 }
248
249
250
251
252
253 struct smb_child {
254         SMBCCTX *ctx;
255 };
256
257 static void failed(struct child_struct *child)
258 {
259         child->failed = 1;
260         fprintf(stderr, "ERROR: child %d failed at line %d\n", child->id, child->line);
261         exit(1);
262 }
263
264 static int check_status(int ret, const char *status)
265 {
266         if (!strcmp(status, "*")) {
267                 return 0;
268         }
269
270         if ((!strcmp(status, "SUCCESS")) && (ret == 0)) {
271                 return 0;
272         }
273
274         if ((!strcmp(status, "ERROR")) && (ret != 0)) {
275                 return 0;
276         }
277
278         return 1;
279 }
280
281
282 void smb_auth_fn(const char *server, const char *share, char *wrkgrp, int wrkgrplen, char *user, int userlen, char *passwd, int passwdlen)
283 {
284         (void)server;
285         (void)share;
286
287         if (smb_domain != NULL) {
288                 strncpy(wrkgrp, smb_domain, wrkgrplen - 1); wrkgrp[wrkgrplen - 1] = 0;
289         }
290         strncpy(user, smb_user, userlen - 1); user[userlen - 1] = 0;
291         strncpy(passwd, smb_password, passwdlen - 1); passwd[passwdlen - 1] = 0;
292 }
293
294 static int smb_init(void)
295 {
296         SMBCCTX *ctx;
297         char *tmp;
298         int ret;
299         char *str;
300
301         if (options.smb_share == NULL) {
302                 fprintf(stderr, "You must specify --smb-share=<share> with the \"smb\" backend.\n");
303                 return 1;
304         }
305         if (options.smb_share[0] != '/' || options.smb_share[1] != '/') {
306                 fprintf(stderr, "--smb-share Must be of the form //SERVER/SHARE[/PATH]\n");
307                 return 1;
308         }
309         smb_server = strdup(options.smb_share+2);
310         tmp = index(smb_server, '/');
311         if (tmp == NULL) {
312                 fprintf(stderr, "--smb-share Must be of the form //SERVER/SHARE[/PATH]\n");
313                 return 1;
314         }
315         *tmp = '\0';
316         smb_share = tmp+1;
317
318         if (options.smb_user == NULL) {
319                 fprintf(stderr, "You must specify --smb-user=[<domain>/]<user>%%<password> with the \"smb\" backend.\n");
320                 return 1;
321         }
322
323         smb_domain = strdup(options.smb_user);
324         tmp = index(smb_domain, '/');
325         if (tmp == NULL) {
326                 smb_user = smb_domain;
327                 smb_domain = NULL;
328         } else {
329                 smb_user = tmp+1;
330                 *tmp = '\0';
331         }
332         tmp = index(smb_user, '%');
333         if (tmp == NULL) {
334                 smb_password = NULL;
335         } else {
336                 smb_password = tmp+1;
337                 *tmp = '\0';
338         }
339
340         ctx = smbc_new_context();
341         if (ctx == NULL) {
342                 fprintf(stderr, "Could not allocate SMB Context\n");
343                 return 1;
344         }
345
346         smbc_setDebug(ctx, 0);
347         smbc_setFunctionAuthData(ctx, smb_auth_fn);
348
349         if (!smbc_init_context(ctx)) {
350                 smbc_free_context(ctx, 0);
351                 fprintf(stderr, "failed to initialize context\n");
352                 return 1;
353         }
354         smbc_set_context(ctx);
355
356         asprintf(&str, "smb://%s/%s", smb_server, smb_share);
357         ret = smbc_opendir(str);
358         free(str);
359
360         if (ret == -1) {
361                 fprintf(stderr, "Failed to access //%s/%s\n", smb_server, smb_share);
362                 return 1;
363         }
364
365         smbc_free_context(ctx, 1);
366         return 0;
367 }
368
369 static void smb_setup(struct child_struct *child)
370 {
371         struct smb_child *ctx;
372         char *str;
373         int ret;
374
375         ctx = malloc(sizeof(struct smb_child));
376         if (ctx == NULL) {
377                 fprintf(stderr, "Failed to malloc child ctx\n");
378                 exit(10);
379         }
380         child->private =ctx;
381
382         ctx->ctx = smbc_new_context();
383         if (ctx->ctx == NULL) {
384                 fprintf(stderr, "Could not allocate SMB Context\n");
385                 exit(10);
386         }
387
388         smbc_setDebug(ctx->ctx, 0);
389         smbc_setFunctionAuthData(ctx->ctx, smb_auth_fn);
390
391         if (!smbc_init_context(ctx->ctx)) {
392                 smbc_free_context(ctx->ctx, 0);
393                 fprintf(stderr, "failed to initialize context\n");
394                 exit(10);
395         }
396         smbc_setOptionUrlEncodeReaddirEntries(ctx->ctx, True);
397         smbc_set_context(ctx->ctx);
398
399
400         /* create clients and /clients/client? */
401         asprintf(&str, "smb://%s/%s/clients", smb_server, smb_share);
402         smbc_mkdir(str, 0777);
403         free(str);
404         asprintf(&str, "smb://%s/%s/clients/client%d", smb_server, smb_share, child->id);
405         smbc_mkdir(str, 0777);
406         free(str);
407
408
409         asprintf(&smb_share, "%s/clients/client%d", smb_share, child->id);
410 }
411
412 static void smb_mkdir(struct dbench_op *op)
413 {
414         char *str;
415         const char *dir;
416         int ret;
417
418         dir = op->fname + 2;
419
420         asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, dir);
421
422         ret = smbc_mkdir(str, 0777);
423         free(str);
424
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);
427                 failed(op->child);
428         }
429 }
430
431 static void smb_rmdir(struct dbench_op *op)
432 {
433         char *str;
434         const char *dir;
435         int ret;
436
437         dir = op->fname + 2;
438         asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, dir);
439         ret = smbc_rmdir(str);
440         free(str);
441
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);
444                 failed(op->child);
445         }
446 }
447
448 static void smb_open(struct dbench_op *op)
449 {
450         const char *file;
451         char *str;
452         int flags = 0;
453         smb_handle_t hnd;
454
455         if (op->params[0] & 0x01) {
456                 flags |= O_RDONLY;
457         }
458         if (op->params[0] & 0x02) {
459                 flags |= O_WRONLY;
460         }
461         if (op->params[0] & 0x04) {
462                 flags |= O_RDWR;
463         }
464         if (op->params[0] & 0x08) {
465                 flags |= O_CREAT;
466         }
467         if (op->params[0] & 0x10) {
468                 flags |= O_EXCL;
469         }
470         if (op->params[0] & 0x20) {
471                 flags |= O_TRUNC;
472         }
473         if (op->params[0] & 0x40) {
474                 flags |= O_APPEND;
475         }
476
477         file = op->fname + 2;
478         asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, file);
479
480         hnd.fd = smbc_open(str, flags, 0777);
481         free(str);
482
483         if (check_status(hnd.fd<0?-1:0, op->status)) {
484                 fprintf(stderr, "[%d] OPEN \"%s\" failed\n", op->child->line, file);
485                 failed(op->child);
486                 return;
487         }
488
489         insert_path(file, &hnd);
490
491 }
492
493 static void smb_close(struct dbench_op *op)
494 {
495         smb_handle_t *hnd;
496         const char *file;
497         int ret;
498
499         file = op->fname + 2;
500
501         hnd = lookup_path(file);
502         if (hnd == NULL) {
503                 fprintf(stderr, "[%d] CLOSE \"%s\" failed. This file is not open.\n", op->child->line, file);
504                 failed(op->child);
505                 return;
506         }
507
508         ret = smbc_close(hnd->fd);
509         delete_path(file);
510
511         if (check_status(ret, op->status)) {
512                 fprintf(stderr, "[%d] CLOSE \"%s\" failed\n", op->child->line, file);
513                 failed(op->child);
514                 return;
515         }
516 }
517
518 static void smb_write(struct dbench_op *op)
519 {
520         smb_handle_t *hnd;
521         const char *file;
522         int ret;
523         size_t length;
524         off_t offset;
525         char garbage[65536];
526
527         offset = op->params[0];
528         length = op->params[1];
529         if (length > 65536) {
530                 length = 65536;
531         }
532
533         file = op->fname + 2;
534
535         hnd = lookup_path(file);
536         if (hnd == NULL) {
537                 fprintf(stderr, "[%d] WRITE \"%s\" failed. This file is not open.\n", op->child->line, file);
538                 failed(op->child);
539                 return;
540         }
541
542
543         smbc_lseek(hnd->fd, offset, SEEK_SET);
544         ret = smbc_write(hnd->fd, garbage, length);
545
546         if (check_status(ret==(int)length?0:-1, op->status)) {
547                 fprintf(stderr, "[%d] WRITE \"%s\" failed\n", op->child->line, file);
548                 failed(op->child);
549                 return;
550         }
551         op->child->bytes += length;
552 }
553
554 static void smb_read(struct dbench_op *op)
555 {
556         smb_handle_t *hnd;
557         const char *file;
558         int ret;
559         size_t length;
560         off_t offset;
561         char garbage[65536];
562
563         offset = op->params[0];
564         length = op->params[1];
565         if (length > 65536) {
566                 length = 65536;
567         }
568
569         file = op->fname + 2;
570
571         hnd = lookup_path(file);
572         if (hnd == NULL) {
573                 fprintf(stderr, "[%d] READ \"%s\" failed. This file is not open.\n", op->child->line, file);
574                 failed(op->child);
575                 return;
576         }
577
578
579         smbc_lseek(hnd->fd, offset, SEEK_SET);
580         ret = smbc_read(hnd->fd, garbage, length);
581
582         if (check_status(ret==(int)length?0:-1, op->status)) {
583                 fprintf(stderr, "[%d] READ \"%s\" failed\n", op->child->line, file);
584                 failed(op->child);
585                 return;
586         }
587         op->child->bytes += length;
588 }
589
590
591 static void smb_unlink(struct dbench_op *op)
592 {
593         const char *path;
594         char *str;
595         int ret;
596
597         path = op->fname + 2;
598         asprintf(&str, "smb://%s/%s/%s", smb_server, smb_share, path);
599
600         ret = smbc_unlink(str);
601         free(str);
602
603         if (check_status(ret, op->status)) {
604                 fprintf(stderr, "[%d] UNLINK \"%s\" failed\n", op->child->line, path);
605                 failed(op->child);
606                 return;
607         }
608 }
609
610 static void recursive_delete_tree(struct dbench_op *op, const char *url)
611 {
612         int dir;
613         struct smbc_dirent *dirent;
614
615         dir = smbc_opendir(url);
616         if (dir < 0) {
617                 fprintf(stderr, "[%d] Deltree \"%s\" failed\n", op->child->line, url);
618                 failed(op->child);
619         }
620         while((dirent = smbc_readdir(dir))) {
621                 char *path;
622
623                 asprintf(&path, "%s/%s", url, dirent->name);
624                 if (!strcmp(dirent->name, ".")) {
625                         continue;
626                 }
627                 if (!strcmp(dirent->name, "..")) {
628                         continue;
629                 }
630                 if (dirent->smbc_type == SMBC_DIR) {
631                         recursive_delete_tree(op, path);
632                         smbc_rmdir(path);
633                 } else {
634                         smbc_unlink(path);
635                 }
636                 free(path);
637         }
638         smbc_closedir(dir);
639
640         return;
641 }
642
643 static void smb_readdir(struct dbench_op *op)
644 {
645         const char *path;
646         char *url;
647         int dir;
648
649         path = op->fname + 2;
650         asprintf(&url, "smb://%s/%s/%s", smb_server, smb_share, path);
651
652         dir = smbc_opendir(url);
653         free(url);
654         if (dir < 0) {
655                 fprintf(stderr, "[%d] READDIR \"%s\" failed\n", op->child->line, url);
656                 failed(op->child);
657         }
658         smbc_closedir(dir);
659 }
660
661 static void smb_deltree(struct dbench_op *op)
662 {
663         const char *path;
664         char *url;
665
666         path = op->fname + 2;
667         asprintf(&url, "smb://%s/%s/%s", smb_server, smb_share, path);
668         recursive_delete_tree(op, url);
669         free(url);
670 }
671
672 static void smb_cleanup(struct child_struct *child)
673 {
674         struct smb_child *ctx = child->private;
675         char *url;
676         struct dbench_op fake_op;
677
678         asprintf(&url, "smb://%s/%s", smb_server, smb_share);
679         recursive_delete_tree(&fake_op, url);
680         free(url);
681
682         smbc_free_context(ctx->ctx, 1);
683         free(ctx);
684 }
685
686
687
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 },
698         { NULL, NULL}
699 };
700
701 struct nb_operations smb_ops = {
702         .backend_name = "smbbench",
703         .init         = smb_init,
704         .setup        = smb_setup,
705         .cleanup      = smb_cleanup,
706         .ops          = ops
707 };