acdeec455aa92c8df3e3496dc38e0e0b8773d831
[jelmer/samba4-debian.git] / source / ntvfs / nbench / vfs_nbench.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    a pass-thru NTVFS module to record a NBENCH load file
5
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   "passthru" in this module refers to the next level of NTVFS being used
25 */
26
27 #include "includes.h"
28
29 /* this is stored in ntvfs_private */
30 struct nbench_private {
31         const struct ntvfs_ops *passthru_ops;
32         void *passthru_private;
33         const struct ntvfs_ops *nbench_ops;
34         int log_fd;
35 };
36
37
38 /*
39   log one request to the nbench log
40 */
41 static void nbench_log(struct nbench_private *private, 
42                        const char *format, ...)
43 {
44         va_list ap;
45         char *s = NULL;
46
47         va_start(ap, format);
48         vasprintf(&s, format, ap);
49         va_end(ap);
50
51         write(private->log_fd, s, strlen(s));
52         free(s);
53 }
54
55
56 /*
57   when we call the next stacked level of NTVFS module we need
58   to give it its own private pointer, plus its own NTVFS operations structure.
59   Then we need to restore both of these after the call, as the next level could
60   modify either of these
61 */
62 #define PASS_THRU(conn, op, args) do { \
63         conn->ntvfs_private = private->passthru_private; \
64         conn->ntvfs_ops = private->passthru_ops; \
65 \
66         status = private->passthru_ops->op args; \
67 \
68         private->passthru_private = conn->ntvfs_private; \
69         private->passthru_ops = conn->ntvfs_ops; \
70 \
71         conn->ntvfs_private = private; \
72         conn->ntvfs_ops = private->nbench_ops; \
73 } while (0)
74
75 /*
76   this pass through macro operates on request contexts, and disables
77   async calls. 
78
79   async calls are a pain for the nbench module as it makes pulling the
80   status code and any result parameters much harder.
81 */
82 #define PASS_THRU_REQ(req, op, args) do { \
83         void *send_fn_saved = req->async.send_fn; \
84         req->async.send_fn = NULL; \
85         PASS_THRU(req->conn, op, args); \
86         req->async.send_fn = send_fn_saved; \
87 } while (0)
88
89
90 /*
91   connect to a share - used when a tree_connect operation comes in.
92 */
93 static NTSTATUS nbench_connect(struct request_context *req, const char *sharename)
94 {
95         struct nbench_private *private;
96         const char *passthru;
97         NTSTATUS status;
98         char *logname = NULL;
99
100         private = talloc_p(req->conn->mem_ctx, struct nbench_private);
101         if (!private) {
102                 return NT_STATUS_NO_MEMORY;
103         }
104
105         asprintf(&logname, "/tmp/nbenchlog.%u", getpid());
106         private->log_fd = open(logname, O_WRONLY|O_CREAT|O_APPEND, 0644);
107         free(logname);
108
109         if (private->log_fd == -1) {
110                 DEBUG(0,("Failed to open nbench log\n"));
111                 return NT_STATUS_UNSUCCESSFUL;
112         }
113
114         passthru = lp_parm_string(req->conn->service, "nbench", "passthru");
115
116         private->passthru_private = NULL;
117         private->nbench_ops = req->conn->ntvfs_ops;
118         private->passthru_ops = ntvfs_backend_byname(passthru, NTVFS_DISK);
119
120         if (!private->passthru_ops) {
121                 DEBUG(0,("Unable to connect to '%s' pass through backend\n", passthru));
122                 return NT_STATUS_UNSUCCESSFUL;
123         }
124         
125         PASS_THRU(req->conn, connect, (req, sharename));
126
127         return status;
128 }
129
130 /*
131   disconnect from a share
132 */
133 static NTSTATUS nbench_disconnect(struct tcon_context *conn)
134 {
135         struct nbench_private *private = conn->ntvfs_private;
136         NTSTATUS status;
137
138         PASS_THRU(conn, disconnect, (conn));
139
140         close(private->log_fd);
141
142         return status;
143 }
144
145 /*
146   delete a file - the dirtype specifies the file types to include in the search. 
147   The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
148 */
149 static NTSTATUS nbench_unlink(struct request_context *req, struct smb_unlink *unl)
150 {
151         struct nbench_private *private = req->conn->ntvfs_private;
152         NTSTATUS status;
153
154         PASS_THRU_REQ(req, unlink, (req, unl));
155
156         nbench_log(private, "Unlink \"%s\" 0x%x %s\n", 
157                    unl->in.pattern, unl->in.attrib, 
158                    get_nt_error_c_code(status));
159
160         return status;
161 }
162
163 /*
164   ioctl interface
165 */
166 static NTSTATUS nbench_ioctl(struct request_context *req, union smb_ioctl *io)
167 {
168         struct nbench_private *private = req->conn->ntvfs_private;
169         NTSTATUS status;
170
171         PASS_THRU_REQ(req, ioctl, (req, io));
172
173         nbench_log(private, "Ioctl - NOT HANDLED\n");
174
175         return status;
176 }
177
178 /*
179   check if a directory exists
180 */
181 static NTSTATUS nbench_chkpath(struct request_context *req, struct smb_chkpath *cp)
182 {
183         struct nbench_private *private = req->conn->ntvfs_private;
184         NTSTATUS status;
185
186         PASS_THRU_REQ(req, chkpath, (req, cp));
187
188         nbench_log(private, "Chkpath \"%s\" %s\n", 
189                    cp->in.path, 
190                    get_nt_error_c_code(status));
191
192         return status;
193 }
194
195 /*
196   return info on a pathname
197 */
198 static NTSTATUS nbench_qpathinfo(struct request_context *req, union smb_fileinfo *info)
199 {
200         struct nbench_private *private = req->conn->ntvfs_private;
201         NTSTATUS status;
202
203         PASS_THRU_REQ(req, qpathinfo, (req, info));
204
205         nbench_log(private, "QUERY_PATH_INFORMATION \"%s\" %d %s\n", 
206                    info->generic.in.fname, 
207                    info->generic.level,
208                    get_nt_error_c_code(status));
209
210         return status;
211 }
212
213 /*
214   query info on a open file
215 */
216 static NTSTATUS nbench_qfileinfo(struct request_context *req, union smb_fileinfo *info)
217 {
218         struct nbench_private *private = req->conn->ntvfs_private;
219         NTSTATUS status;
220
221         PASS_THRU_REQ(req, qfileinfo, (req, info));
222
223         nbench_log(private, "QUERY_FILE_INFORMATION %d %d %s\n", 
224                    info->generic.in.fnum, 
225                    info->generic.level,
226                    get_nt_error_c_code(status));
227
228         return status;
229 }
230
231
232 /*
233   set info on a pathname
234 */
235 static NTSTATUS nbench_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
236 {
237         struct nbench_private *private = req->conn->ntvfs_private;
238         NTSTATUS status;
239
240         PASS_THRU_REQ(req, setpathinfo, (req, st));
241
242         nbench_log(private, "SET_PATH_INFORMATION \"%s\" %d %s\n", 
243                    st->generic.file.fname, 
244                    st->generic.level,
245                    get_nt_error_c_code(status));
246
247         return status;
248 }
249
250 /*
251   open a file
252 */
253 static NTSTATUS nbench_open(struct request_context *req, union smb_open *io)
254 {
255         struct nbench_private *private = req->conn->ntvfs_private;
256         NTSTATUS status;
257
258         PASS_THRU_REQ(req, open, (req, io));
259
260         switch (io->generic.level) {
261         case RAW_OPEN_NTCREATEX:
262                 nbench_log(private, "NTCreateX \"%s\" 0x%x 0x%x %d %s\n", 
263                            io->ntcreatex.in.fname, 
264                            io->ntcreatex.in.create_options, 
265                            io->ntcreatex.in.open_disposition, 
266                            io->ntcreatex.out.fnum,
267                            get_nt_error_c_code(status));
268                 break;
269
270         default:
271                 nbench_log(private, "Open-%d - NOT HANDLED\n",
272                            io->generic.level);
273                 break;
274         }
275
276         return status;
277 }
278
279 /*
280   create a directory
281 */
282 static NTSTATUS nbench_mkdir(struct request_context *req, union smb_mkdir *md)
283 {
284         struct nbench_private *private = req->conn->ntvfs_private;
285         NTSTATUS status;
286
287         PASS_THRU_REQ(req, mkdir, (req, md));
288
289         nbench_log(private, "Mkdir - NOT HANDLED\n");
290
291         return status;
292 }
293
294 /*
295   remove a directory
296 */
297 static NTSTATUS nbench_rmdir(struct request_context *req, struct smb_rmdir *rd)
298 {
299         struct nbench_private *private = req->conn->ntvfs_private;
300         NTSTATUS status;
301
302         PASS_THRU_REQ(req, rmdir, (req, rd));
303
304         nbench_log(private, "Rmdir \"%s\" %s\n", 
305                    rd->in.path, 
306                    get_nt_error_c_code(status));
307
308         return status;
309 }
310
311 /*
312   rename a set of files
313 */
314 static NTSTATUS nbench_rename(struct request_context *req, union smb_rename *ren)
315 {
316         struct nbench_private *private = req->conn->ntvfs_private;
317         NTSTATUS status;
318
319         PASS_THRU_REQ(req, rename, (req, ren));
320
321         switch (ren->generic.level) {
322         case RAW_RENAME_RENAME:
323                 nbench_log(private, "Rename \"%s\" \"%s\" %s\n", 
324                            ren->rename.in.pattern1, 
325                            ren->rename.in.pattern2, 
326                            get_nt_error_c_code(status));
327                 break;
328
329         default:
330                 nbench_log(private, "Rename-%d - NOT HANDLED\n",
331                            ren->generic.level);
332                 break;
333         }
334
335         return status;
336 }
337
338 /*
339   copy a set of files
340 */
341 static NTSTATUS nbench_copy(struct request_context *req, struct smb_copy *cp)
342 {
343         struct nbench_private *private = req->conn->ntvfs_private;
344         NTSTATUS status;
345
346         PASS_THRU_REQ(req, copy, (req, cp));
347
348         nbench_log(private, "Copy - NOT HANDLED\n");
349
350         return status;
351 }
352
353 /*
354   read from a file
355 */
356 static NTSTATUS nbench_read(struct request_context *req, union smb_read *rd)
357 {
358         struct nbench_private *private = req->conn->ntvfs_private;
359         NTSTATUS status;
360
361         PASS_THRU_REQ(req, read, (req, rd));
362
363         switch (rd->generic.level) {
364         case RAW_READ_READX:
365                 nbench_log(private, "ReadX %d %d %d %d %s\n", 
366                            rd->readx.in.fnum, 
367                            (int)rd->readx.in.offset,
368                            rd->readx.in.maxcnt,
369                            rd->readx.out.nread,
370                            get_nt_error_c_code(status));
371                 break;
372         default:
373                 nbench_log(private, "Read-%d - NOT HANDLED\n",
374                            rd->generic.level);
375                 break;
376         }
377
378         return status;
379 }
380
381 /*
382   write to a file
383 */
384 static NTSTATUS nbench_write(struct request_context *req, union smb_write *wr)
385 {
386         struct nbench_private *private = req->conn->ntvfs_private;
387         NTSTATUS status;
388
389         PASS_THRU_REQ(req, write, (req, wr));
390
391         switch (wr->generic.level) {
392         case RAW_WRITE_WRITEX:
393                 nbench_log(private, "WriteX %d %d %d %d %s\n", 
394                            wr->writex.in.fnum, 
395                            (int)wr->writex.in.offset,
396                            wr->writex.in.count,
397                            wr->writex.out.nwritten,
398                            get_nt_error_c_code(status));
399                 break;
400
401         case RAW_WRITE_WRITE:
402                 nbench_log(private, "Write %d %d %d %d %s\n", 
403                            wr->write.in.fnum, 
404                            wr->write.in.offset,
405                            wr->write.in.count,
406                            wr->write.out.nwritten,
407                            get_nt_error_c_code(status));
408                 break;
409
410         default:
411                 nbench_log(private, "Write-%d - NOT HANDLED\n",
412                            wr->generic.level);
413                 break;
414         }
415
416         return status;
417 }
418
419 /*
420   seek in a file
421 */
422 static NTSTATUS nbench_seek(struct request_context *req, struct smb_seek *io)
423 {
424         struct nbench_private *private = req->conn->ntvfs_private;
425         NTSTATUS status;
426
427         PASS_THRU_REQ(req, seek, (req, io));
428
429         nbench_log(private, "Seek - NOT HANDLED\n");
430
431         return status;
432 }
433
434 /*
435   flush a file
436 */
437 static NTSTATUS nbench_flush(struct request_context *req, struct smb_flush *io)
438 {
439         struct nbench_private *private = req->conn->ntvfs_private;
440         NTSTATUS status;
441
442         PASS_THRU_REQ(req, flush, (req, io));
443
444         nbench_log(private, "Flush %d %s\n",
445                    io->in.fnum,
446                    get_nt_error_c_code(status));
447
448         return status;
449 }
450
451 /*
452   close a file
453 */
454 static NTSTATUS nbench_close(struct request_context *req, union smb_close *io)
455 {
456         struct nbench_private *private = req->conn->ntvfs_private;
457         NTSTATUS status;
458
459         PASS_THRU_REQ(req, close, (req, io));
460
461         switch (io->generic.level) {
462         case RAW_CLOSE_CLOSE:
463                 nbench_log(private, "Close %d %s\n",
464                            io->close.in.fnum,
465                            get_nt_error_c_code(status));
466                 break;
467
468         default:
469                 nbench_log(private, "Close-%d - NOT HANDLED\n",
470                            io->generic.level);
471                 break;
472         }               
473
474         return status;
475 }
476
477 /*
478   exit - closing files
479 */
480 static NTSTATUS nbench_exit(struct request_context *req)
481 {
482         struct nbench_private *private = req->conn->ntvfs_private;
483         NTSTATUS status;
484
485         PASS_THRU_REQ(req, exit, (req));
486
487         return status;
488 }
489
490 /*
491   lock a byte range
492 */
493 static NTSTATUS nbench_lock(struct request_context *req, union smb_lock *lck)
494 {
495         struct nbench_private *private = req->conn->ntvfs_private;
496         NTSTATUS status;
497
498         PASS_THRU_REQ(req, lock, (req, lck));
499
500         if (lck->generic.level == RAW_LOCK_LOCKX &&
501             lck->lockx.in.lock_cnt == 1 &&
502             lck->lockx.in.ulock_cnt == 0) {
503                 nbench_log(private, "LockX %d %d %d %s\n", 
504                            lck->lockx.in.fnum,
505                            (int)lck->lockx.in.locks[0].offset,
506                            (int)lck->lockx.in.locks[0].count,
507                            get_nt_error_c_code(status));
508         } else if (lck->generic.level == RAW_LOCK_LOCKX &&
509                    lck->lockx.in.ulock_cnt == 1) {
510                 nbench_log(private, "UnlockX %d %d %d %s\n", 
511                            lck->lockx.in.fnum,
512                            (int)lck->lockx.in.locks[0].offset,
513                            (int)lck->lockx.in.locks[0].count,
514                            get_nt_error_c_code(status));
515         } else {
516                 nbench_log(private, "Lock-%d - NOT HANDLED\n", lck->generic.level);
517         }
518
519         return status;
520 }
521
522 /*
523   set info on a open file
524 */
525 static NTSTATUS nbench_setfileinfo(struct request_context *req, 
526                                  union smb_setfileinfo *info)
527 {
528         struct nbench_private *private = req->conn->ntvfs_private;
529         NTSTATUS status;
530
531         PASS_THRU_REQ(req, setfileinfo, (req, info));
532
533         nbench_log(private, "SET_FILE_INFORMATION %d %d %s\n", 
534                    info->generic.file.fnum,
535                    info->generic.level,
536                    get_nt_error_c_code(status));
537
538         return status;
539 }
540
541
542 /*
543   return filesystem space info
544 */
545 static NTSTATUS nbench_fsinfo(struct request_context *req, union smb_fsinfo *fs)
546 {
547         struct nbench_private *private = req->conn->ntvfs_private;
548         NTSTATUS status;
549
550         PASS_THRU_REQ(req, fsinfo, (req, fs));
551
552         nbench_log(private, "QUERY_FS_INFORMATION %d %s\n", 
553                    fs->generic.level, 
554                    get_nt_error_c_code(status));
555
556         return status;
557 }
558
559 /*
560   return print queue info
561 */
562 static NTSTATUS nbench_lpq(struct request_context *req, union smb_lpq *lpq)
563 {
564         struct nbench_private *private = req->conn->ntvfs_private;
565         NTSTATUS status;
566
567         PASS_THRU_REQ(req, lpq, (req, lpq));
568
569         nbench_log(private, "Lpq-%d - NOT HANDLED\n", lpq->generic.level);
570
571         return status;
572 }
573
574 /* 
575    list files in a directory matching a wildcard pattern
576 */
577 static NTSTATUS nbench_search_first(struct request_context *req, union smb_search_first *io, 
578                                   void *search_private, 
579                                   BOOL (*callback)(void *, union smb_search_data *))
580 {
581         struct nbench_private *private = req->conn->ntvfs_private;
582         NTSTATUS status;
583
584         PASS_THRU_REQ(req, search_first, (req, io, search_private, callback));
585
586         switch (io->generic.level) {
587         case RAW_SEARCH_BOTH_DIRECTORY_INFO:
588                 nbench_log(private, "FIND_FIRST \"%s\" %d %d %d %s\n", 
589                            io->t2ffirst.in.pattern,
590                            io->generic.level,
591                            io->t2ffirst.in.max_count,
592                            io->t2ffirst.out.count,
593                            get_nt_error_c_code(status));
594                 break;
595                 
596         default:
597                 nbench_log(private, "Search-%d - NOT HANDLED\n", io->generic.level);
598                 break;
599         }
600
601         return status;
602 }
603
604 /* continue a search */
605 static NTSTATUS nbench_search_next(struct request_context *req, union smb_search_next *io, 
606                                  void *search_private, 
607                                  BOOL (*callback)(void *, union smb_search_data *))
608 {
609         struct nbench_private *private = req->conn->ntvfs_private;
610         NTSTATUS status;
611
612         PASS_THRU_REQ(req, search_next, (req, io, search_private, callback));
613
614         nbench_log(private, "Searchnext-%d - NOT HANDLED\n", io->generic.level);
615
616         return status;
617 }
618
619 /* close a search */
620 static NTSTATUS nbench_search_close(struct request_context *req, union smb_search_close *io)
621 {
622         struct nbench_private *private = req->conn->ntvfs_private;
623         NTSTATUS status;
624
625         PASS_THRU_REQ(req, search_close, (req, io));
626
627         nbench_log(private, "Searchclose-%d - NOT HANDLED\n", io->generic.level);
628
629         return status;
630 }
631
632 /* SMBtrans - not used on file shares */
633 static NTSTATUS nbench_trans(struct request_context *req, struct smb_trans2 *trans2)
634 {
635         struct nbench_private *private = req->conn->ntvfs_private;
636         NTSTATUS status;
637
638         PASS_THRU_REQ(req, trans, (req,trans2));
639
640         nbench_log(private, "Trans - NOT HANDLED\n");
641
642         return status;
643 }
644
645 /*
646   initialise the nbench backend, registering ourselves with the ntvfs subsystem
647  */
648 NTSTATUS ntvfs_nbench_init(void)
649 {
650         NTSTATUS ret;
651         struct ntvfs_ops ops;
652
653         ZERO_STRUCT(ops);
654
655         /* fill in the name and type */
656         ops.name = "nbench";
657         ops.type = NTVFS_DISK;
658         
659         /* fill in all the operations */
660         ops.connect = nbench_connect;
661         ops.disconnect = nbench_disconnect;
662         ops.unlink = nbench_unlink;
663         ops.chkpath = nbench_chkpath;
664         ops.qpathinfo = nbench_qpathinfo;
665         ops.setpathinfo = nbench_setpathinfo;
666         ops.open = nbench_open;
667         ops.mkdir = nbench_mkdir;
668         ops.rmdir = nbench_rmdir;
669         ops.rename = nbench_rename;
670         ops.copy = nbench_copy;
671         ops.ioctl = nbench_ioctl;
672         ops.read = nbench_read;
673         ops.write = nbench_write;
674         ops.seek = nbench_seek;
675         ops.flush = nbench_flush;       
676         ops.close = nbench_close;
677         ops.exit = nbench_exit;
678         ops.lock = nbench_lock;
679         ops.setfileinfo = nbench_setfileinfo;
680         ops.qfileinfo = nbench_qfileinfo;
681         ops.fsinfo = nbench_fsinfo;
682         ops.lpq = nbench_lpq;
683         ops.search_first = nbench_search_first;
684         ops.search_next = nbench_search_next;
685         ops.search_close = nbench_search_close;
686         ops.trans = nbench_trans;
687
688         /* we don't register a trans2 handler as we want to be able to
689            log individual trans2 requests */
690         ops.trans2 = NULL;
691
692         /* register ourselves with the NTVFS subsystem. */
693         ret = register_backend("ntvfs", &ops);
694
695         if (!NT_STATUS_IS_OK(ret)) {
696                 DEBUG(0,("Failed to register nbench backend!\n"));
697         }
698         
699         return ret;
700 }