041493b58b88aede5fb2824934406ffcb40634ea
[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(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("open directory \"%s\" for sync failed: %s\n",
65                        copy_name,
66                        strerror(errno));
67         } else {
68 #if defined(HAVE_FDATASYNC)
69                 if (fdatasync(dir_fd) == -1) {
70 #else
71                 if (fsync(dir_fd) == -1) {
72 #endif
73                         printf("datasync directory \"%s\" failed: %s\n",
74                                copy_name,
75                                strerror(errno));
76                 }
77                 if (close(dir_fd) == -1) {
78                         printf("close directory failed: %s\n",
79                                strerror(errno));
80                 }
81         }
82         free(copy_name);
83 }
84
85 static void xattr_fd_read_hook(int fd)
86 {
87 #if HAVE_EA_SUPPORT
88         char buf[44];
89         if (options.ea_enable) {
90                 memset(buf, 0, sizeof(buf));
91                 sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
92         }
93 #else
94         (void)fd;
95 #endif
96 }
97
98 static void xattr_fname_read_hook(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 }
109
110 static void xattr_fd_write_hook(int fd)
111 {
112 #if HAVE_EA_SUPPORT
113         if (options.ea_enable) {
114                 struct timeval tv;
115                 char buf[44];
116                 sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
117                 memset(buf, 0, sizeof(buf));
118                 /* give some probability of sharing */
119                 if (random() % 10 < 2) {
120                         *(time_t *)buf = time(NULL);
121                 } else {
122                         gettimeofday(&tv, NULL);
123                         memcpy(buf, &tv, sizeof(tv));
124                 }
125                 if (sys_fsetxattr(fd, "user.DosAttrib", buf, sizeof(buf), 0) != 0) {
126                         printf("fsetxattr failed - %s\n", strerror(errno));
127                         exit(1);
128                 }
129         }
130 #else
131         (void)fd;
132 #endif
133 }
134
135 static int expected_status(const char *status)
136 {
137         if (strcmp(status, "NT_STATUS_OK") == 0) {
138                 return 0;
139         }
140         if (strncmp(status, "0x", 2) == 0 &&
141             strtoul(status, NULL, 16) == 0) {
142                 return 0;
143         }
144         return -1;
145 }
146
147 /*
148   simulate pvfs_resolve_name()
149 */
150 static void resolve_name(const char *name)
151 {
152         struct stat st;
153         char *dname, *fname;
154         DIR *dir;
155         char *p;
156         struct dirent *d;
157
158         return;
159
160         if (name == NULL) return;
161
162         if (stat(name, &st) == 0) {
163                 xattr_fname_read_hook(name);
164                 return;
165         }
166
167         dname = strdup(name);
168         p = strrchr(dname, '/');
169         if (!p) return;
170         *p = 0;
171         fname = p+1;
172
173         dir = opendir(dname);
174         if (!dir) {
175                 free(dname);
176                 return;
177         }
178         while ((d = readdir(dir))) {
179                 if (strcasecmp(fname, d->d_name) == 0) break;
180         }
181         closedir(dir);
182         free(dname);
183 }
184
185 static void failed(struct child_struct *child)
186 {
187         child->failed = 1;
188         printf("ERROR: child %d failed\n", child->id);
189         exit(1);
190 }
191
192 void nb_setup(struct child_struct *child)
193 {
194         struct ftable *ftable;
195         ftable = calloc(MAX_FILES, sizeof(struct ftable));
196         child->private = ftable;
197         child->rate.last_time = timeval_current();
198         child->rate.last_bytes = 0;
199 }
200
201 void nb_unlink(struct child_struct *child, const char *fname, int attr, const char *status)
202 {
203         (void)attr;
204
205         resolve_name(fname);
206
207         if (unlink(fname) != expected_status(status)) {
208                 printf("(%d) unlink %s failed (%s) - expected %s\n", 
209                        child->line, fname, strerror(errno), status);
210                 failed(child);
211         }
212         if (options.sync_dirs) sync_parent(fname);
213 }
214
215 void nb_mkdir(struct child_struct *child, const char *dname, const char *status)
216 {
217         (void)child;
218         (void)status;
219         resolve_name(dname);
220         mkdir(dname, 0777);
221 }
222
223 void nb_rmdir(struct child_struct *child, const char *fname, const char *status)
224 {
225         resolve_name(fname);
226
227         if (rmdir(fname) != expected_status(status)) {
228                 printf("(%d) rmdir %s failed (%s) - expected %s\n", 
229                        child->line, fname, strerror(errno), status);
230                 failed(child);
231         }
232         if (options.sync_dirs) sync_parent(fname);
233 }
234
235 void nb_createx(struct child_struct *child, const char *fname, 
236                 uint32_t create_options, uint32_t create_disposition, int fnum,
237                 const char *status)
238 {
239         int fd, i;
240         int flags = O_RDWR;
241         struct stat st;
242         struct ftable *ftable = (struct ftable *)child->private;
243
244         resolve_name(fname);
245
246         if (options.sync_open) flags |= O_SYNC;
247
248         if (create_disposition == FILE_CREATE) {
249                 flags |= O_CREAT;
250         }
251
252         if (create_disposition == FILE_OVERWRITE ||
253             create_disposition == FILE_OVERWRITE_IF) {
254                 flags |= O_CREAT | O_TRUNC;
255         }
256
257         if (create_options & FILE_DIRECTORY_FILE) {
258                 /* not strictly correct, but close enough */
259                 mkdir(fname, 0700);
260         }
261
262         if (create_options & FILE_DIRECTORY_FILE) flags = O_RDONLY|O_DIRECTORY;
263
264         fd = open(fname, flags, 0600);
265         if (fd == -1 && errno == EISDIR) {
266                 flags = O_RDONLY|O_DIRECTORY;
267                 fd = open(fname, flags, 0600);
268         }
269         if (fd == -1) {
270                 if (expected_status(status) == 0) {
271                         printf("(%d) open %s failed for handle %d (%s)\n", 
272                                child->line, fname, fnum, strerror(errno));
273                 }
274                 return;
275         }
276         if (expected_status(status) != 0) {
277                 printf("(%d) open %s succeeded for handle %d\n", 
278                        child->line, fname, fnum);
279                 close(fd);
280                 return;
281         }
282         
283         for (i=0;i<MAX_FILES;i++) {
284                 if (ftable[i].handle == 0) break;
285         }
286         if (i == MAX_FILES) {
287                 printf("file table full for %s\n", fname);
288                 exit(1);
289         }
290         ftable[i].name = strdup(fname);
291         ftable[i].handle = fnum;
292         ftable[i].fd = fd;
293
294         fstat(fd, &st);
295
296         if (!S_ISDIR(st.st_mode)) {
297                 xattr_fd_write_hook(fd);
298         }
299 }
300
301 void nb_writex(struct child_struct *child, int handle, int offset, 
302                int size, int ret_size, const char *status)
303 {
304         int i = find_handle(child, handle);
305         void *buf;
306         struct stat st;
307         struct ftable *ftable = (struct ftable *)child->private;
308
309         (void)status;
310
311         buf = calloc(size, 1);
312
313         if (size == 1 && fstat(ftable[i].fd, &st) == 0) {
314                 if (st.st_size > offset) {
315                         unsigned char c;
316                         pread(ftable[i].fd, &c, 1, offset);
317                         if (c == ((unsigned char *)buf)[0]) {
318                                 free(buf);
319                                 child->bytes += size;
320                                 return;
321                         }
322                 } else if (((unsigned char *)buf)[0] == 0) {
323                         ftruncate(ftable[i].fd, offset+1);
324                         free(buf);
325                         child->bytes += size;
326                         return;
327                 } 
328         }
329
330         if (pwrite(ftable[i].fd, buf, size, offset) != ret_size) {
331                 printf("write failed on handle %d (%s)\n", handle, strerror(errno));
332                 exit(1);
333         }
334
335         if (options.do_fsync) fsync(ftable[i].fd);
336
337         free(buf);
338
339         child->bytes += size;
340         child->bytes_since_fsync += size;
341 }
342
343 void nb_readx(struct child_struct *child, int handle, int offset, 
344               int size, int ret_size, const char *status)
345 {
346         int i = find_handle(child, handle);
347         void *buf;
348         struct ftable *ftable = (struct ftable *)child->private;
349
350         (void)status;
351
352         buf = malloc(size);
353
354         if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
355                 printf("read failed on handle %d (%s)\n", handle, strerror(errno));
356         }
357
358         free(buf);
359
360         child->bytes += size;
361 }
362
363 void nb_close(struct child_struct *child, int handle, const char *status)
364 {
365         struct ftable *ftable = (struct ftable *)child->private;
366         int i = find_handle(child, handle);
367         (void)status;
368         close(ftable[i].fd);
369         ftable[i].handle = 0;
370         if (ftable[i].name) free(ftable[i].name);
371         ftable[i].name = NULL;
372 }
373
374 void nb_rename(struct child_struct *child, const char *old, const char *new, const char *status)
375 {
376         resolve_name(old);
377         resolve_name(new);
378
379         if (rename(old, new) != expected_status(status)) {
380                 printf("rename %s %s failed (%s) - expected %s\n", 
381                        old, new, strerror(errno), status);
382                 failed(child);
383         }
384         if (options.sync_dirs) sync_parent(new);
385 }
386
387 void nb_flush(struct child_struct *child, int handle, const char *status)
388 {
389         struct ftable *ftable = (struct ftable *)child->private;
390         int i = find_handle(child, handle);
391         (void)status;
392         fsync(ftable[i].fd);
393 }
394
395 void nb_qpathinfo(struct child_struct *child, const char *fname, int level, 
396                   const char *status)
397 {
398         (void)child;
399         (void)level;
400         (void)status;
401         resolve_name(fname);
402 }
403
404 void nb_qfileinfo(struct child_struct *child, int handle, int level, const char *status)
405 {
406         struct ftable *ftable = (struct ftable *)child->private;
407         struct stat st;
408         int i = find_handle(child, handle);
409         (void)child;
410         (void)level;
411         (void)status;
412         fstat(ftable[i].fd, &st);
413         xattr_fd_read_hook(ftable[i].fd);
414 }
415
416 void nb_qfsinfo(struct child_struct *child, int level, const char *status)
417 {
418         struct statvfs st;
419
420         (void)level;
421         (void)status;
422
423         statvfs(child->directory, &st);
424 }
425
426 void nb_findfirst(struct child_struct *child, const char *fname, int level, int maxcnt, 
427                   int count, const char *status)
428 {
429         DIR *dir;
430         struct dirent *d;
431         char *p;
432
433         (void)child;
434         (void)level;
435         (void)count;
436         (void)status;
437
438         resolve_name(fname);
439
440         if (strpbrk(fname, "<>*?\"") == NULL) {
441                 return;
442         }
443
444         p = strrchr(fname, '/');
445         if (!p) return;
446         *p = 0;
447         dir = opendir(fname);
448         if (!dir) return;
449         while (maxcnt && (d = readdir(dir))) maxcnt--;
450         closedir(dir);
451 }
452
453 void nb_cleanup(struct child_struct *child)
454 {
455         char *dname;
456
457         asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
458         nb_deltree(child, dname);
459         free(dname);
460
461         asprintf(&dname, "%s%s", child->directory, "/clients");
462         rmdir(dname);
463         free(dname);
464 }
465
466 void nb_deltree(struct child_struct *child, const char *dname)
467 {
468         DIR *d;
469         struct dirent *de;
470         (void)child;
471         
472         d = opendir(dname);
473         if (d == NULL) return;
474
475         for (de=readdir(d);de;de=readdir(d)) {
476                 struct stat st;
477                 char *fname = NULL;
478                 if (strcmp(de->d_name, ".") == 0 ||
479                     strcmp(de->d_name, "..") == 0) {
480                         continue;
481                 }
482                 asprintf(&fname, "%s/%s", dname, de->d_name);
483                 if (fname == NULL) {
484                         printf("Out of memory\n");
485                         exit(1);
486                 }
487                 if (stat(fname, &st) != 0) {
488                         continue;
489                 }
490                 if (S_ISDIR(st.st_mode)) {
491                         nb_deltree(child, fname);
492                 } else {
493                         if (unlink(fname) != 0) {
494                                 printf("[%d] unlink '%s' failed - %s\n",
495                                        child->line, fname, strerror(errno));
496                         }
497                 }
498                 free(fname);
499         }
500         closedir(d);
501 }
502
503 void nb_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
504 {
505         struct ftable *ftable = (struct ftable *)child->private;
506         int i = find_handle(child, handle);
507         struct utimbuf tm;
508         struct stat st;
509         (void)child;
510         (void)handle;
511         (void)level;
512         (void)status;
513         xattr_fd_read_hook(ftable[i].fd);
514
515         fstat(ftable[i].fd, &st);
516
517         tm.actime = st.st_atime - 10;
518         tm.modtime = st.st_mtime - 12;
519
520         utime(ftable[i].name, &tm);
521
522         if (!S_ISDIR(st.st_mode)) {
523                 xattr_fd_write_hook(ftable[i].fd);
524         }
525 }
526
527 void nb_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
528               const char *status)
529 {
530         struct ftable *ftable = (struct ftable *)child->private;
531         int i = find_handle(child, handle);
532         struct flock lock;
533
534         (void)child;
535         (void)status;
536
537         lock.l_type = F_WRLCK;
538         lock.l_whence = SEEK_SET;
539         lock.l_start = offset;
540         lock.l_len = size;
541         lock.l_pid = 0;
542
543         fcntl(ftable[i].fd, F_SETLKW, &lock);
544 }
545
546 void nb_unlockx(struct child_struct *child,
547                 int handle, uint32_t offset, int size, const char *status)
548 {
549         struct ftable *ftable = (struct ftable *)child->private;
550         int i = find_handle(child, handle);
551         struct flock lock;
552
553         (void)child;
554         (void)status;
555
556         lock.l_type = F_UNLCK;
557         lock.l_whence = SEEK_SET;
558         lock.l_start = offset;
559         lock.l_len = size;
560         lock.l_pid = 0;
561
562         fcntl(ftable[i].fd, F_SETLKW, &lock);
563 }
564
565 void nb_sleep(struct child_struct *child, int usec, const char *status)
566 {
567         (void)child;
568         (void)usec;
569         (void)status;
570         usleep(usec);
571 }