340276a79004f43359efe14abb5561867ba1b95e
[gd/samba-autobuild/.git] / source3 / modules / vfs_commit.c
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, commiting 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 static int commit_do(
80         struct commit_info *            c,
81         int                             fd)
82 {
83         int result;
84
85         DEBUG(module_debug,
86                 ("%s: flushing %lu dirty bytes\n",
87                  MODULE, (unsigned long)c->dbytes));
88
89 #if HAVE_FDATASYNC
90         result = fdatasync(fd);
91 #elif 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         if (result == 0) {
99                 c->dbytes = 0;  /* on success, no dirty bytes */
100         }
101         return result;
102 }
103
104 static int commit_all(
105         struct vfs_handle_struct *      handle,
106         files_struct *                  fsp)
107 {
108         struct commit_info *c;
109
110         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
111                 if (c->dbytes) {
112                         DEBUG(module_debug,
113                                 ("%s: flushing %lu dirty bytes\n",
114                                  MODULE, (unsigned long)c->dbytes));
115
116                         return commit_do(c, fsp->fh->fd);
117                 }
118         }
119         return 0;
120 }
121
122 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         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))
131             == NULL) {
132                 return 0;
133         }
134
135         c->dbytes += last_write;        /* dirty bytes always counted */
136
137         if (c->dthresh && (c->dbytes > c->dthresh)) {
138                 return commit_do(c, fsp->fh->fd);
139         }
140
141         /* Return if we are not in EOF mode or if we have temporarily opted
142          * out of it.
143          */
144         if (c->on_eof == EOF_NONE || c->eof < 0) {
145                 return 0;
146         }
147
148         /* This write hit or went past our cache the file size. */
149         if ((offset + last_write) >= c->eof) {
150                 if (commit_do(c, fsp->fh->fd) == -1) {
151                         return -1;
152                 }
153
154                 /* Hinted mode only commits the first time we hit EOF. */
155                 if (c->on_eof == EOF_HINTED) {
156                     c->eof = -1;
157                 } else if (c->on_eof == EOF_GROWTH) {
158                     c->eof = offset + last_write;
159                 }
160         }
161
162         return 0;
163 }
164
165 static int commit_connect(
166         struct vfs_handle_struct *  handle,
167         const char *                service,
168         const char *                user)
169 {
170         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
171
172         if (ret < 0) {
173                 return ret;
174         }
175
176         module_debug = lp_parm_int(SNUM(handle->conn), MODULE, "debug", 100);
177         return 0;
178 }
179
180 static int commit_open(
181         vfs_handle_struct * handle,
182         struct smb_filename *smb_fname,
183         files_struct *      fsp,
184         int                 flags,
185         mode_t              mode)
186 {
187         off_t dthresh;
188         const char *eof_mode;
189         struct commit_info *c = NULL;
190         int fd;
191
192         /* Don't bother with read-only files. */
193         if ((flags & O_ACCMODE) == O_RDONLY) {
194                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
195         }
196
197         /* Read and check module configuration */
198         dthresh = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
199                                         MODULE, "dthresh", NULL));
200
201         eof_mode = lp_parm_const_string(SNUM(handle->conn),
202                                         MODULE, "eof mode", "none");
203
204         if (dthresh > 0 || !strequal(eof_mode, "none")) {
205                 c = (struct commit_info *)VFS_ADD_FSP_EXTENSION(
206                         handle, fsp, struct commit_info, NULL);
207                 /* Process main tunables */
208                 if (c) {
209                         c->dthresh = dthresh;
210                         c->dbytes = 0;
211                         c->on_eof = EOF_NONE;
212                         c->eof = 0;
213                 }
214         }
215         /* Process eof_mode tunable */
216         if (c) {
217                 if (strequal(eof_mode, "hinted")) {
218                         c->on_eof = EOF_HINTED;
219                 } else if (strequal(eof_mode, "growth")) {
220                         c->on_eof = EOF_GROWTH;
221                 }
222         }
223
224         fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
225         if (fd == -1) {
226                 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
227                 return fd;
228         }
229
230         /* EOF commit modes require us to know the initial file size. */
231         if (c && (c->on_eof != EOF_NONE)) {
232                 SMB_STRUCT_STAT st;
233                 /*
234                  * Setting the fd of the FSP is a hack
235                  * but also practiced elsewhere -
236                  * needed for calling the VFS.
237                  */
238                 fsp->fh->fd = fd;
239                 if (SMB_VFS_FSTAT(fsp, &st) == -1) {
240                         int saved_errno = errno;
241                         SMB_VFS_CLOSE(fsp);
242                         errno = saved_errno;
243                         return -1;
244                 }
245                 c->eof = st.st_ex_size;
246         }
247
248         return fd;
249 }
250
251 static ssize_t commit_write(
252         vfs_handle_struct * handle,
253         files_struct *      fsp,
254         const void *        data,
255         size_t              count)
256 {
257         ssize_t ret;
258         ret = SMB_VFS_NEXT_WRITE(handle, fsp, data, count);
259
260         if (ret > 0) {
261                 if (commit(handle, fsp, fsp->fh->pos, ret) == -1) {
262                         return -1;
263                 }
264         }
265
266         return ret;
267 }
268
269 static ssize_t commit_pwrite(
270         vfs_handle_struct * handle,
271         files_struct *      fsp,
272         const void *        data,
273         size_t              count,
274         off_t       offset)
275 {
276         ssize_t ret;
277
278         ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, count, offset);
279         if (ret > 0) {
280                 if (commit(handle, fsp, offset, ret) == -1) {
281                         return -1;
282                 }
283         }
284
285         return ret;
286 }
287
288 struct commit_pwrite_state {
289         struct vfs_handle_struct *handle;
290         struct files_struct *fsp;
291         ssize_t ret;
292         struct vfs_aio_state vfs_aio_state;
293 };
294
295 static void commit_pwrite_written(struct tevent_req *subreq);
296
297 static struct tevent_req *commit_pwrite_send(struct vfs_handle_struct *handle,
298                                              TALLOC_CTX *mem_ctx,
299                                              struct tevent_context *ev,
300                                              struct files_struct *fsp,
301                                              const void *data,
302                                              size_t n, off_t offset)
303 {
304         struct tevent_req *req, *subreq;
305         struct commit_pwrite_state *state;
306
307         req = tevent_req_create(mem_ctx, &state, struct commit_pwrite_state);
308         if (req == NULL) {
309                 return NULL;
310         }
311         state->handle = handle;
312         state->fsp = fsp;
313
314         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
315                                           n, offset);
316         if (tevent_req_nomem(subreq, req)) {
317                 return tevent_req_post(req, ev);
318         }
319         tevent_req_set_callback(subreq, commit_pwrite_written, req);
320         return req;
321 }
322
323 static void commit_pwrite_written(struct tevent_req *subreq)
324 {
325         struct tevent_req *req = tevent_req_callback_data(
326                 subreq, struct tevent_req);
327         struct commit_pwrite_state *state = tevent_req_data(
328                 req, struct commit_pwrite_state);
329         int commit_ret;
330
331         state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
332         TALLOC_FREE(subreq);
333
334         if (state->ret <= 0) {
335                 tevent_req_done(req);
336                 return;
337         }
338
339         /*
340          * Ok, this is a sync fake. We should make the sync async as well, but
341          * I'm too lazy for that right now -- vl
342          */
343         commit_ret = commit(state->handle, state->fsp, state->fsp->fh->pos,
344                             state->ret);
345
346         if (commit_ret == -1) {
347                 state->ret = -1;
348         }
349
350         tevent_req_done(req);
351 }
352
353 static ssize_t commit_pwrite_recv(struct tevent_req *req,
354                                   struct vfs_aio_state *vfs_aio_state)
355 {
356         struct commit_pwrite_state *state =
357                 tevent_req_data(req, struct commit_pwrite_state);
358
359         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
360                 return -1;
361         }
362         *vfs_aio_state = state->vfs_aio_state;
363         return state->ret;
364 }
365
366 static int commit_close(
367         vfs_handle_struct * handle,
368         files_struct *      fsp)
369 {
370         /* Commit errors not checked, close() will find them again */
371         commit_all(handle, fsp);
372         return SMB_VFS_NEXT_CLOSE(handle, fsp);
373 }
374
375 static int commit_ftruncate(
376         vfs_handle_struct * handle,
377         files_struct *      fsp,
378         off_t           len)
379 {
380         int result;
381
382         result = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
383         if (result == 0) {
384                 struct commit_info *c;
385                 if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(
386                              handle, fsp))) {
387                         commit(handle, fsp, len, 0);
388                         c->eof = len;
389                 }
390         }
391
392         return result;
393 }
394
395 static struct vfs_fn_pointers vfs_commit_fns = {
396         .open_fn = commit_open,
397         .close_fn = commit_close,
398         .write_fn = commit_write,
399         .pwrite_fn = commit_pwrite,
400         .pwrite_send_fn = commit_pwrite_send,
401         .pwrite_recv_fn = commit_pwrite_recv,
402         .connect_fn = commit_connect,
403         .ftruncate_fn = commit_ftruncate
404 };
405
406 NTSTATUS vfs_commit_init(TALLOC_CTX *);
407 NTSTATUS vfs_commit_init(TALLOC_CTX *ctx)
408 {
409         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE,
410                                 &vfs_commit_fns);
411 }
412
413