converted all backends to new common format
[tridge/dbench.git] / fileio.c
1 /* 
2    dbench version 4
3
4    Copyright (C) 1999-2007 by Andrew Tridgell <tridge@samba.org>
5    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "dbench.h"
22
23 #define MAX_FILES 200
24
25 struct ftable {
26         char *name;
27         int fd;
28         int handle;
29 };
30
31 static int find_handle(struct child_struct *child, int handle)
32 {
33         struct ftable *ftable = child->private;
34         int i;
35         for (i=0;i<MAX_FILES;i++) {
36                 if (ftable[i].handle == handle) return i;
37         }
38         printf("(%d) ERROR: handle %d was not found\n", 
39                child->line, handle);
40         exit(1);
41 }
42
43
44 /* Find the directory holding a file, and flush it to disk.  We do
45    this in -S mode after a directory-modifying mode, to simulate the
46    way knfsd tries to flush directories.  MKDIR and similar operations
47    are meant to be synchronous on NFSv2. */
48 static void sync_parent(struct child_struct *child, const char *fname)
49 {
50         char *copy_name;
51         int dir_fd;
52         char *slash;
53
54         if (strchr(fname, '/')) {
55                 copy_name = strdup(fname);
56                 slash = strrchr(copy_name, '/');
57                 *slash = '\0';
58         } else {
59                 copy_name = strdup(".");
60         } 
61         
62         dir_fd = open(copy_name, O_RDONLY);
63         if (dir_fd == -1) {
64                 printf("[%d] open directory \"%s\" for sync failed: %s\n",
65                        child->line, copy_name, strerror(errno));
66         } else {
67 #if defined(HAVE_FDATASYNC)
68                 if (fdatasync(dir_fd) == -1) {
69 #else
70                 if (fsync(dir_fd) == -1) {
71 #endif
72                         printf("[%d] datasync directory \"%s\" failed: %s\n",
73                                child->line, copy_name,
74                                strerror(errno));
75                 }
76                 if (close(dir_fd) == -1) {
77                         printf("[%d] close directory failed: %s\n",
78                                child->line, strerror(errno));
79                 }
80         }
81         free(copy_name);
82 }
83
84 static void xattr_fd_read_hook(struct child_struct *child, int fd)
85 {
86 #if HAVE_EA_SUPPORT
87         char buf[44];
88         if (options.ea_enable) {
89                 memset(buf, 0, sizeof(buf));
90                 sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
91         }
92 #else
93         (void)fd;
94 #endif
95         (void)child;
96 }
97
98 static void xattr_fname_read_hook(struct child_struct *child, const char *fname)
99 {
100 #if HAVE_EA_SUPPORT
101         if (options.ea_enable) {
102                 char buf[44];
103                 sys_getxattr(fname, "user.DosAttrib", buf, sizeof(buf));
104         }
105 #else
106         (void)fname;
107 #endif
108         (void)child;
109 }
110
111 static void xattr_fd_write_hook(struct child_struct *child, int fd)
112 {
113 #if HAVE_EA_SUPPORT
114         if (options.ea_enable) {
115                 struct timeval tv;
116                 char buf[44];
117                 sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
118                 memset(buf, 0, sizeof(buf));
119                 /* give some probability of sharing */
120                 if (random() % 10 < 2) {
121                         *(time_t *)buf = time(NULL);
122                 } else {
123                         gettimeofday(&tv, NULL);
124                         memcpy(buf, &tv, sizeof(tv));
125                 }
126                 if (sys_fsetxattr(fd, "user.DosAttrib", buf, sizeof(buf), 0) != 0) {
127                         printf("[%d] fsetxattr failed - %s\n", 
128                                child->line, strerror(errno));
129                         exit(1);
130                 }
131         }
132 #else
133         (void)fd;
134 #endif
135 }
136
137 static int expected_status(const char *status)
138 {
139         if (strcmp(status, "NT_STATUS_OK") == 0) {
140                 return 0;
141         }
142         if (strncmp(status, "0x", 2) == 0 &&
143             strtoul(status, NULL, 16) == 0) {
144                 return 0;
145         }
146         return -1;
147 }
148
149 /*
150   simulate pvfs_resolve_name()
151 */
152 static void resolve_name(struct child_struct *child, const char *name)
153 {
154         struct stat st;
155         char *dname, *fname;
156         DIR *dir;
157         char *p;
158         struct dirent *d;
159
160         if (name == NULL) return;
161
162         if (stat(name, &st) == 0) {
163                 xattr_fname_read_hook(child, name);
164                 return;
165         }
166
167         if (options.no_resolve) {
168                 return;
169         }
170
171         dname = strdup(name);
172         p = strrchr(dname, '/');
173         if (!p) return;
174         *p = 0;
175         fname = p+1;
176
177         dir = opendir(dname);
178         if (!dir) {
179                 free(dname);
180                 return;
181         }
182         while ((d = readdir(dir))) {
183                 if (strcasecmp(fname, d->d_name) == 0) break;
184         }
185         closedir(dir);
186         free(dname);
187 }
188
189 static void failed(struct child_struct *child)
190 {
191         child->failed = 1;
192         printf("ERROR: child %d failed at line %d\n", child->id, child->line);
193         exit(1);
194 }
195
196 static void fio_setup(struct child_struct *child)
197 {
198         struct ftable *ftable;
199         ftable = calloc(MAX_FILES, sizeof(struct ftable));
200         child->private = ftable;
201         child->rate.last_time = timeval_current();
202         child->rate.last_bytes = 0;
203 }
204
205 static void fio_unlink(struct dbench_op *op)
206 {
207         resolve_name(op->child, op->fname);
208
209         if (unlink(op->fname) != expected_status(op->status)) {
210                 printf("[%d] unlink %s failed (%s) - expected %s\n", 
211                        op->child->line, op->fname, strerror(errno), op->status);
212                 failed(op->child);
213         }
214         if (options.sync_dirs) sync_parent(op->child, op->fname);
215 }
216
217 static void fio_mkdir(struct dbench_op *op)
218 {
219         struct stat st;
220         resolve_name(op->child, op->fname);
221         if (options.stat_check && stat(op->fname, &st) == 0) {
222                 return;
223         }
224         mkdir(op->fname, 0777);
225 }
226
227 static void fio_rmdir(struct dbench_op *op)
228 {
229         struct stat st;
230         resolve_name(op->child, op->fname);
231
232         if (options.stat_check && 
233             (stat(op->fname, &st) != 0 || !S_ISDIR(st.st_mode))) {
234                 return;
235         }
236
237         if (rmdir(op->fname) != expected_status(op->status)) {
238                 printf("[%d] rmdir %s failed (%s) - expected %s\n", 
239                        op->child->line, op->fname, strerror(errno), op->status);
240                 failed(op->child);
241         }
242         if (options.sync_dirs) sync_parent(op->child, op->fname);
243 }
244
245 static void fio_createx(struct dbench_op *op)
246 {
247         uint32_t create_options = op->params[0];
248         uint32_t create_disposition = op->params[1];
249         int fnum = op->params[2];
250         int fd, i;
251         int flags = O_RDWR;
252         struct stat st;
253         struct ftable *ftable = (struct ftable *)op->child->private;
254
255         resolve_name(op->child, op->fname);
256
257         if (options.sync_open) flags |= O_SYNC;
258
259         if (create_disposition == FILE_CREATE) {
260                 if (options.stat_check && stat(op->fname, &st) == 0) {
261                         create_disposition = FILE_OPEN;
262                 } else {
263                         flags |= O_CREAT;
264                 }
265         }
266
267         if (create_disposition == FILE_OVERWRITE ||
268             create_disposition == FILE_OVERWRITE_IF) {
269                 flags |= O_CREAT | O_TRUNC;
270         }
271
272         if (create_options & FILE_DIRECTORY_FILE) {
273                 /* not strictly correct, but close enough */
274                 if (!options.stat_check || stat(op->fname, &st) == -1) {
275                         mkdir(op->fname, 0700);
276                 }
277         }
278
279         if (create_options & FILE_DIRECTORY_FILE) flags = O_RDONLY|O_DIRECTORY;
280
281         fd = open(op->fname, flags, 0600);
282         if (fd == -1 && errno == EISDIR) {
283                 flags = O_RDONLY|O_DIRECTORY;
284                 fd = open(op->fname, flags, 0600);
285         }
286         if (fd == -1) {
287                 if (expected_status(op->status) == 0) {
288                         printf("[%d] open %s failed for handle %d (%s)\n", 
289                                op->child->line, op->fname, fnum, strerror(errno));
290                 }
291                 return;
292         }
293         if (expected_status(op->status) != 0) {
294                 printf("[%d] open %s succeeded for handle %d\n", 
295                        op->child->line, op->fname, fnum);
296                 close(fd);
297                 return;
298         }
299         
300         for (i=0;i<MAX_FILES;i++) {
301                 if (ftable[i].handle == 0) break;
302         }
303         if (i == MAX_FILES) {
304                 printf("file table full for %s\n", op->fname);
305                 exit(1);
306         }
307         ftable[i].name = strdup(op->fname);
308         ftable[i].handle = fnum;
309         ftable[i].fd = fd;
310
311         fstat(fd, &st);
312
313         if (!S_ISDIR(st.st_mode)) {
314                 xattr_fd_write_hook(op->child, fd);
315         }
316 }
317
318 static void fio_writex(struct dbench_op *op)
319 {
320         int handle = op->params[0];
321         int offset = op->params[1];
322         int size = op->params[2];
323         int ret_size = op->params[3];
324         int i = find_handle(op->child, handle);
325         void *buf;
326         struct stat st;
327         struct ftable *ftable = (struct ftable *)op->child->private;
328         ssize_t ret;
329
330         if (options.fake_io) {
331                 op->child->bytes += ret_size;
332                 op->child->bytes_since_fsync += ret_size;
333                 return;
334         }
335
336         buf = calloc(size, 1);
337
338         if (options.one_byte_write_fix &&
339             size == 1 && fstat(ftable[i].fd, &st) == 0) {
340                 if (st.st_size > offset) {
341                         unsigned char c;
342                         pread(ftable[i].fd, &c, 1, offset);
343                         if (c == ((unsigned char *)buf)[0]) {
344                                 free(buf);
345                                 op->child->bytes += size;
346                                 return;
347                         }
348                 } else if (((unsigned char *)buf)[0] == 0) {
349                         ftruncate(ftable[i].fd, offset+1);
350                         free(buf);
351                         op->child->bytes += size;
352                         return;
353                 } 
354         }
355
356         ret = pwrite(ftable[i].fd, buf, size, offset);
357         if (ret == -1) {
358                 printf("[%d] write failed on handle %d (%s)\n", 
359                        op->child->line, handle, strerror(errno));
360                 exit(1);
361         }
362         if (ret != ret_size) {
363                 printf("[%d] wrote %d bytes, expected to write %d bytes on handle %d\n", 
364                        op->child->line, (int)ret, (int)ret_size, handle);
365                 exit(1);
366         }
367
368         if (options.do_fsync) fsync(ftable[i].fd);
369
370         free(buf);
371
372         op->child->bytes += size;
373         op->child->bytes_since_fsync += size;
374 }
375
376 static void fio_readx(struct dbench_op *op)
377 {
378         int handle = op->params[0];
379         int offset = op->params[1];
380         int size = op->params[2];
381         int ret_size = op->params[3];
382         int i = find_handle(op->child, handle);
383         void *buf;
384         struct ftable *ftable = (struct ftable *)op->child->private;
385
386         if (options.fake_io) {
387                 op->child->bytes += ret_size;
388                 return;
389         }
390
391         buf = malloc(size);
392
393         if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
394                 printf("[%d] read failed on handle %d (%s)\n", 
395                        op->child->line, handle, strerror(errno));
396         }
397
398         free(buf);
399
400         op->child->bytes += size;
401 }
402
403 static void fio_close(struct dbench_op *op)
404 {
405         int handle = op->params[0];
406         struct ftable *ftable = (struct ftable *)op->child->private;
407         int i = find_handle(op->child, handle);
408         close(ftable[i].fd);
409         ftable[i].handle = 0;
410         if (ftable[i].name) free(ftable[i].name);
411         ftable[i].name = NULL;
412 }
413
414 static void fio_rename(struct dbench_op *op)
415 {
416         const char *old = op->fname;
417         const char *new = op->fname2;
418
419         resolve_name(op->child, old);
420         resolve_name(op->child, new);
421
422         if (options.stat_check) {
423                 struct stat st;
424                 if (stat(old, &st) != 0 && expected_status(op->status) == 0) {
425                         printf("[%d] rename %s %s failed - file doesn't exist\n",
426                                op->child->line, old, new);
427                         failed(op->child);
428                         return;
429                 }
430         }
431
432         if (rename(old, new) != expected_status(op->status)) {
433                 printf("[%d] rename %s %s failed (%s) - expected %s\n", 
434                        op->child->line, old, new, strerror(errno), op->status);
435                 failed(op->child);
436         }
437         if (options.sync_dirs) sync_parent(op->child, new);
438 }
439
440 static void fio_flush(struct dbench_op *op)
441 {
442         int handle = op->params[0];
443         struct ftable *ftable = (struct ftable *)op->child->private;
444         int i = find_handle(op->child, handle);
445         fsync(ftable[i].fd);
446 }
447
448 static void fio_qpathinfo(struct dbench_op *op)
449 {
450         resolve_name(op->child, op->fname);
451 }
452
453 static void fio_qfileinfo(struct dbench_op *op)
454 {
455         int handle = op->params[0];
456         int level = op->params[1];
457         struct ftable *ftable = (struct ftable *)op->child->private;
458         struct stat st;
459         int i = find_handle(op->child, handle);
460         (void)op->child;
461         (void)level;
462         fstat(ftable[i].fd, &st);
463         xattr_fd_read_hook(op->child, ftable[i].fd);
464 }
465
466 static void fio_qfsinfo(struct dbench_op *op)
467 {
468         int level = op->params[0];
469         struct statvfs st;
470
471         (void)level;
472
473         statvfs(op->child->directory, &st);
474 }
475
476 static void fio_findfirst(struct dbench_op *op)
477 {
478         int level = op->params[0];
479         int maxcnt = op->params[1];
480         int count = op->params[2];
481         DIR *dir;
482         struct dirent *d;
483         char *p;
484
485         (void)op->child;
486         (void)level;
487         (void)count;
488
489         resolve_name(op->child, op->fname);
490
491         if (strpbrk(op->fname, "<>*?\"") == NULL) {
492                 return;
493         }
494
495         p = strrchr(op->fname, '/');
496         if (!p) return;
497         *p = 0;
498         dir = opendir(op->fname);
499         if (!dir) return;
500         while (maxcnt && (d = readdir(dir))) maxcnt--;
501         closedir(dir);
502 }
503
504 static void fio_deltree(struct dbench_op *op)
505 {
506         DIR *d;
507         struct dirent *de;
508         
509         d = opendir(op->fname);
510         if (d == NULL) return;
511
512         for (de=readdir(d);de;de=readdir(d)) {
513                 struct stat st;
514                 char *fname = NULL;
515                 if (strcmp(de->d_name, ".") == 0 ||
516                     strcmp(de->d_name, "..") == 0) {
517                         continue;
518                 }
519                 asprintf(&fname, "%s/%s", op->fname, de->d_name);
520                 if (fname == NULL) {
521                         printf("Out of memory\n");
522                         exit(1);
523                 }
524                 if (stat(fname, &st) != 0) {
525                         continue;
526                 }
527                 if (S_ISDIR(st.st_mode)) {
528                         struct dbench_op op2 = *op;
529                         op2.fname = fname;
530                         fio_deltree(&op2);
531                 } else {
532                         if (unlink(fname) != 0) {
533                                 printf("[%d] unlink '%s' failed - %s\n",
534                                        op->child->line, fname, strerror(errno));
535                         }
536                 }
537                 free(fname);
538         }
539         closedir(d);
540 }
541
542 static void fio_cleanup(struct child_struct *child)
543 {
544         char *dname;
545         struct dbench_op op;
546
547         ZERO_STRUCT(op);
548
549         asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
550         op.child = child;
551         op.fname = dname;
552         fio_deltree(&op);
553         free(dname);
554
555         asprintf(&dname, "%s%s", child->directory, "/clients");
556         rmdir(dname);
557         free(dname);
558 }
559
560
561 static void fio_sfileinfo(struct dbench_op *op)
562 {
563         int handle = op->params[0];
564         int level = op->params[1];
565         struct ftable *ftable = (struct ftable *)op->child->private;
566         int i = find_handle(op->child, handle);
567         struct utimbuf tm;
568         struct stat st;
569         (void)op->child;
570         (void)handle;
571         (void)level;
572         xattr_fd_read_hook(op->child, ftable[i].fd);
573
574         fstat(ftable[i].fd, &st);
575
576         tm.actime = st.st_atime - 10;
577         tm.modtime = st.st_mtime - 12;
578
579         utime(ftable[i].name, &tm);
580
581         if (!S_ISDIR(st.st_mode)) {
582                 xattr_fd_write_hook(op->child, ftable[i].fd);
583         }
584 }
585
586 static void fio_lockx(struct dbench_op *op)
587 {
588         int handle = op->params[0];
589         uint32_t offset = op->params[1];
590         int size = op->params[2];
591         struct ftable *ftable = (struct ftable *)op->child->private;
592         int i = find_handle(op->child, handle);
593         struct flock lock;
594
595         (void)op->child;
596
597         lock.l_type = F_WRLCK;
598         lock.l_whence = SEEK_SET;
599         lock.l_start = offset;
600         lock.l_len = size;
601         lock.l_pid = 0;
602
603         fcntl(ftable[i].fd, F_SETLKW, &lock);
604 }
605
606 static void fio_unlockx(struct dbench_op *op)
607 {
608         int handle = op->params[0];
609         uint32_t offset = op->params[1];
610         int size = op->params[2];
611         struct ftable *ftable = (struct ftable *)op->child->private;
612         int i = find_handle(op->child, handle);
613         struct flock lock;
614
615         lock.l_type = F_UNLCK;
616         lock.l_whence = SEEK_SET;
617         lock.l_start = offset;
618         lock.l_len = size;
619         lock.l_pid = 0;
620
621         fcntl(ftable[i].fd, F_SETLKW, &lock);
622 }
623
624 static struct backend_op ops[] = {
625         { "Deltree", fio_deltree },
626         { "Flush", fio_flush },
627         { "Close", fio_close },
628         { "LockX", fio_lockx },
629         { "Rmdir", fio_rmdir },
630         { "Mkdir", fio_mkdir },
631         { "Rename", fio_rename },
632         { "ReadX", fio_readx },
633         { "WriteX", fio_writex },
634         { "Unlink", fio_unlink },
635         { "UnlockX", fio_unlockx },
636         { "FIND_FIRST", fio_findfirst },
637         { "SET_FILE_INFORMATION", fio_sfileinfo },
638         { "QUERY_FILE_INFORMATION", fio_qfileinfo },
639         { "QUERY_PATH_INFORMATION", fio_qpathinfo },
640         { "QUERY_FS_INFORMATION", fio_qfsinfo },
641         { "NTCreateX", fio_createx },
642         { NULL, NULL}
643 };
644
645 struct nb_operations nb_ops = {
646         .backend_name = "dbench",
647         .setup          = fio_setup,
648         .cleanup        = fio_cleanup,
649         .ops          = ops
650 };