LCOV - code coverage report
Current view: top level - source3/modules - vfs_commit.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 2 115 1.7 %
Date: 2024-04-21 15:09:00 Functions: 1 12 8.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) James Peach 2006, 2007
       3             :  * Copyright (c) David Losada Carballo 2007
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify
       6             :  * it under the terms of the GNU General Public License as published by
       7             :  * the Free Software Foundation; either version 3 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include "includes.h"
      20             : #include "system/filesys.h"
      21             : #include "smbd/smbd.h"
      22             : #include "lib/util/tevent_unix.h"
      23             : 
      24             : /* Commit data module.
      25             :  *
      26             :  * The purpose of this module is to flush data to disk at regular intervals,
      27             :  * just like the NFS commit operation. There's two rationales for this. First,
      28             :  * it minimises the data loss in case of a power outage without incurring
      29             :  * the poor performance of synchronous I/O. Second, a steady flush rate
      30             :  * can produce better throughput than suddenly dumping massive amounts of
      31             :  * writes onto a disk.
      32             :  *
      33             :  * Tunables:
      34             :  *
      35             :  *  commit: dthresh         Amount of dirty data that can accumulate
      36             :  *                          before we commit (sync) it.
      37             :  *
      38             :  *  commit: debug           Debug level at which to emit messages.
      39             :  *
      40             :  *  commit: eof mode        String. Tunes how the module tries to guess when
      41             :  *                          the client has written the last bytes of the file.
      42             :  *                          Possible values (default = hinted):
      43             :  *
      44             :  *     (*)  = hinted        Some clients (i.e. Windows Explorer) declare the
      45             :  *                          size of the file before transferring it. With this
      46             :  *                          option, we remember that hint, and commit after
      47             :  *                          writing in that file position. If the client
      48             :  *                          doesn't declare the size of file, committing on EOF
      49             :  *                          is not triggered.
      50             :  *
      51             :  *          = growth        Commits after a write operation has made the file
      52             :  *                          size grow. If the client declares a file size, it
      53             :  *                          refrains to commit until the file has reached it.
      54             :  *                          Useful for defeating writeback on NFS shares.
      55             :  *
      56             :  */
      57             : 
      58             : #define MODULE "commit"
      59             : 
      60             : static int module_debug;
      61             : 
      62             : enum eof_mode
      63             : {
      64             :     EOF_NONE = 0x0000,
      65             :     EOF_HINTED = 0x0001,
      66             :     EOF_GROWTH = 0x0002
      67             : };
      68             : 
      69             : struct commit_info
      70             : {
      71             :         /* For chunk-based commits */
      72             :         off_t dbytes;   /* Dirty (uncommitted) bytes */
      73             :         off_t dthresh;  /* Dirty data threshold */
      74             :         /* For commits on EOF */
      75             :         enum eof_mode on_eof;
      76             :         off_t eof;              /* Expected file size */
      77             : };
      78             : 
      79           0 : static int commit_do(
      80             :         struct commit_info *            c,
      81             :         int                             fd)
      82             : {
      83             :         int result;
      84             : 
      85           0 :         DEBUG(module_debug,
      86             :                 ("%s: flushing %lu dirty bytes\n",
      87             :                  MODULE, (unsigned long)c->dbytes));
      88             : 
      89             : #if defined(HAVE_FDATASYNC)
      90           0 :         result = fdatasync(fd);
      91             : #elif defined(HAVE_FSYNC)
      92             :         result = fsync(fd);
      93             : #else
      94             :         DEBUG(0, ("%s: WARNING: no commit support on this platform\n",
      95             :                 MODULE));
      96             :         result = 0
      97             : #endif
      98           0 :         if (result == 0) {
      99           0 :                 c->dbytes = 0;       /* on success, no dirty bytes */
     100             :         }
     101           0 :         return result;
     102             : }
     103             : 
     104           0 : static int commit_all(
     105             :         struct vfs_handle_struct *      handle,
     106             :         files_struct *                  fsp)
     107             : {
     108             :         struct commit_info *c;
     109             : 
     110           0 :         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
     111           0 :                 if (c->dbytes) {
     112           0 :                         DEBUG(module_debug,
     113             :                                 ("%s: flushing %lu dirty bytes\n",
     114             :                                  MODULE, (unsigned long)c->dbytes));
     115             : 
     116           0 :                         return commit_do(c, fsp_get_io_fd(fsp));
     117             :                 }
     118             :         }
     119           0 :         return 0;
     120             : }
     121             : 
     122           0 : static int commit(
     123             :         struct vfs_handle_struct *      handle,
     124             :         files_struct *                  fsp,
     125             :         off_t                   offset,
     126             :         ssize_t                         last_write)
     127             : {
     128             :         struct commit_info *c;
     129             : 
     130           0 :         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))
     131             :             == NULL) {
     132           0 :                 return 0;
     133             :         }
     134             : 
     135           0 :         c->dbytes += last_write;     /* dirty bytes always counted */
     136             : 
     137           0 :         if (c->dthresh && (c->dbytes > c->dthresh)) {
     138           0 :                 return commit_do(c, fsp_get_io_fd(fsp));
     139             :         }
     140             : 
     141             :         /* Return if we are not in EOF mode or if we have temporarily opted
     142             :          * out of it.
     143             :          */
     144           0 :         if (c->on_eof == EOF_NONE || c->eof < 0) {
     145           0 :                 return 0;
     146             :         }
     147             : 
     148             :         /* This write hit or went past our cache the file size. */
     149           0 :         if ((offset + last_write) >= c->eof) {
     150           0 :                 if (commit_do(c, fsp_get_io_fd(fsp)) == -1) {
     151           0 :                         return -1;
     152             :                 }
     153             : 
     154             :                 /* Hinted mode only commits the first time we hit EOF. */
     155           0 :                 if (c->on_eof == EOF_HINTED) {
     156           0 :                     c->eof = -1;
     157           0 :                 } else if (c->on_eof == EOF_GROWTH) {
     158           0 :                     c->eof = offset + last_write;
     159             :                 }
     160             :         }
     161             : 
     162           0 :         return 0;
     163             : }
     164             : 
     165           0 : static int commit_connect(
     166             :         struct vfs_handle_struct *  handle,
     167             :         const char *                service,
     168             :         const char *                user)
     169             : {
     170           0 :         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
     171             : 
     172           0 :         if (ret < 0) {
     173           0 :                 return ret;
     174             :         }
     175             : 
     176           0 :         module_debug = lp_parm_int(SNUM(handle->conn), MODULE, "debug", 100);
     177           0 :         return 0;
     178             : }
     179             : 
     180           0 : static int commit_openat(struct vfs_handle_struct *handle,
     181             :                          const struct files_struct *dirfsp,
     182             :                          const struct smb_filename *smb_fname,
     183             :                          files_struct *fsp,
     184             :                          const struct vfs_open_how *how)
     185             : {
     186             :         off_t dthresh;
     187             :         const char *eof_mode;
     188           0 :         struct commit_info *c = NULL;
     189             :         int fd;
     190             : 
     191             :         /* Don't bother with read-only files. */
     192           0 :         if ((how->flags & O_ACCMODE) == O_RDONLY) {
     193           0 :                 return SMB_VFS_NEXT_OPENAT(handle,
     194             :                                            dirfsp,
     195             :                                            smb_fname,
     196             :                                            fsp,
     197             :                                            how);
     198             :         }
     199             : 
     200             :         /* Read and check module configuration */
     201           0 :         dthresh = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
     202             :                                         MODULE, "dthresh", NULL));
     203             : 
     204           0 :         eof_mode = lp_parm_const_string(SNUM(handle->conn),
     205             :                                         MODULE, "eof mode", "none");
     206             : 
     207           0 :         if (dthresh > 0 || !strequal(eof_mode, "none")) {
     208           0 :                 c = VFS_ADD_FSP_EXTENSION(
     209             :                         handle, fsp, struct commit_info, NULL);
     210             :                 /* Process main tunables */
     211           0 :                 if (c) {
     212           0 :                         c->dthresh = dthresh;
     213           0 :                         c->dbytes = 0;
     214           0 :                         c->on_eof = EOF_NONE;
     215           0 :                         c->eof = 0;
     216             :                 }
     217             :         }
     218             :         /* Process eof_mode tunable */
     219           0 :         if (c) {
     220           0 :                 if (strequal(eof_mode, "hinted")) {
     221           0 :                         c->on_eof = EOF_HINTED;
     222           0 :                 } else if (strequal(eof_mode, "growth")) {
     223           0 :                         c->on_eof = EOF_GROWTH;
     224             :                 }
     225             :         }
     226             : 
     227           0 :         fd = SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
     228           0 :         if (fd == -1) {
     229           0 :                 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
     230           0 :                 return fd;
     231             :         }
     232             : 
     233             :         /* EOF commit modes require us to know the initial file size. */
     234           0 :         if (c && (c->on_eof != EOF_NONE)) {
     235             :                 SMB_STRUCT_STAT st;
     236             :                 /*
     237             :                  * Setting the fd of the FSP is a hack
     238             :                  * but also practiced elsewhere -
     239             :                  * needed for calling the VFS.
     240             :                  */
     241           0 :                 fsp_set_fd(fsp, fd);
     242           0 :                 if (SMB_VFS_FSTAT(fsp, &st) == -1) {
     243           0 :                         int saved_errno = errno;
     244           0 :                         SMB_VFS_CLOSE(fsp);
     245           0 :                         fsp_set_fd(fsp, -1);
     246           0 :                         errno = saved_errno;
     247           0 :                         return -1;
     248             :                 }
     249           0 :                 c->eof = st.st_ex_size;
     250             :         }
     251             : 
     252           0 :         return fd;
     253             : }
     254             : 
     255           0 : static ssize_t commit_pwrite(
     256             :         vfs_handle_struct * handle,
     257             :         files_struct *      fsp,
     258             :         const void *        data,
     259             :         size_t              count,
     260             :         off_t       offset)
     261             : {
     262             :         ssize_t ret;
     263             : 
     264           0 :         ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, count, offset);
     265           0 :         if (ret > 0) {
     266           0 :                 if (commit(handle, fsp, offset, ret) == -1) {
     267           0 :                         return -1;
     268             :                 }
     269             :         }
     270             : 
     271           0 :         return ret;
     272             : }
     273             : 
     274             : struct commit_pwrite_state {
     275             :         struct vfs_handle_struct *handle;
     276             :         struct files_struct *fsp;
     277             :         ssize_t ret;
     278             :         struct vfs_aio_state vfs_aio_state;
     279             : };
     280             : 
     281             : static void commit_pwrite_written(struct tevent_req *subreq);
     282             : 
     283           0 : static struct tevent_req *commit_pwrite_send(struct vfs_handle_struct *handle,
     284             :                                              TALLOC_CTX *mem_ctx,
     285             :                                              struct tevent_context *ev,
     286             :                                              struct files_struct *fsp,
     287             :                                              const void *data,
     288             :                                              size_t n, off_t offset)
     289             : {
     290             :         struct tevent_req *req, *subreq;
     291             :         struct commit_pwrite_state *state;
     292             : 
     293           0 :         req = tevent_req_create(mem_ctx, &state, struct commit_pwrite_state);
     294           0 :         if (req == NULL) {
     295           0 :                 return NULL;
     296             :         }
     297           0 :         state->handle = handle;
     298           0 :         state->fsp = fsp;
     299             : 
     300           0 :         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
     301             :                                           n, offset);
     302           0 :         if (tevent_req_nomem(subreq, req)) {
     303           0 :                 return tevent_req_post(req, ev);
     304             :         }
     305           0 :         tevent_req_set_callback(subreq, commit_pwrite_written, req);
     306           0 :         return req;
     307             : }
     308             : 
     309           0 : static void commit_pwrite_written(struct tevent_req *subreq)
     310             : {
     311           0 :         struct tevent_req *req = tevent_req_callback_data(
     312             :                 subreq, struct tevent_req);
     313           0 :         struct commit_pwrite_state *state = tevent_req_data(
     314             :                 req, struct commit_pwrite_state);
     315             :         int commit_ret;
     316             : 
     317           0 :         state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
     318           0 :         TALLOC_FREE(subreq);
     319             : 
     320           0 :         if (state->ret <= 0) {
     321           0 :                 tevent_req_done(req);
     322           0 :                 return;
     323             :         }
     324             : 
     325             :         /*
     326             :          * Ok, this is a sync fake. We should make the sync async as well, but
     327             :          * I'm too lazy for that right now -- vl
     328             :          */
     329           0 :         commit_ret = commit(state->handle,
     330           0 :                             state->fsp,
     331           0 :                             fh_get_pos(state->fsp->fh),
     332             :                             state->ret);
     333             : 
     334           0 :         if (commit_ret == -1) {
     335           0 :                 state->ret = -1;
     336             :         }
     337             : 
     338           0 :         tevent_req_done(req);
     339             : }
     340             : 
     341           0 : static ssize_t commit_pwrite_recv(struct tevent_req *req,
     342             :                                   struct vfs_aio_state *vfs_aio_state)
     343             : {
     344             :         struct commit_pwrite_state *state =
     345           0 :                 tevent_req_data(req, struct commit_pwrite_state);
     346             : 
     347           0 :         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
     348           0 :                 return -1;
     349             :         }
     350           0 :         *vfs_aio_state = state->vfs_aio_state;
     351           0 :         return state->ret;
     352             : }
     353             : 
     354           0 : static int commit_close(
     355             :         vfs_handle_struct * handle,
     356             :         files_struct *      fsp)
     357             : {
     358             :         /* Commit errors not checked, close() will find them again */
     359           0 :         commit_all(handle, fsp);
     360           0 :         return SMB_VFS_NEXT_CLOSE(handle, fsp);
     361             : }
     362             : 
     363           0 : static int commit_ftruncate(
     364             :         vfs_handle_struct * handle,
     365             :         files_struct *      fsp,
     366             :         off_t           len)
     367             : {
     368             :         int result;
     369             : 
     370           0 :         result = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
     371           0 :         if (result == 0) {
     372             :                 struct commit_info *c;
     373           0 :                 if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(
     374             :                              handle, fsp))) {
     375           0 :                         commit(handle, fsp, len, 0);
     376           0 :                         c->eof = len;
     377             :                 }
     378             :         }
     379             : 
     380           0 :         return result;
     381             : }
     382             : 
     383             : static struct vfs_fn_pointers vfs_commit_fns = {
     384             :         .openat_fn = commit_openat,
     385             :         .close_fn = commit_close,
     386             :         .pwrite_fn = commit_pwrite,
     387             :         .pwrite_send_fn = commit_pwrite_send,
     388             :         .pwrite_recv_fn = commit_pwrite_recv,
     389             :         .connect_fn = commit_connect,
     390             :         .ftruncate_fn = commit_ftruncate
     391             : };
     392             : 
     393             : static_decl_vfs;
     394          27 : NTSTATUS vfs_commit_init(TALLOC_CTX *ctx)
     395             : {
     396          27 :         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE,
     397             :                                 &vfs_commit_fns);
     398             : }
     399             : 
     400             : 

Generated by: LCOV version 1.14