source: vendor/current/source3/modules/vfs_commit.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 11.1 KB
Line 
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
60static int module_debug;
61
62enum eof_mode
63{
64 EOF_NONE = 0x0000,
65 EOF_HINTED = 0x0001,
66 EOF_GROWTH = 0x0002
67};
68
69struct 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
79static 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
104static 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
122static 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
165static 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
180static 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
251static 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
269static 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
288struct commit_pwrite_state {
289 struct vfs_handle_struct *handle;
290 struct files_struct *fsp;
291 ssize_t ret;
292 int err;
293};
294
295static void commit_pwrite_written(struct tevent_req *subreq);
296
297static 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
323static 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->err);
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
353static ssize_t commit_pwrite_recv(struct tevent_req *req, int *err)
354{
355 struct commit_pwrite_state *state =
356 tevent_req_data(req, struct commit_pwrite_state);
357
358 if (tevent_req_is_unix_error(req, err)) {
359 return -1;
360 }
361 *err = state->err;
362 return state->ret;
363}
364
365static int commit_close(
366 vfs_handle_struct * handle,
367 files_struct * fsp)
368{
369 /* Commit errors not checked, close() will find them again */
370 commit_all(handle, fsp);
371 return SMB_VFS_NEXT_CLOSE(handle, fsp);
372}
373
374static int commit_ftruncate(
375 vfs_handle_struct * handle,
376 files_struct * fsp,
377 off_t len)
378{
379 int result;
380
381 result = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
382 if (result == 0) {
383 struct commit_info *c;
384 if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(
385 handle, fsp))) {
386 commit(handle, fsp, len, 0);
387 c->eof = len;
388 }
389 }
390
391 return result;
392}
393
394static struct vfs_fn_pointers vfs_commit_fns = {
395 .open_fn = commit_open,
396 .close_fn = commit_close,
397 .write_fn = commit_write,
398 .pwrite_fn = commit_pwrite,
399 .pwrite_send_fn = commit_pwrite_send,
400 .pwrite_recv_fn = commit_pwrite_recv,
401 .connect_fn = commit_connect,
402 .ftruncate_fn = commit_ftruncate
403};
404
405NTSTATUS vfs_commit_init(void);
406NTSTATUS vfs_commit_init(void)
407{
408 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE,
409 &vfs_commit_fns);
410}
411
412
Note: See TracBrowser for help on using the repository browser.