4dc0a64a3e386c9271e5e78e227b654a2c0ef4b3
[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 (options.no_resolve) {
161                 return;
162         }
163
164         if (name == NULL) return;
165
166         if (stat(name, &st) == 0) {
167                 xattr_fname_read_hook(child, name);
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                 mkdir(fname, 0700);
278         }
279
280         if (create_options & FILE_DIRECTORY_FILE) flags = O_RDONLY|O_DIRECTORY;
281
282         fd = open(fname, flags, 0600);
283         if (fd == -1 && errno == EISDIR) {
284                 flags = O_RDONLY|O_DIRECTORY;
285                 fd = open(fname, flags, 0600);
286         }
287         if (fd == -1) {
288                 if (expected_status(status) == 0) {
289                         printf("[%d] open %s failed for handle %d (%s)\n", 
290                                child->line, fname, fnum, strerror(errno));
291                 }
292                 return;
293         }
294         if (expected_status(status) != 0) {
295                 printf("[%d] open %s succeeded for handle %d\n", 
296                        child->line, fname, fnum);
297                 close(fd);
298                 return;
299         }
300         
301         for (i=0;i<MAX_FILES;i++) {
302                 if (ftable[i].handle == 0) break;
303         }
304         if (i == MAX_FILES) {
305                 printf("file table full for %s\n", fname);
306                 exit(1);
307         }
308         ftable[i].name = strdup(fname);
309         ftable[i].handle = fnum;
310         ftable[i].fd = fd;
311
312         fstat(fd, &st);
313
314         if (!S_ISDIR(st.st_mode)) {
315                 xattr_fd_write_hook(child, fd);
316         }
317 }
318
319 void nb_writex(struct child_struct *child, int handle, int offset, 
320                int size, int ret_size, const char *status)
321 {
322         int i = find_handle(child, handle);
323         void *buf;
324         struct stat st;
325         struct ftable *ftable = (struct ftable *)child->private;
326         ssize_t ret;
327
328         (void)status;
329
330         buf = calloc(size, 1);
331
332         if (options.one_byte_write_fix &&
333             size == 1 && fstat(ftable[i].fd, &st) == 0) {
334                 if (st.st_size > offset) {
335                         unsigned char c;
336                         pread(ftable[i].fd, &c, 1, offset);
337                         if (c == ((unsigned char *)buf)[0]) {
338                                 free(buf);
339                                 child->bytes += size;
340                                 return;
341                         }
342                 } else if (((unsigned char *)buf)[0] == 0) {
343                         ftruncate(ftable[i].fd, offset+1);
344                         free(buf);
345                         child->bytes += size;
346                         return;
347                 } 
348         }
349
350         ret = pwrite(ftable[i].fd, buf, size, offset);
351         if (ret == -1) {
352                 printf("[%d] write failed on handle %d (%s)\n", 
353                        child->line, handle, strerror(errno));
354                 exit(1);
355         }
356         if (ret != ret_size) {
357                 printf("[%d] wrote %d bytes, expected to write %d bytes on handle %d\n", 
358                        child->line, (int)ret, (int)ret_size, handle);
359                 exit(1);
360         }
361
362         if (options.do_fsync) fsync(ftable[i].fd);
363
364         free(buf);
365
366         child->bytes += size;
367         child->bytes_since_fsync += size;
368 }
369
370 void nb_readx(struct child_struct *child, int handle, int offset, 
371               int size, int ret_size, const char *status)
372 {
373         int i = find_handle(child, handle);
374         void *buf;
375         struct ftable *ftable = (struct ftable *)child->private;
376
377         (void)status;
378
379         buf = malloc(size);
380
381         if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
382                 printf("[%d] read failed on handle %d (%s)\n", 
383                        child->line, handle, strerror(errno));
384         }
385
386         free(buf);
387
388         child->bytes += size;
389 }
390
391 void nb_close(struct child_struct *child, int handle, const char *status)
392 {
393         struct ftable *ftable = (struct ftable *)child->private;
394         int i = find_handle(child, handle);
395         (void)status;
396         close(ftable[i].fd);
397         ftable[i].handle = 0;
398         if (ftable[i].name) free(ftable[i].name);
399         ftable[i].name = NULL;
400 }
401
402 void nb_rename(struct child_struct *child, const char *old, const char *new, const char *status)
403 {
404         resolve_name(child, old);
405         resolve_name(child, new);
406
407         if (options.stat_check) {
408                 struct stat st;
409                 if (stat(old, &st) != 0 && expected_status(status) == 0) {
410                         printf("[%d] rename %s %s failed - file doesn't exist\n",
411                                child->line, old, new);
412                         failed(child);
413                         return;
414                 }
415         }
416
417         if (rename(old, new) != expected_status(status)) {
418                 printf("[%d] rename %s %s failed (%s) - expected %s\n", 
419                        child->line, old, new, strerror(errno), status);
420                 failed(child);
421         }
422         if (options.sync_dirs) sync_parent(child, new);
423 }
424
425 void nb_flush(struct child_struct *child, int handle, const char *status)
426 {
427         struct ftable *ftable = (struct ftable *)child->private;
428         int i = find_handle(child, handle);
429         (void)status;
430         fsync(ftable[i].fd);
431 }
432
433 void nb_qpathinfo(struct child_struct *child, const char *fname, int level, 
434                   const char *status)
435 {
436         (void)child;
437         (void)level;
438         (void)status;
439         resolve_name(child, fname);
440 }
441
442 void nb_qfileinfo(struct child_struct *child, int handle, int level, const char *status)
443 {
444         struct ftable *ftable = (struct ftable *)child->private;
445         struct stat st;
446         int i = find_handle(child, handle);
447         (void)child;
448         (void)level;
449         (void)status;
450         fstat(ftable[i].fd, &st);
451         xattr_fd_read_hook(child, ftable[i].fd);
452 }
453
454 void nb_qfsinfo(struct child_struct *child, int level, const char *status)
455 {
456         struct statvfs st;
457
458         (void)level;
459         (void)status;
460
461         statvfs(child->directory, &st);
462 }
463
464 void nb_findfirst(struct child_struct *child, const char *fname, int level, int maxcnt, 
465                   int count, const char *status)
466 {
467         DIR *dir;
468         struct dirent *d;
469         char *p;
470
471         (void)child;
472         (void)level;
473         (void)count;
474         (void)status;
475
476         resolve_name(child, fname);
477
478         if (strpbrk(fname, "<>*?\"") == NULL) {
479                 return;
480         }
481
482         p = strrchr(fname, '/');
483         if (!p) return;
484         *p = 0;
485         dir = opendir(fname);
486         if (!dir) return;
487         while (maxcnt && (d = readdir(dir))) maxcnt--;
488         closedir(dir);
489 }
490
491 void nb_cleanup(struct child_struct *child)
492 {
493         char *dname;
494
495         asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
496         nb_deltree(child, dname);
497         free(dname);
498
499         asprintf(&dname, "%s%s", child->directory, "/clients");
500         rmdir(dname);
501         free(dname);
502 }
503
504 void nb_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                         nb_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 void nb_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
542 {
543         struct ftable *ftable = (struct ftable *)child->private;
544         int i = find_handle(child, handle);
545         struct utimbuf tm;
546         struct stat st;
547         (void)child;
548         (void)handle;
549         (void)level;
550         (void)status;
551         xattr_fd_read_hook(child, ftable[i].fd);
552
553         fstat(ftable[i].fd, &st);
554
555         tm.actime = st.st_atime - 10;
556         tm.modtime = st.st_mtime - 12;
557
558         utime(ftable[i].name, &tm);
559
560         if (!S_ISDIR(st.st_mode)) {
561                 xattr_fd_write_hook(child, ftable[i].fd);
562         }
563 }
564
565 void nb_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
566               const char *status)
567 {
568         struct ftable *ftable = (struct ftable *)child->private;
569         int i = find_handle(child, handle);
570         struct flock lock;
571
572         (void)child;
573         (void)status;
574
575         lock.l_type = F_WRLCK;
576         lock.l_whence = SEEK_SET;
577         lock.l_start = offset;
578         lock.l_len = size;
579         lock.l_pid = 0;
580
581         fcntl(ftable[i].fd, F_SETLKW, &lock);
582 }
583
584 void nb_unlockx(struct child_struct *child,
585                 int handle, uint32_t offset, int size, const char *status)
586 {
587         struct ftable *ftable = (struct ftable *)child->private;
588         int i = find_handle(child, handle);
589         struct flock lock;
590
591         (void)child;
592         (void)status;
593
594         lock.l_type = F_UNLCK;
595         lock.l_whence = SEEK_SET;
596         lock.l_start = offset;
597         lock.l_len = size;
598         lock.l_pid = 0;
599
600         fcntl(ftable[i].fd, F_SETLKW, &lock);
601 }
602
603 void nb_sleep(struct child_struct *child, int usec, const char *status)
604 {
605         (void)child;
606         (void)usec;
607         (void)status;
608         usleep(usec);
609 }