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