s3-vfs: Use the system. namespace for fake ACLs
[kai/samba.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
23 /* Commit data module.
24  *
25  * The purpose of this module is to flush data to disk at regular intervals,
26  * just like the NFS commit operation. There's two rationales for this. First,
27  * it minimises the data loss in case of a power outage without incurring
28  * the poor performance of synchronous I/O. Second, a steady flush rate
29  * can produce better throughput than suddenly dumping massive amounts of
30  * writes onto a disk.
31  *
32  * Tunables:
33  *
34  *  commit: dthresh         Amount of dirty data that can accumulate
35  *                          before we commit (sync) it.
36  *
37  *  commit: debug           Debug level at which to emit messages.
38  *
39  *  commit: eof mode        String. Tunes how the module tries to guess when
40  *                          the client has written the last bytes of the file.
41  *                          Possible values (default = hinted):
42  *
43  *     (*)  = hinted        Some clients (i.e. Windows Explorer) declare the
44  *                          size of the file before transferring it. With this
45  *                          option, we remember that hint, and commit after
46  *                          writing in that file position. If the client
47  *                          doesn't declare the size of file, commiting on EOF 
48  *                          is not triggered.
49  *
50  *          = growth        Commits after a write operation has made the file
51  *                          size grow. If the client declares a file size, it
52  *                          refrains to commit until the file has reached it.
53  *                          Useful for defeating writeback on NFS shares.
54  *
55  */
56
57 #define MODULE "commit"
58
59 static int module_debug;
60
61 enum eof_mode
62 {
63     EOF_NONE = 0x0000,
64     EOF_HINTED = 0x0001,
65     EOF_GROWTH = 0x0002
66 };
67
68 struct commit_info
69 {
70         /* For chunk-based commits */
71         off_t dbytes;   /* Dirty (uncommitted) bytes */
72         off_t dthresh;  /* Dirty data threshold */
73         /* For commits on EOF */
74         enum eof_mode on_eof;
75         off_t eof;              /* Expected file size */
76 };
77
78 static int commit_do(
79         struct commit_info *            c,
80         int                             fd)
81 {
82         int result;
83
84         DEBUG(module_debug,
85                 ("%s: flushing %lu dirty bytes\n",
86                  MODULE, (unsigned long)c->dbytes));
87
88 #if HAVE_FDATASYNC
89         result = fdatasync(fd);
90 #elif HAVE_FSYNC
91         result = fsync(fd);
92 #else
93         DEBUG(0, ("%s: WARNING: no commit support on this platform\n",
94                 MODULE));
95         result = 0
96 #endif
97         if (result == 0) {
98                 c->dbytes = 0;  /* on success, no dirty bytes */
99         }
100         return result;
101 }
102
103 static int commit_all(
104         struct vfs_handle_struct *      handle,
105         files_struct *                  fsp)
106 {
107         struct commit_info *c;
108
109         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
110                 if (c->dbytes) {
111                         DEBUG(module_debug,
112                                 ("%s: flushing %lu dirty bytes\n",
113                                  MODULE, (unsigned long)c->dbytes));
114
115                         return commit_do(c, fsp->fh->fd);
116                 }
117         }
118         return 0;
119 }
120
121 static int commit(
122         struct vfs_handle_struct *      handle,
123         files_struct *                  fsp,
124         off_t                   offset,
125         ssize_t                         last_write)
126 {
127         struct commit_info *c;
128
129         if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(handle, fsp))
130             == NULL) {
131                 return 0;
132         }
133
134         c->dbytes += last_write;        /* dirty bytes always counted */
135
136         if (c->dthresh && (c->dbytes > c->dthresh)) {
137                 return commit_do(c, fsp->fh->fd);
138         }
139
140         /* Return if we are not in EOF mode or if we have temporarily opted
141          * out of it.
142          */
143         if (c->on_eof == EOF_NONE || c->eof < 0) {
144                 return 0;
145         }
146
147         /* This write hit or went past our cache the file size. */
148         if ((offset + last_write) >= c->eof) {
149                 if (commit_do(c, fsp->fh->fd) == -1) {
150                         return -1;
151                 }
152
153                 /* Hinted mode only commits the first time we hit EOF. */
154                 if (c->on_eof == EOF_HINTED) {
155                     c->eof = -1;
156                 } else if (c->on_eof == EOF_GROWTH) {
157                     c->eof = offset + last_write;
158                 }
159         }
160
161         return 0;
162 }
163
164 static int commit_connect(
165         struct vfs_handle_struct *  handle,
166         const char *                service,
167         const char *                user)
168 {
169         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
170
171         if (ret < 0) {
172                 return ret;
173         }
174
175         module_debug = lp_parm_int(SNUM(handle->conn), MODULE, "debug", 100);
176         return 0;
177 }
178
179 static int commit_open(
180         vfs_handle_struct * handle,
181         struct smb_filename *smb_fname,
182         files_struct *      fsp,
183         int                 flags,
184         mode_t              mode)
185 {
186         off_t dthresh;
187         const char *eof_mode;
188         struct commit_info *c = NULL;
189         int fd;
190
191         /* Don't bother with read-only files. */
192         if ((flags & O_ACCMODE) == O_RDONLY) {
193                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
194         }
195
196         /* Read and check module configuration */
197         dthresh = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
198                                         MODULE, "dthresh", NULL));
199
200         eof_mode = lp_parm_const_string(SNUM(handle->conn),
201                                         MODULE, "eof mode", "none");
202
203         if (dthresh > 0 || !strequal(eof_mode, "none")) {
204                 c = (struct commit_info *)VFS_ADD_FSP_EXTENSION(
205                         handle, fsp, struct commit_info, NULL);
206                 /* Process main tunables */
207                 if (c) {
208                         c->dthresh = dthresh;
209                         c->dbytes = 0;
210                         c->on_eof = EOF_NONE;
211                         c->eof = 0;
212                 }
213         }
214         /* Process eof_mode tunable */
215         if (c) {
216                 if (strequal(eof_mode, "hinted")) {
217                         c->on_eof = EOF_HINTED;
218                 } else if (strequal(eof_mode, "growth")) {
219                         c->on_eof = EOF_GROWTH;
220                 }
221         }
222
223         fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
224         if (fd == -1) {
225                 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
226                 return fd;
227         }
228
229         /* EOF commit modes require us to know the initial file size. */
230         if (c && (c->on_eof != EOF_NONE)) {
231                 SMB_STRUCT_STAT st;
232                 if (SMB_VFS_FSTAT(fsp, &st) == -1) {
233                         return -1;
234                 }
235                 c->eof = st.st_ex_size;
236         }
237
238         return fd;
239 }
240
241 static ssize_t commit_write(
242         vfs_handle_struct * handle,
243         files_struct *      fsp,
244         const void *        data,
245         size_t              count)
246 {
247         ssize_t ret;
248         ret = SMB_VFS_NEXT_WRITE(handle, fsp, data, count);
249
250         if (ret > 0) {
251                 if (commit(handle, fsp, fsp->fh->pos, ret) == -1) {
252                         return -1;
253                 }
254         }
255
256         return ret;
257 }
258
259 static ssize_t commit_pwrite(
260         vfs_handle_struct * handle,
261         files_struct *      fsp,
262         const void *        data,
263         size_t              count,
264         off_t       offset)
265 {
266         ssize_t ret;
267
268         ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, count, offset);
269         if (ret > 0) {
270                 if (commit(handle, fsp, offset, ret) == -1) {
271                         return -1;
272                 }
273         }
274
275         return ret;
276 }
277
278 static int commit_close(
279         vfs_handle_struct * handle,
280         files_struct *      fsp)
281 {
282         /* Commit errors not checked, close() will find them again */
283         commit_all(handle, fsp);
284         return SMB_VFS_NEXT_CLOSE(handle, fsp);
285 }
286
287 static int commit_ftruncate(
288         vfs_handle_struct * handle,
289         files_struct *      fsp,
290         off_t           len)
291 {
292         int result;
293
294         result = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
295         if (result == 0) {
296                 struct commit_info *c;
297                 if ((c = (struct commit_info *)VFS_FETCH_FSP_EXTENSION(
298                              handle, fsp))) {
299                         commit(handle, fsp, len, 0);
300                         c->eof = len;
301                 }
302         }
303
304         return result;
305 }
306
307 static struct vfs_fn_pointers vfs_commit_fns = {
308         .open_fn = commit_open,
309         .close_fn = commit_close,
310         .write = commit_write,
311         .pwrite = commit_pwrite,
312         .connect_fn = commit_connect,
313         .ftruncate = commit_ftruncate
314 };
315
316 NTSTATUS vfs_commit_init(void);
317 NTSTATUS vfs_commit_init(void)
318 {
319         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE,
320                                 &vfs_commit_fns);
321 }
322
323