RIP BOOL. Convert BOOL -> bool. I found a few interesting
[nivanova/samba-autobuild/.git] / source3 / smbd / aio.c
index f747cf5c065c3a586c137e0ebe715ffed2d74705..994b10d6a813bb3468c73c032a0e040b7eaa114c 100644 (file)
@@ -6,7 +6,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -15,8 +15,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -36,7 +35,7 @@ struct aio_extra {
        struct aio_extra *next, *prev;
        SMB_STRUCT_AIOCB acb;
        files_struct *fsp;
-       BOOL read_req;
+       bool read_req;
        uint16 mid;
        char *inbuf;
        char *outbuf;
@@ -49,10 +48,8 @@ static struct aio_extra *aio_list_head;
  of the aio_read call.
 *****************************************************************************/
 
-static struct aio_extra *create_aio_ex_read(files_struct *fsp,
-                                               size_t buflen,
-                                               uint16 mid,
-                                               const char *inbuf)
+static struct aio_extra *create_aio_ex_read(files_struct *fsp, size_t buflen,
+                                           uint16 mid)
 {
        struct aio_extra *aio_ex = SMB_MALLOC_P(struct aio_extra);
 
@@ -68,14 +65,6 @@ static struct aio_extra *create_aio_ex_read(files_struct *fsp,
                SAFE_FREE(aio_ex);
                return NULL;
        }
-       /* Save the first 8 bytes of inbuf for possible enc data. */
-       aio_ex->inbuf = SMB_MALLOC_ARRAY(char, 8);
-       if (!aio_ex->inbuf) {
-               SAFE_FREE(aio_ex->outbuf);
-               SAFE_FREE(aio_ex);
-               return NULL;
-       }
-       memcpy(aio_ex->inbuf, inbuf, 8);
        DLIST_ADD(aio_list_head, aio_ex);
        aio_ex->fsp = fsp;
        aio_ex->read_req = True;
@@ -174,7 +163,7 @@ static void signal_handler(int sig, siginfo_t *info, void *unused)
  Is there a signal waiting ?
 *****************************************************************************/
 
-BOOL aio_finished(void)
+bool aio_finished(void)
 {
        return (signals_received != 0);
 }
@@ -203,9 +192,8 @@ void initialize_async_io_handler(void)
  Set up an aio request from a SMBreadX call.
 *****************************************************************************/
 
-BOOL schedule_aio_read_and_X(connection_struct *conn,
-                            char *inbuf, char *outbuf,
-                            int length, int len_outbuf,
+bool schedule_aio_read_and_X(connection_struct *conn,
+                            struct smb_request *req,
                             files_struct *fsp, SMB_OFF_T startpos,
                             size_t smb_maxcnt)
 {
@@ -225,7 +213,7 @@ BOOL schedule_aio_read_and_X(connection_struct *conn,
 
        /* Only do this on non-chained and non-chaining reads not using the
         * write cache. */
-        if (chain_size !=0 || (CVAL(inbuf,smb_vwv0) != 0xFF)
+        if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
            || (lp_write_cache_size(SNUM(conn)) != 0) ) {
                return False;
        }
@@ -237,18 +225,18 @@ BOOL schedule_aio_read_and_X(connection_struct *conn,
                return False;
        }
 
-       /* The following is safe from integer wrap as we've already
-          checked smb_maxcnt is 128k or less. */
-       bufsize = PTR_DIFF(smb_buf(outbuf),outbuf) + smb_maxcnt;
+       /* The following is safe from integer wrap as we've already checked
+          smb_maxcnt is 128k or less. Wct is 12 for read replies */
+
+       bufsize = smb_size + 12 * 2 + smb_maxcnt;
 
-       if ((aio_ex = create_aio_ex_read(fsp, bufsize,
-                                        SVAL(inbuf,smb_mid), inbuf)) == NULL) {
+       if ((aio_ex = create_aio_ex_read(fsp, bufsize, req->mid)) == NULL) {
                DEBUG(10,("schedule_aio_read_and_X: malloc fail.\n"));
                return False;
        }
 
-       /* Copy the SMB header already setup in outbuf. */
-       memcpy(aio_ex->outbuf, outbuf, smb_buf(outbuf) - outbuf);
+       construct_reply_common((char *)req->inbuf, aio_ex->outbuf);
+       set_message(aio_ex->outbuf, 12, 0, True);
        SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
 
        a = &aio_ex->acb;
@@ -284,16 +272,16 @@ BOOL schedule_aio_read_and_X(connection_struct *conn,
  Set up an aio request from a SMBwriteX call.
 *****************************************************************************/
 
-BOOL schedule_aio_write_and_X(connection_struct *conn,
-                               char *inbuf, char *outbuf,
-                               int length, int len_outbuf,
-                               files_struct *fsp, char *data,
-                               SMB_OFF_T startpos,
-                               size_t numtowrite)
+bool schedule_aio_write_and_X(connection_struct *conn,
+                             struct smb_request *req,
+                             files_struct *fsp, char *data,
+                             SMB_OFF_T startpos,
+                             size_t numtowrite)
 {
        struct aio_extra *aio_ex;
        SMB_STRUCT_AIOCB *a;
        size_t inbufsize, outbufsize;
+       bool write_through = BITSETW(req->inbuf+smb_vwv7,0);
        size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
 
        if (!min_aio_write_size || (numtowrite < min_aio_write_size)) {
@@ -307,7 +295,7 @@ BOOL schedule_aio_write_and_X(connection_struct *conn,
 
        /* Only do this on non-chained and non-chaining reads not using the
         * write cache. */
-        if (chain_size !=0 || (CVAL(inbuf,smb_vwv0) != 0xFF)
+        if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
            || (lp_write_cache_size(SNUM(conn)) != 0) ) {
                return False;
        }
@@ -321,23 +309,25 @@ BOOL schedule_aio_write_and_X(connection_struct *conn,
                          "(mid = %u)\n",
                          fsp->fsp_name, (double)startpos,
                          (unsigned int)numtowrite,
-                         (unsigned int)SVAL(inbuf,smb_mid) ));
+                         (unsigned int)req->mid ));
                return False;
        }
 
-       inbufsize =  smb_len(inbuf) + 4;
-       outbufsize = smb_len(outbuf) + 4;
+       inbufsize =  smb_len(req->inbuf) + 4;
+       reply_outbuf(req, 6, 0);
+       outbufsize = smb_len(req->outbuf) + 4;
        if (!(aio_ex = create_aio_ex_write(fsp, inbufsize, outbufsize,
-                                          SVAL(inbuf,smb_mid)))) {
+                                          req->mid))) {
                DEBUG(0,("schedule_aio_write_and_X: malloc fail.\n"));
                return False;
        }
 
        /* Copy the SMB header already setup in outbuf. */
-       memcpy(aio_ex->inbuf, inbuf, inbufsize);
+       memcpy(aio_ex->inbuf, req->inbuf, inbufsize);
 
        /* Copy the SMB header already setup in outbuf. */
-       memcpy(aio_ex->outbuf, outbuf, outbufsize);
+       memcpy(aio_ex->outbuf, req->outbuf, outbufsize);
+       TALLOC_FREE(req->outbuf);
        SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
 
        a = &aio_ex->acb;
@@ -345,7 +335,7 @@ BOOL schedule_aio_write_and_X(connection_struct *conn,
        /* Now set up the aio record for the write call. */
        
        a->aio_fildes = fsp->fh->fd;
-       a->aio_buf = aio_ex->inbuf + (PTR_DIFF(data, inbuf));
+       a->aio_buf = aio_ex->inbuf + (PTR_DIFF(data, req->inbuf));
        a->aio_nbytes = numtowrite;
        a->aio_offset = startpos;
        a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
@@ -359,7 +349,22 @@ BOOL schedule_aio_write_and_X(connection_struct *conn,
                return False;
        }
 
-       srv_defer_sign_response(aio_ex->mid);
+       if (!write_through && !lp_syncalways(SNUM(fsp->conn))
+           && fsp->aio_write_behind) {
+               /* Lie to the client and immediately claim we finished the
+                * write. */
+               SSVAL(aio_ex->outbuf,smb_vwv2,numtowrite);
+                SSVAL(aio_ex->outbuf,smb_vwv4,(numtowrite>>16)&1);
+               show_msg(aio_ex->outbuf);
+               if (!send_smb(smbd_server_fd(),aio_ex->outbuf)) {
+                       exit_server_cleanly("handle_aio_write: send_smb "
+                                           "failed.");
+               }
+               DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write "
+                         "behind for file %s\n", fsp->fsp_name ));
+       } else {
+               srv_defer_sign_response(aio_ex->mid);
+       }
        outstanding_aio_calls++;
 
        DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write for file "
@@ -382,7 +387,6 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
        int ret = 0;
        int outsize;
        char *outbuf = aio_ex->outbuf;
-       char *inbuf = aio_ex->inbuf;
        char *data = smb_buf(outbuf);
        ssize_t nread = SMB_VFS_AIO_RETURN(aio_ex->fsp,&aio_ex->acb);
 
@@ -406,7 +410,7 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
                outsize = (UNIXERROR(ERRDOS,ERRnoaccess));
                ret = errno;
        } else {
-               outsize = set_message(inbuf,outbuf,12,nread,False);
+               outsize = set_message(outbuf,12,nread,False);
                SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be * -1. */
                SSVAL(outbuf,smb_vwv5,nread);
                SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
@@ -416,10 +420,10 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
                DEBUG( 3, ( "handle_aio_read_complete file %s max=%d "
                            "nread=%d\n",
                            aio_ex->fsp->fsp_name,
-                           aio_ex->acb.aio_nbytes, (int)nread ) );
+                           (int)aio_ex->acb.aio_nbytes, (int)nread ) );
 
        }
