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