[dbench @ tridge@samba.org-20070727060117-mjnjs3nd4ca15xbm]
[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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 void nb_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 nb_cleanup(struct child_struct *child)
505 {
506         char *dname;
507
508         asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
509         nb_deltree(child, dname);
510         free(dname);
511
512         asprintf(&dname, "%s%s", child->directory, "/clients");
513         rmdir(dname);
514         free(dname);
515 }
516
517 void nb_deltree(struct child_struct *child, const char *dname)
518 {
519         DIR *d;
520         struct dirent *de;
521         (void)child;
522         
523         d = opendir(dname);
524         if (d == NULL) return;
525
526         for (de=readdir(d);de;de=readdir(d)) {
527                 struct stat st;
528                 char *fname = NULL;
529                 if (strcmp(de->d_name, ".") == 0 ||
530                     strcmp(de->d_name, "..") == 0) {
531                         continue;
532                 }
533                 asprintf(&fname, "%s/%s", dname, de->d_name);
534                 if (fname == NULL) {
535                         printf("Out of memory\n");
536                         exit(1);
537                 }
538                 if (stat(fname, &st) != 0) {
539                         continue;
540                 }
541                 if (S_ISDIR(st.st_mode)) {
542                         nb_deltree(child, fname);
543                 } else {
544                         if (unlink(fname) != 0) {
545                                 printf("[%d] unlink '%s' failed - %s\n",
546                                        child->line, fname, strerror(errno));
547                         }
548                 }
549                 free(fname);
550         }
551         closedir(d);
552 }
553
554 void nb_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
555 {
556         struct ftable *ftable = (struct ftable *)child->private;
557         int i = find_handle(child, handle);
558         struct utimbuf tm;
559         struct stat st;
560         (void)child;
561         (void)handle;
562         (void)level;
563         (void)status;
564         xattr_fd_read_hook(child, ftable[i].fd);
565
566         fstat(ftable[i].fd, &st);
567
568         tm.actime = st.st_atime - 10;
569         tm.modtime = st.st_mtime - 12;
570
571         utime(ftable[i].name, &tm);
572
573         if (!S_ISDIR(st.st_mode)) {
574                 xattr_fd_write_hook(child, ftable[i].fd);
575         }
576 }
577
578 void nb_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
579               const char *status)
580 {
581         struct ftable *ftable = (struct ftable *)child->private;
582         int i = find_handle(child, handle);
583         struct flock lock;
584
585         (void)child;
586         (void)status;
587
588         lock.l_type = F_WRLCK;
589         lock.l_whence = SEEK_SET;
590         lock.l_start = offset;
591         lock.l_len = size;
592         lock.l_pid = 0;
593
594         fcntl(ftable[i].fd, F_SETLKW, &lock);
595 }
596
597 void nb_unlockx(struct child_struct *child,
598                 int handle, uint32_t offset, int size, const char *status)
599 {
600         struct ftable *ftable = (struct ftable *)child->private;
601         int i = find_handle(child, handle);
602         struct flock lock;
603
604         (void)child;
605         (void)status;
606
607         lock.l_type = F_UNLCK;
608         lock.l_whence = SEEK_SET;
609         lock.l_start = offset;
610         lock.l_len = size;
611         lock.l_pid = 0;
612
613         fcntl(ftable[i].fd, F_SETLKW, &lock);
614 }
615
616 void nb_sleep(struct child_struct *child, int usec, const char *status)
617 {
618         (void)child;
619         (void)usec;
620         (void)status;
621         usleep(usec);
622 }