-       smb_setlen(inbuf,outbuf,outsize - 4);
+       smb_setlen(outbuf,outsize - 4);
        show_msg(outbuf);
        if (!send_smb(smbd_server_fd(),outbuf)) {
                exit_server_cleanly("handle_aio_read_complete: send_smb "
@@ -444,10 +448,34 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
        int ret = 0;
        files_struct *fsp = aio_ex->fsp;
        char *outbuf = aio_ex->outbuf;
-       char *inbuf = aio_ex->inbuf;
        ssize_t numtowrite = aio_ex->acb.aio_nbytes;
        ssize_t nwritten = SMB_VFS_AIO_RETURN(fsp,&aio_ex->acb);
 
+       if (fsp->aio_write_behind) {
+               if (nwritten != numtowrite) {
+                       if (nwritten == -1) {
+                               DEBUG(5,("handle_aio_write_complete: "
+                                        "aio_write_behind failed ! File %s "
+                                        "is corrupt ! Error %s\n",
+                                        fsp->fsp_name, strerror(errno) ));
+                               ret = errno;
+                       } else {
+                               DEBUG(0,("handle_aio_write_complete: "
+                                        "aio_write_behind failed ! File %s "
+                                        "is corrupt ! Wanted %u bytes but "
+                                        "only wrote %d\n", fsp->fsp_name,
+                                        (unsigned int)numtowrite,
+                                        (int)nwritten ));
+                               ret = EIO;
+                       }
+               } else {
+                       DEBUG(10,("handle_aio_write_complete: "
+                                 "aio_write_behind completed for file %s\n",
+                                 fsp->fsp_name ));
+               }
+               return 0;
+       }
+
        /* We don't need outsize or set_message here as we've already set the
           fixed size length when we set up the aio call. */
 
@@ -467,7 +495,7 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
                UNIXERROR(ERRHRD,ERRdiskfull);
                ret = errno;
         } else {
-               BOOL write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0);
+               bool write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0);
                NTSTATUS status;
 
                SSVAL(outbuf,smb_vwv2,nwritten);
