02b734f19f09311fc8b8e8432d5a308e7a7545ac
[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 child_struct *child, const char *fname, int attr, const char *status)
206 {
207         (void)attr;
208
209         resolve_name(child, fname);
210
211         if (unlink(fname) != expected_status(status)) {
212                 printf("[%d] unlink %s failed (%s) - expected %s\n", 
213                        child->line, fname, strerror(errno), status);
214                 failed(child);
215         }
216         if (options.sync_dirs) sync_parent(child, fname);
217 }
218
219 static void fio_mkdir(struct child_struct *child, const char *dname, const char *status)
220 {
221         struct stat st;
222         (void)child;
223         (void)status;
224         resolve_name(child, dname);
225         if (options.stat_check && stat(dname, &st) == 0) {
226                 return;
227         }
228         mkdir(dname, 0777);
229 }
230
231 static void fio_rmdir(struct child_struct *child, const char *fname, const char *status)
232 {
233         struct stat st;
234         resolve_name(child, fname);
235
236         if (options.stat_check && 
237             (stat(fname, &st) != 0 || !S_ISDIR(st.st_mode))) {
238                 return;
239         }
240
241         if (rmdir(fname) != expected_status(status)) {
242                 printf("[%d] rmdir %s failed (%s) - expected %s\n", 
243                        child->line, fname, strerror(errno), status);
244                 failed(child);
245         }
246         if (options.sync_dirs) sync_parent(child, fname);
247 }
248
249 static void fio_createx(struct child_struct *child, const char *fname, 
250                 uint32_t create_options, uint32_t create_disposition, int fnum,
251                 const char *status)
252 {
253         int fd, i;
254         int flags = O_RDWR;
255         struct stat st;
256         struct ftable *ftable = (struct ftable *)child->private;
257
258         resolve_name(child, fname);
259
260         if (options.sync_open) flags |= O_SYNC;
261
262         if (create_disposition == FILE_CREATE) {
263                 if (options.stat_check && stat(fname, &st) == 0) {
264                         create_disposition = FILE_OPEN;
265                 } else {
266                         flags |= O_CREAT;
267                 }
268         }
269
270         if (create_disposition == FILE_OVERWRITE ||
271             create_disposition == FILE_OVERWRITE_IF) {
272                 flags |= O_CREAT | O_TRUNC;
273         }
274
275         if (create_options & FILE_DIRECTORY_FILE) {
276                 /* not strictly correct, but close enough */
277                 if (!options.stat_check || stat(fname, &st) == -1) {
278                         mkdir(fname, 0700);
279                 }
280         }
281
282         if (create_options & FILE_DIRECTORY_FILE) flags = O_RDONLY|O_DIRECTORY;
283
284         fd = open(fname, flags, 0600);
285         if (fd == -1 && errno == EISDIR) {
286                 flags = O_RDONLY|O_DIRECTORY;
287                 fd = open(fname, flags, 0600);
288         }
289         if (fd == -1) {
290                 if (expected_status(status) == 0) {
291                         printf("[%d] open %s failed for handle %d (%s)\n", 
292                                child->line, fname, fnum, strerror(errno));
293                 }
294                 return;
295         }
296         if (expected_status(status) != 0) {
297                 printf("[%d] open %s succeeded for handle %d\n", 
298                        child->line, fname, fnum);
299                 close(fd);
300                 return;
301         }
302         
303         for (i=0;i<MAX_FILES;i++) {
304                 if (ftable[i].handle == 0) break;
305         }
306         if (i == MAX_FILES) {
307                 printf("file table full for %s\n", fname);
308                 exit(1);
309         }
310         ftable[i].name = strdup(fname);
311         ftable[i].handle = fnum;
312         ftable[i].fd = fd;
313
314         fstat(fd, &st);
315
316         if (!S_ISDIR(st.st_mode)) {
317                 xattr_fd_write_hook(child, fd);
318         }
319 }
320
321 static void fio_writex(struct child_struct *child, int handle, int offset, 
322                int size, int ret_size, const char *status)
323 {
324         int i = find_handle(child, handle);
325         void *buf;
326         struct stat st;
327         struct ftable *ftable = (struct ftable *)child->private;
328         ssize_t ret;
329
330         if (options.fake_io) {
331                 child->bytes += ret_size;
332                 child->bytes_since_fsync += ret_size;
333                 return;
334         }
335
336         (void)status;
337
338         buf = calloc(size, 1);
339
340         if (options.one_byte_write_fix &&
341             size == 1 && fstat(ftable[i].fd, &st) == 0) {
342                 if (st.st_size > offset) {
343                         unsigned char c;
344                         pread(ftable[i].fd, &c, 1, offset);
345                         if (c == ((unsigned char *)buf)[0]) {
346                                 free(buf);
347                                 child->bytes += size;
348                                 return;
349                         }
350                 } else if (((unsigned char *)buf)[0] == 0) {
351                         ftruncate(ftable[i].fd, offset+1);
352                         free(buf);
353                         child->bytes += size;
354                         return;
355                 } 
356         }
357
358         ret = pwrite(ftable[i].fd, buf, size, offset);
359         if (ret == -1) {
360                 printf("[%d] write failed on handle %d (%s)\n", 
361                        child->line, handle, strerror(errno));
362                 exit(1);
363         }
364         if (ret != ret_size) {
365                 printf("[%d] wrote %d bytes, expected to write %d bytes on handle %d\n", 
366                        child->line, (int)ret, (int)ret_size, handle);
367                 exit(1);
368         }
369
370         if (options.do_fsync) fsync(ftable[i].fd);
371
372         free(buf);
373
374         child->bytes += size;
375         child->bytes_since_fsync += size;
376 }
377
378 static void fio_readx(struct child_struct *child, int handle, int offset, 
379               int size, int ret_size, const char *status)
380 {
381         int i = find_handle(child, handle);
382         void *buf;
383         struct ftable *ftable = (struct ftable *)child->private;
384
385         if (options.fake_io) {
386                 child->bytes += ret_size;
387                 return;
388         }
389
390         (void)status;
391
392         buf = malloc(size);
393
394         if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
395                 printf("[%d] read failed on handle %d (%s)\n", 
396                        child->line, handle, strerror(errno));
397         }
398
399         free(buf);
400
401         child->bytes += size;
402 }
403
404 static void fio_close(struct child_struct *child, int handle, const char *status)
405 {
406         struct ftable *ftable = (struct ftable *)child->private;
407         int i = find_handle(child, handle);
408         (void)status;
409         close(ftable[i].fd);
410         ftable[i].handle = 0;
411         if (ftable[i].name) free(ftable[i].name);
412         ftable[i].name = NULL;
413 }
414
415 static void fio_rename(struct child_struct *child, const char *old, const char *new, const char *status)
416 {
417         resolve_name(child, old);
418         resolve_name(child, new);
419
420         if (options.stat_check) {
421                 struct stat st;
422                 if (stat(old, &st) != 0 && expected_status(status) == 0) {
423                         printf("[%d] rename %s %s failed - file doesn't exist\n",
424                                child->line, old, new);
425                         failed(child);
426                         return;
427                 }
428         }
429
430         if (rename(old, new) != expected_status(status)) {
431                 printf("[%d] rename %s %s failed (%s) - expected %s\n", 
432                        child->line, old, new, strerror(errno), status);
433                 failed(child);
434         }
435         if (options.sync_dirs) sync_parent(child, new);
436 }
437
438 static void fio_flush(struct child_struct *child, int handle, const char *status)
439 {
440         struct ftable *ftable = (struct ftable *)child->private;
441         int i = find_handle(child, handle);
442         (void)status;
443         fsync(ftable[i].fd);
444 }
445
446 static void fio_qpathinfo(struct child_struct *child, const char *fname, int level, 
447                   const char *status)
448 {
449         (void)child;
450         (void)level;
451         (void)status;
452         resolve_name(child, fname);
453 }
454
455 static void fio_qfileinfo(struct child_struct *child, int handle, int level, const char *status)
456 {
457         struct ftable *ftable = (struct ftable *)child->private;
458         struct stat st;
459         int i = find_handle(child, handle);
460         (void)child;
461         (void)level;
462         (void)status;
463         fstat(ftable[i].fd, &st);
464         xattr_fd_read_hook(child, ftable[i].fd);
465 }
466
467 static void fio_qfsinfo(struct child_struct *child, int level, const char *status)
468 {
469         struct statvfs st;
470
471         (void)level;
472         (void)status;
473
474         statvfs(child->directory, &st);
475 }
476
477 static void fio_findfirst(struct child_struct *child, const char *fname, int level, int maxcnt, 
478                   int count, const char *status)
479 {
480         DIR *dir;
481         struct dirent *d;
482         char *p;
483
484         (void)child;
485         (void)level;
486         (void)count;
487         (void)status;
488
489         resolve_name(child, fname);
490
491         if (strpbrk(fname, "<>*?\"") == NULL) {
492                 return;
493         }
494
495         p = strrchr(fname, '/');
496         if (!p) return;
497         *p = 0;
498         dir = opendir(fname);
499         if (!dir) return;
500         while (maxcnt && (d = readdir(dir))) maxcnt--;
501         closedir(dir);
502 }
503
504 void fio_deltree(struct child_struct *child, const char *dname)
505 {
506         DIR *d;
507         struct dirent *de;
508         (void)child;
509         
510         d = opendir(dname);
511         if (d == NULL) return;
512
513         for (de=readdir(d);de;de=readdir(d)) {
514                 struct stat st;
515                 char *fname = NULL;
516                 if (strcmp(de->d_name, ".") == 0 ||
517                     strcmp(de->d_name, "..") == 0) {
518                         continue;
519                 }
520                 asprintf(&fname, "%s/%s", dname, de->d_name);
521                 if (fname == NULL) {
522                         printf("Out of memory\n");
523                         exit(1);
524                 }
525                 if (stat(fname, &st) != 0) {
526                         continue;
527                 }
528                 if (S_ISDIR(st.st_mode)) {
529                         fio_deltree(child, fname);
530                 } else {
531                         if (unlink(fname) != 0) {
532                                 printf("[%d] unlink '%s' failed - %s\n",
533                                        child->line, fname, strerror(errno));
534                         }
535                 }
536                 free(fname);
537         }
538         closedir(d);
539 }
540
541 static void fio_cleanup(struct child_struct *child)
542 {
543         char *dname;
544
545         asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
546         fio_deltree(child, dname);
547         free(dname);
548
549         asprintf(&dname, "%s%s", child->directory, "/clients");
550         rmdir(dname);
551         free(dname);
552 }
553
554
555 static void fio_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
556 {
557         struct ftable *ftable = (struct ftable *)child->private;
558         int i = find_handle(child, handle);
559         struct utimbuf tm;
560         struct stat st;
561         (void)child;
562         (void)handle;
563         (void)level;
564         (void)status;
565         xattr_fd_read_hook(child, ftable[i].fd);
566
567         fstat(ftable[i].fd, &st);
568
569         tm.actime = st.st_atime - 10;
570         tm.modtime = st.st_mtime - 12;
571
572         utime(ftable[i].name, &tm);
573
574         if (!S_ISDIR(st.st_mode)) {
575                 xattr_fd_write_hook(child, ftable[i].fd);
576         }
577 }
578
579 static void fio_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
580               const char *status)
581 {
582         struct ftable *ftable = (struct ftable *)child->private;
583         int i = find_handle(child, handle);
584         struct flock lock;
585
586         (void)child;
587         (void)status;
588
589         lock.l_type = F_WRLCK;
590         lock.l_whence = SEEK_SET;
591         lock.l_start = offset;
592         lock.l_len = size;
593         lock.l_pid = 0;
594
595         fcntl(ftable[i].fd, F_SETLKW, &lock);
596 }
597
598 static void fio_unlockx(struct child_struct *child,
599                 int handle, uint32_t offset, int size, const char *status)
600 {
601         struct ftable *ftable = (struct ftable *)child->private;
602         int i = find_handle(child, handle);
603         struct flock lock;
604
605         (void)child;
606         (void)status;
607
608         lock.l_type = F_UNLCK;
609         lock.l_whence = SEEK_SET;
610         lock.l_start = offset;
611         lock.l_len = size;
612         lock.l_pid = 0;
613
614         fcntl(ftable[i].fd, F_SETLKW, &lock);
615 }
616
617 void nb_sleep(struct child_struct *child, int usec, const char *status)
618 {
619         (void)child;
620         (void)usec;
621         (void)status;
622         usleep(usec);
623 }
624
625 struct nb_operations nb_ops = {
626         .setup          = fio_setup,
627         .deltree        = fio_deltree,
628         .cleanup        = fio_cleanup,
629
630         .flush          = fio_flush,
631         .close          = fio_close,
632         .lockx          = fio_lockx,
633         .rmdir          = fio_rmdir,
634         .mkdir          = fio_mkdir,
635         .rename         = fio_rename,
636         .readx          = fio_readx,
637         .writex         = fio_writex,
638         .unlink         = fio_unlink,
639         .unlockx        = fio_unlockx,
640         .findfirst      = fio_findfirst,
641         .sfileinfo      = fio_sfileinfo,
642         .qfileinfo      = fio_qfileinfo,
643         .qpathinfo      = fio_qpathinfo,
644         .qfsinfo        = fio_qfsinfo,
645         .createx        = fio_createx,
646 };