s3: Convert aio_fork to pread/pwrite_send/recv
[samba.git] / source3 / modules / vfs_aio_fork.c
1 /*
2  * Simulate the Posix AIO using mmap/fork
3  *
4  * Copyright (C) Volker Lendecke 2008
5  * Copyright (C) Jeremy Allison 2010
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 2 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, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "system/shmem.h"
25 #include "smbd/smbd.h"
26 #include "smbd/globals.h"
27 #include "lib/async_req/async_sock.h"
28 #include "lib/util/tevent_unix.h"
29
30 #ifndef MAP_FILE
31 #define MAP_FILE 0
32 #endif
33
34 struct mmap_area {
35         size_t size;
36         volatile void *ptr;
37 };
38
39 static int mmap_area_destructor(struct mmap_area *area)
40 {
41         munmap((void *)area->ptr, area->size);
42         return 0;
43 }
44
45 static struct mmap_area *mmap_area_init(TALLOC_CTX *mem_ctx, size_t size)
46 {
47         struct mmap_area *result;
48         int fd;
49
50         result = talloc(mem_ctx, struct mmap_area);
51         if (result == NULL) {
52                 DEBUG(0, ("talloc failed\n"));
53                 goto fail;
54         }
55
56         fd = open("/dev/zero", O_RDWR);
57         if (fd == -1) {
58                 DEBUG(3, ("open(\"/dev/zero\") failed: %s\n",
59                           strerror(errno)));
60                 goto fail;
61         }
62
63         result->ptr = mmap(NULL, size, PROT_READ|PROT_WRITE,
64                            MAP_SHARED|MAP_FILE, fd, 0);
65         if (result->ptr == MAP_FAILED) {
66                 DEBUG(1, ("mmap failed: %s\n", strerror(errno)));
67                 goto fail;
68         }
69
70         close(fd);
71
72         result->size = size;
73         talloc_set_destructor(result, mmap_area_destructor);
74
75         return result;
76
77 fail:
78         TALLOC_FREE(result);
79         return NULL;
80 }
81
82 struct rw_cmd {
83         size_t n;
84         off_t offset;
85         bool read_cmd;
86 };
87
88 struct rw_ret {
89         ssize_t size;
90         int ret_errno;
91 };
92
93 struct aio_child_list;
94
95 struct aio_child {
96         struct aio_child *prev, *next;
97         struct aio_child_list *list;
98         pid_t pid;
99         int sockfd;
100         struct mmap_area *map;
101         bool dont_delete;       /* Marked as in use since last cleanup */
102         bool busy;
103 };
104
105 struct aio_child_list {
106         struct aio_child *children;
107         struct timed_event *cleanup_event;
108 };
109
110 static void free_aio_children(void **p)
111 {
112         TALLOC_FREE(*p);
113 }
114
115 static ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
116 {
117         struct msghdr msg;
118         struct iovec iov[1];
119         ssize_t n;
120 #ifndef HAVE_MSGHDR_MSG_CONTROL
121         int newfd;
122 #endif
123
124 #ifdef  HAVE_MSGHDR_MSG_CONTROL
125         union {
126           struct cmsghdr        cm;
127           char                          control[CMSG_SPACE(sizeof(int))];
128         } control_un;
129         struct cmsghdr  *cmptr;
130
131         msg.msg_control = control_un.control;
132         msg.msg_controllen = sizeof(control_un.control);
133 #else
134 #if HAVE_MSGHDR_MSG_ACCTRIGHTS
135         msg.msg_accrights = (caddr_t) &newfd;
136         msg.msg_accrightslen = sizeof(int);
137 #else
138 #error Can not pass file descriptors
139 #endif
140 #endif
141
142         msg.msg_name = NULL;
143         msg.msg_namelen = 0;
144         msg.msg_flags = 0;
145
146         iov[0].iov_base = (void *)ptr;
147         iov[0].iov_len = nbytes;
148         msg.msg_iov = iov;
149         msg.msg_iovlen = 1;
150
151         if ( (n = recvmsg(fd, &msg, 0)) <= 0) {
152                 return(n);
153         }
154
155 #ifdef  HAVE_MSGHDR_MSG_CONTROL
156         if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL
157             && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
158                 if (cmptr->cmsg_level != SOL_SOCKET) {
159                         DEBUG(10, ("control level != SOL_SOCKET"));
160                         errno = EINVAL;
161                         return -1;
162                 }
163                 if (cmptr->cmsg_type != SCM_RIGHTS) {
164                         DEBUG(10, ("control type != SCM_RIGHTS"));
165                         errno = EINVAL;
166                         return -1;
167                 }
168                 memcpy(recvfd, CMSG_DATA(cmptr), sizeof(*recvfd));
169         } else {
170                 *recvfd = -1;           /* descriptor was not passed */
171         }
172 #else
173         if (msg.msg_accrightslen == sizeof(int)) {
174                 *recvfd = newfd;
175         }
176         else {
177                 *recvfd = -1;           /* descriptor was not passed */
178         }
179 #endif
180
181         return(n);
182 }
183
184 static ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
185 {
186         struct msghdr   msg;
187         struct iovec    iov[1];
188
189 #ifdef  HAVE_MSGHDR_MSG_CONTROL
190         union {
191                 struct cmsghdr  cm;
192                 char control[CMSG_SPACE(sizeof(int))];
193         } control_un;
194         struct cmsghdr  *cmptr;
195
196         ZERO_STRUCT(msg);
197         ZERO_STRUCT(control_un);
198
199         msg.msg_control = control_un.control;
200         msg.msg_controllen = sizeof(control_un.control);
201
202         cmptr = CMSG_FIRSTHDR(&msg);
203         cmptr->cmsg_len = CMSG_LEN(sizeof(int));
204         cmptr->cmsg_level = SOL_SOCKET;
205         cmptr->cmsg_type = SCM_RIGHTS;
206         memcpy(CMSG_DATA(cmptr), &sendfd, sizeof(sendfd));
207 #else
208         ZERO_STRUCT(msg);
209         msg.msg_accrights = (caddr_t) &sendfd;
210         msg.msg_accrightslen = sizeof(int);
211 #endif
212
213         msg.msg_name = NULL;
214         msg.msg_namelen = 0;
215
216         ZERO_STRUCT(iov);
217         iov[0].iov_base = (void *)ptr;
218         iov[0].iov_len = nbytes;
219         msg.msg_iov = iov;
220         msg.msg_iovlen = 1;
221
222         return (sendmsg(fd, &msg, 0));
223 }
224
225 static void aio_child_cleanup(struct event_context *event_ctx,
226                               struct timed_event *te,
227                               struct timeval now,
228                               void *private_data)
229 {
230         struct aio_child_list *list = talloc_get_type_abort(
231                 private_data, struct aio_child_list);
232         struct aio_child *child, *next;
233
234         TALLOC_FREE(list->cleanup_event);
235
236         for (child = list->children; child != NULL; child = next) {
237                 next = child->next;
238
239                 if (child->busy) {
240                         DEBUG(10, ("child %d currently active\n",
241                                    (int)child->pid));
242                         continue;
243                 }
244
245                 if (child->dont_delete) {
246                         DEBUG(10, ("Child %d was active since last cleanup\n",
247                                    (int)child->pid));
248                         child->dont_delete = false;
249                         continue;
250                 }
251
252                 DEBUG(10, ("Child %d idle for more than 30 seconds, "
253                            "deleting\n", (int)child->pid));
254
255                 TALLOC_FREE(child);
256                 child = next;
257         }
258
259         if (list->children != NULL) {
260                 /*
261                  * Re-schedule the next cleanup round
262                  */
263                 list->cleanup_event = event_add_timed(server_event_context(), list,
264                                                       timeval_add(&now, 30, 0),
265                                                       aio_child_cleanup, list);
266
267         }
268 }
269
270 static struct aio_child_list *init_aio_children(struct vfs_handle_struct *handle)
271 {
272         struct aio_child_list *data = NULL;
273
274         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
275                 SMB_VFS_HANDLE_GET_DATA(handle, data, struct aio_child_list,
276                                         return NULL);
277         }
278
279         if (data == NULL) {
280                 data = talloc_zero(NULL, struct aio_child_list);
281                 if (data == NULL) {
282                         return NULL;
283                 }
284         }
285
286         /*
287          * Regardless of whether the child_list had been around or not, make
288          * sure that we have a cleanup timed event. This timed event will
289          * delete itself when it finds that no children are around anymore.
290          */
291
292         if (data->cleanup_event == NULL) {
293                 data->cleanup_event = event_add_timed(server_event_context(), data,
294                                                       timeval_current_ofs(30, 0),
295                                                       aio_child_cleanup, data);
296                 if (data->cleanup_event == NULL) {
297                         TALLOC_FREE(data);
298                         return NULL;
299                 }
300         }
301
302         if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
303                 SMB_VFS_HANDLE_SET_DATA(handle, data, free_aio_children,
304                                         struct aio_child_list, return False);
305         }
306
307         return data;
308 }
309
310 static void aio_child_loop(int sockfd, struct mmap_area *map)
311 {
312         while (true) {
313                 int fd = -1;
314                 ssize_t ret;
315                 struct rw_cmd cmd_struct;
316                 struct rw_ret ret_struct;
317
318                 ret = read_fd(sockfd, &cmd_struct, sizeof(cmd_struct), &fd);
319                 if (ret != sizeof(cmd_struct)) {
320                         DEBUG(10, ("read_fd returned %d: %s\n", (int)ret,
321                                    strerror(errno)));
322                         exit(1);
323                 }
324
325                 DEBUG(10, ("aio_child_loop: %s %d bytes at %d from fd %d\n",
326                            cmd_struct.read_cmd ? "read" : "write",
327                            (int)cmd_struct.n, (int)cmd_struct.offset, fd));
328
329 #ifdef ENABLE_BUILD_FARM_HACKS
330                 {
331                         /*
332                          * In the build farm, we want erratic behaviour for
333                          * async I/O times
334                          */
335                         uint8_t randval;
336                         unsigned msecs;
337                         /*
338                          * use generate_random_buffer, we just forked from a
339                          * common parent state
340                          */
341                         generate_random_buffer(&randval, sizeof(randval));
342                         msecs = randval + 20;
343                         DEBUG(10, ("delaying for %u msecs\n", msecs));
344                         smb_msleep(msecs);
345                 }
346 #endif
347
348
349                 ZERO_STRUCT(ret_struct);
350
351                 if (cmd_struct.read_cmd) {
352                         ret_struct.size = sys_pread(
353                                 fd, (void *)map->ptr, cmd_struct.n,
354                                 cmd_struct.offset);
355 #if 0
356 /* This breaks "make test" when run with aio_fork module. */
357 #ifdef ENABLE_BUILD_FARM_HACKS
358                         ret_struct.size = MAX(1, ret_struct.size * 0.9);
359 #endif
360 #endif
361                 }
362                 else {
363                         ret_struct.size = sys_pwrite(
364                                 fd, (void *)map->ptr, cmd_struct.n,
365                                 cmd_struct.offset);
366                 }
367
368                 DEBUG(10, ("aio_child_loop: syscall returned %d\n",
369                            (int)ret_struct.size));
370
371                 if (ret_struct.size == -1) {
372                         ret_struct.ret_errno = errno;
373                 }
374
375                 /*
376                  * Close the fd before telling our parent we're done. The
377                  * parent might close and re-open the file very quickly, and
378                  * with system-level share modes (GPFS) we would get an
379                  * unjustified SHARING_VIOLATION.
380                  */
381                 close(fd);
382
383                 ret = write_data(sockfd, (char *)&ret_struct,
384                                  sizeof(ret_struct));
385                 if (ret != sizeof(ret_struct)) {
386                         DEBUG(10, ("could not write ret_struct: %s\n",
387                                    strerror(errno)));
388                         exit(2);
389                 }
390         }
391 }
392
393 static int aio_child_destructor(struct aio_child *child)
394 {
395         char c=0;
396
397         SMB_ASSERT(!child->busy);
398
399         DEBUG(10, ("aio_child_destructor: removing child %d on fd %d\n",
400                         child->pid, child->sockfd));
401
402         /*
403          * closing the sockfd makes the child not return from recvmsg() on RHEL
404          * 5.5 so instead force the child to exit by writing bad data to it
405          */
406         write(child->sockfd, &c, sizeof(c));
407         close(child->sockfd);
408         DLIST_REMOVE(child->list->children, child);
409         return 0;
410 }
411
412 /*
413  * We have to close all fd's in open files, we might incorrectly hold a system
414  * level share mode on a file.
415  */
416
417 static struct files_struct *close_fsp_fd(struct files_struct *fsp,
418                                          void *private_data)
419 {
420         if ((fsp->fh != NULL) && (fsp->fh->fd != -1)) {
421                 close(fsp->fh->fd);
422                 fsp->fh->fd = -1;
423         }
424         return NULL;
425 }
426
427 static int create_aio_child(struct smbd_server_connection *sconn,
428                             struct aio_child_list *children,
429                             size_t map_size,
430                             struct aio_child **presult)
431 {
432         struct aio_child *result;
433         int fdpair[2];
434         int ret;
435
436         fdpair[0] = fdpair[1] = -1;
437
438         result = talloc_zero(children, struct aio_child);
439         if (result == NULL) {
440                 return ENOMEM;
441         }
442
443         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
444                 ret = errno;
445                 DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
446                 goto fail;
447         }
448
449         DEBUG(10, ("fdpair = %d/%d\n", fdpair[0], fdpair[1]));
450
451         result->map = mmap_area_init(result, map_size);
452         if (result->map == NULL) {
453                 ret = errno;
454                 DEBUG(0, ("Could not create mmap area\n"));
455                 goto fail;
456         }
457
458         result->pid = fork();
459         if (result->pid == -1) {
460                 ret = errno;
461                 DEBUG(0, ("fork failed: %s\n", strerror(errno)));
462                 goto fail;
463         }
464
465         if (result->pid == 0) {
466                 close(fdpair[0]);
467                 result->sockfd = fdpair[1];
468                 files_forall(sconn, close_fsp_fd, NULL);
469                 aio_child_loop(result->sockfd, result->map);
470         }
471
472         DEBUG(10, ("Child %d created with sockfd %d\n",
473                         result->pid, fdpair[0]));
474
475         result->sockfd = fdpair[0];
476         close(fdpair[1]);
477
478         result->list = children;
479         DLIST_ADD(children->children, result);
480
481         talloc_set_destructor(result, aio_child_destructor);
482
483         *presult = result;
484
485         return 0;
486
487  fail:
488         if (fdpair[0] != -1) close(fdpair[0]);
489         if (fdpair[1] != -1) close(fdpair[1]);
490         TALLOC_FREE(result);
491
492         return ret;
493 }
494
495 static int get_idle_child(struct vfs_handle_struct *handle,
496                           struct aio_child **pchild)
497 {
498         struct aio_child_list *children;
499         struct aio_child *child;
500
501         children = init_aio_children(handle);
502         if (children == NULL) {
503                 return ENOMEM;
504         }
505
506         for (child = children->children; child != NULL; child = child->next) {
507                 if (!child->busy) {
508                         break;
509                 }
510         }
511
512         if (child == NULL) {
513                 int ret;
514
515                 DEBUG(10, ("no idle child found, creating new one\n"));
516
517                 ret = create_aio_child(handle->conn->sconn, children,
518                                           128*1024, &child);
519                 if (ret != 0) {
520                         DEBUG(10, ("create_aio_child failed: %s\n",
521                                    strerror(errno)));
522                         return ret;
523                 }
524         }
525
526         child->dont_delete = true;
527         child->busy = true;
528
529         *pchild = child;
530         return 0;
531 }
532
533 struct aio_fork_pread_state {
534         struct aio_child *child;
535         ssize_t ret;
536         int err;
537 };
538
539 static void aio_fork_pread_done(struct tevent_req *subreq);
540
541 static struct tevent_req *aio_fork_pread_send(struct vfs_handle_struct *handle,
542                                               TALLOC_CTX *mem_ctx,
543                                               struct tevent_context *ev,
544                                               struct files_struct *fsp,
545                                               void *data,
546                                               size_t n, off_t offset)
547 {
548         struct tevent_req *req, *subreq;
549         struct aio_fork_pread_state *state;
550         struct rw_cmd cmd;
551         ssize_t written;
552         int err;
553
554         req = tevent_req_create(mem_ctx, &state, struct aio_fork_pread_state);
555         if (req == NULL) {
556                 return NULL;
557         }
558
559         if (n > 128*1024) {
560                 /* TODO: support variable buffers */
561                 tevent_req_error(req, EINVAL);
562                 return tevent_req_post(req, ev);
563         }
564
565         err = get_idle_child(handle, &state->child);
566         if (err != 0) {
567                 tevent_req_error(req, err);
568                 return tevent_req_post(req, ev);
569         }
570
571         ZERO_STRUCT(cmd);
572         cmd.n = n;
573         cmd.offset = offset;
574         cmd.read_cmd = true;
575
576         DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
577                    (int)state->child->pid));
578
579         /*
580          * Not making this async. We're writing into an empty unix
581          * domain socket. This should never block.
582          */
583         written = write_fd(state->child->sockfd, &cmd, sizeof(cmd),
584                            fsp->fh->fd);
585         if (written == -1) {
586                 err = errno;
587
588                 TALLOC_FREE(state->child);
589
590                 DEBUG(10, ("write_fd failed: %s\n", strerror(err)));
591                 tevent_req_error(req, err);
592                 return tevent_req_post(req, ev);
593         }
594
595         subreq = read_packet_send(state, ev, state->child->sockfd,
596                                   sizeof(struct rw_ret), NULL, NULL);
597         if (tevent_req_nomem(subreq, req)) {
598                 TALLOC_FREE(state->child); /* we sent sth down */
599                 return tevent_req_post(req, ev);
600         }
601         tevent_req_set_callback(subreq, aio_fork_pread_done, req);
602         return req;
603 }
604
605 static void aio_fork_pread_done(struct tevent_req *subreq)
606 {
607         struct tevent_req *req = tevent_req_callback_data(
608                 subreq, struct tevent_req);
609         struct aio_fork_pread_state *state = tevent_req_data(
610                 req, struct aio_fork_pread_state);
611         ssize_t nread;
612         uint8_t *buf;
613         int err;
614         struct rw_ret *retbuf;
615
616         nread = read_packet_recv(subreq, talloc_tos(), &buf, &err);
617         TALLOC_FREE(subreq);
618         if (nread == -1) {
619                 TALLOC_FREE(state->child);
620                 tevent_req_error(req, err);
621                 return;
622         }
623
624         state->child->busy = false;
625
626         retbuf = (struct rw_ret *)buf;
627         state->ret = retbuf->size;
628         state->err = retbuf->ret_errno;
629         tevent_req_done(req);
630 }
631
632 static ssize_t aio_fork_pread_recv(struct tevent_req *req, int *err)
633 {
634         struct aio_fork_pread_state *state = tevent_req_data(
635                 req, struct aio_fork_pread_state);
636
637         if (tevent_req_is_unix_error(req, err)) {
638                 return -1;
639         }
640         if (state->ret == -1) {
641                 *err = state->err;
642         }
643         return state->ret;
644 }
645
646 struct aio_fork_pwrite_state {
647         struct aio_child *child;
648         ssize_t ret;
649         int err;
650 };
651
652 static void aio_fork_pwrite_done(struct tevent_req *subreq);
653
654 static struct tevent_req *aio_fork_pwrite_send(
655         struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
656         struct tevent_context *ev, struct files_struct *fsp,
657         const void *data, size_t n, off_t offset)
658 {
659         struct tevent_req *req, *subreq;
660         struct aio_fork_pwrite_state *state;
661         struct rw_cmd cmd;
662         ssize_t written;
663         int err;
664
665         req = tevent_req_create(mem_ctx, &state, struct aio_fork_pwrite_state);
666         if (req == NULL) {
667                 return NULL;
668         }
669
670         if (n > 128*1024) {
671                 /* TODO: support variable buffers */
672                 tevent_req_error(req, EINVAL);
673                 return tevent_req_post(req, ev);
674         }
675
676         err = get_idle_child(handle, &state->child);
677         if (err != 0) {
678                 tevent_req_error(req, err);
679                 return tevent_req_post(req, ev);
680         }
681
682         ZERO_STRUCT(cmd);
683         cmd.n = n;
684         cmd.offset = offset;
685         cmd.read_cmd = false;
686
687         DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
688                    (int)state->child->pid));
689
690         /*
691          * Not making this async. We're writing into an empty unix
692          * domain socket. This should never block.
693          */
694         written = write_fd(state->child->sockfd, &cmd, sizeof(cmd),
695                            fsp->fh->fd);
696         if (written == -1) {
697                 err = errno;
698
699                 TALLOC_FREE(state->child);
700
701                 DEBUG(10, ("write_fd failed: %s\n", strerror(err)));
702                 tevent_req_error(req, err);
703                 return tevent_req_post(req, ev);
704         }
705
706         subreq = read_packet_send(state, ev, state->child->sockfd,
707                                   sizeof(struct rw_ret), NULL, NULL);
708         if (tevent_req_nomem(subreq, req)) {
709                 TALLOC_FREE(state->child); /* we sent sth down */
710                 return tevent_req_post(req, ev);
711         }
712         tevent_req_set_callback(subreq, aio_fork_pwrite_done, req);
713         return req;
714 }
715
716 static void aio_fork_pwrite_done(struct tevent_req *subreq)
717 {
718         struct tevent_req *req = tevent_req_callback_data(
719                 subreq, struct tevent_req);
720         struct aio_fork_pwrite_state *state = tevent_req_data(
721                 req, struct aio_fork_pwrite_state);
722         ssize_t nread;
723         uint8_t *buf;
724         int err;
725         struct rw_ret *retbuf;
726
727         nread = read_packet_recv(subreq, talloc_tos(), &buf, &err);
728         TALLOC_FREE(subreq);
729         if (nread == -1) {
730                 TALLOC_FREE(state->child);
731                 tevent_req_error(req, err);
732                 return;
733         }
734
735         state->child->busy = false;
736
737         retbuf = (struct rw_ret *)buf;
738         state->ret = retbuf->size;
739         state->err = retbuf->ret_errno;
740         tevent_req_done(req);
741 }
742
743 static ssize_t aio_fork_pwrite_recv(struct tevent_req *req, int *err)
744 {
745         struct aio_fork_pwrite_state *state = tevent_req_data(
746                 req, struct aio_fork_pwrite_state);
747
748         if (tevent_req_is_unix_error(req, err)) {
749                 return -1;
750         }
751         if (state->ret == -1) {
752                 *err = state->err;
753         }
754         return state->ret;
755 }
756
757 static int aio_fork_connect(vfs_handle_struct *handle, const char *service,
758                             const char *user)
759 {
760         /*********************************************************************
761          * How many threads to initialize ?
762          * 100 per process seems insane as a default until you realize that
763          * (a) Threads terminate after 1 second when idle.
764          * (b) Throttling is done in SMB2 via the crediting algorithm.
765          * (c) SMB1 clients are limited to max_mux (50) outstanding
766          *     requests and Windows clients don't use this anyway.
767          * Essentially we want this to be unlimited unless smb.conf
768          * says different.
769          *********************************************************************/
770         aio_pending_size = 100;
771         return SMB_VFS_NEXT_CONNECT(handle, service, user);
772 }
773
774 static struct vfs_fn_pointers vfs_aio_fork_fns = {
775         .connect_fn = aio_fork_connect,
776         .pread_send_fn = aio_fork_pread_send,
777         .pread_recv_fn = aio_fork_pread_recv,
778         .pwrite_send_fn = aio_fork_pwrite_send,
779         .pwrite_recv_fn = aio_fork_pwrite_recv,
780 };
781
782 NTSTATUS vfs_aio_fork_init(void);
783 NTSTATUS vfs_aio_fork_init(void)
784 {
785         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
786                                 "aio_fork", &vfs_aio_fork_fns);
787 }