@@ -506,7 +534,7 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
  was non-zero), False if not.
 *****************************************************************************/
 
-static BOOL handle_aio_completed(struct aio_extra *aio_ex, int *perr)
+static bool handle_aio_completed(struct aio_extra *aio_ex, int *perr)
 {
        int err;
 
@@ -589,6 +617,115 @@ int process_aio_queue(void)
        return ret;
 }
 
+/****************************************************************************
+ We're doing write behind and the client closed the file. Wait up to 30
+ seconds (my arbitrary choice) for the aio to complete. Return 0 if all writes
+ completed, errno to return if not.
+*****************************************************************************/
+
+#define SMB_TIME_FOR_AIO_COMPLETE_WAIT 29
+
+int wait_for_aio_completion(files_struct *fsp)
+{
+       struct aio_extra *aio_ex;
+       const SMB_STRUCT_AIOCB **aiocb_list;
+       int aio_completion_count = 0;
+       time_t start_time = time(NULL);
+       int seconds_left;
+
+       for (seconds_left = SMB_TIME_FOR_AIO_COMPLETE_WAIT;
+            seconds_left >= 0;) {
+               int err = 0;
+               int i;
+               struct timespec ts;
+
+               aio_completion_count = 0;
+               for( aio_ex = aio_list_head; aio_ex; aio_ex = aio_ex->next) {
+                       if (aio_ex->fsp == fsp) {
+                               aio_completion_count++;
+                       }
+               }
+
+               if (!aio_completion_count) {
+                       return 0;
+               }
+
+               DEBUG(3,("wait_for_aio_completion: waiting for %d aio events "
+                        "to complete.\n", aio_completion_count ));
+
+               aiocb_list = SMB_MALLOC_ARRAY(const SMB_STRUCT_AIOCB *,
+                                             aio_completion_count);
+               if (!aiocb_list) {
+                       return ENOMEM;
+               }
+
+               for( i = 0, aio_ex = aio_list_head;
+                    aio_ex;
+                    aio_ex = aio_ex->next) {
+                       if (aio_ex->fsp == fsp) {
+                               aiocb_list[i++] = &aio_ex->acb;
+                       }
+               }
+
+               /* Now wait up to seconds_left for completion. */
+               ts.tv_sec = seconds_left;
+               ts.tv_nsec = 0;
+
+               DEBUG(10,("wait_for_aio_completion: %d events, doing a wait "
+                         "of %d seconds.\n",
+                         aio_completion_count, seconds_left ));
+
+               err = SMB_VFS_AIO_SUSPEND(fsp, aiocb_list,
+                                         aio_completion_count, &ts);
+
+               DEBUG(10,("wait_for_aio_completion: returned err = %d, "
+                         "errno = %s\n", err, strerror(errno) ));
+               
+               if (err == -1 && errno == EAGAIN) {
+                       DEBUG(0,("wait_for_aio_completion: aio_suspend timed "
+                                "out waiting for %d events after a wait of "
+                                "%d seconds\n", aio_completion_count,
+                                seconds_left));
+                       /* Timeout. */
+                       cancel_aio_by_fsp(fsp);
+                       SAFE_FREE(aiocb_list);
+                       return EIO;
+               }
+
+               /* One or more events might have completed - process them if
+                * so. */
+               for( i = 0; i < aio_completion_count; i++) {
+                       uint16 mid = aiocb_list[i]->aio_sigevent.sigev_value.sival_int;
+
+                       aio_ex = find_aio_ex(mid);
+
+                       if (!aio_ex) {
+                               DEBUG(0, ("wait_for_aio_completion: mid %u "
+                                         "doesn't match an aio record\n",
+                                         (unsigned int)mid ));
+                               continue;
+                       }
+
+                       if (!handle_aio_completed(aio_ex, &err)) {
+                               continue;
+                       }
+                       delete_aio_ex(aio_ex);
+               }
+
+               SAFE_FREE(aiocb_list);
+               seconds_left = SMB_TIME_FOR_AIO_COMPLETE_WAIT
+                       - (time(NULL) - start_time);
+       }
+
+       /* We timed out - we don't know why. Return ret if already an error,
+        * else EIO. */
+       DEBUG(10,("wait_for_aio_completion: aio_suspend timed out waiting "
+                 "for %d events\n",
+                 aio_completion_count));
+
+       return EIO;
+}
+
 /****************************************************************************
  Cancel any outstanding aio requests. The client doesn't care about the reply.
 *****************************************************************************/
