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