@@ -610,7 +747,7 @@ void cancel_aio_by_fsp(files_struct *fsp)
 }
 
 #else
-BOOL aio_finished(void)
+bool aio_finished(void)
 {
        return False;
 }
@@ -624,21 +761,19 @@ int process_aio_queue(void)
        return False;
 }
 
-BOOL schedule_aio_read_and_X(connection_struct *conn,
-                            char *inbuf, char *outbuf,
-                            int length, int len_outbuf,
+bool schedule_aio_read_and_X(connection_struct *conn,
+                            struct smb_request *req,
                             files_struct *fsp, SMB_OFF_T startpos,
                             size_t smb_maxcnt)
 {
        return False;
 }
 
-BOOL schedule_aio_write_and_X(connection_struct *conn,
-                                char *inbuf, char *outbuf,
-                                int length, int len_outbuf,
-                                files_struct *fsp, char *data,
-                                SMB_OFF_T startpos,
-                                size_t numtowrite)
+bool schedule_aio_write_and_X(connection_struct *conn,
+                             struct smb_request *req,
+                             files_struct *fsp, char *data,
+                             SMB_OFF_T startpos,
+                             size_t numtowrite)
 {
        return False;
 }
@@ -647,4 +782,8 @@ void cancel_aio_by_fsp(files_struct *fsp)
 {
 }
 
+int wait_for_aio_completion(files_struct *fsp)
+{
+       return ENOSYS;
+}
 #endif