vfs: change openat propotype to match linux openat2
[samba.git] / source3 / modules / vfs_syncops.c
1 /* 
2  * ensure meta data operations are performed synchronously
3  *
4  * Copyright (C) Andrew Tridgell     2007
5  * Copyright (C) Christian Ambach, 2010-2011
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *  
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *  
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "smbd/smbd.h"
25
26 /*
27
28   Some filesystems (even some journaled filesystems) require that a
29   fsync() be performed on many meta data operations to ensure that the
30   operation is guaranteed to remain in the filesystem after a power
31   failure. This is particularly important for some cluster filesystems
32   which are participating in a node failover system with clustered
33   Samba
34
35   On those filesystems this module provides a way to perform those
36   operations safely.  
37
38   most of the performance loss with this module is in fsync on close(). 
39   You can disable that with
40      syncops:onclose = no
41   that can be set either globally or per share.
42
43   On certain filesystems that only require the last data written to be
44   fsync()'ed, you can disable the metadata synchronization of this module with
45      syncops:onmeta = no
46   This option can be set either globally or per share.
47
48   you can also disable the module completely for a share with
49      syncops:disable = true
50
51  */
52
53 struct syncops_config_data {
54         bool onclose;
55         bool onmeta;
56         bool disable;
57 };
58
59 /*
60   given a filename, find the parent directory
61  */
62 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
63 {
64         const char *p = strrchr(name, '/');
65         if (p == NULL) {
66                 return talloc_strdup(mem_ctx, ".");
67         }
68         return talloc_strndup(mem_ctx, name, (p+1) - name);
69 }
70
71 /*
72   fsync a directory by name
73  */
74 static void syncops_sync_directory(connection_struct *conn,
75                                    char *dname)
76 {
77         struct smb_Dir *dir_hnd = NULL;
78         struct files_struct *dirfsp = NULL;
79         struct smb_filename smb_dname = { .base_name = dname };
80         NTSTATUS status;
81
82         status = OpenDir(talloc_tos(),
83                          conn,
84                          &smb_dname,
85                          "*",
86                          0,
87                          &dir_hnd);
88         if (!NT_STATUS_IS_OK(status)) {
89                 errno = map_errno_from_nt_status(status);
90                 return;
91         }
92
93         dirfsp = dir_hnd_fetch_fsp(dir_hnd);
94
95         smb_vfs_fsync_sync(dirfsp);
96
97         TALLOC_FREE(dir_hnd);
98 }
99
100 /*
101   sync two meta data changes for 2 names
102  */
103 static void syncops_two_names(connection_struct *conn,
104                               const struct smb_filename *name1,
105                               const struct smb_filename *name2)
106 {
107         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
108         char *parent1, *parent2;
109         parent1 = parent_dir(tmp_ctx, name1->base_name);
110         parent2 = parent_dir(tmp_ctx, name2->base_name);
111         if (!parent1 || !parent2) {
112                 talloc_free(tmp_ctx);
113                 return;
114         }
115         syncops_sync_directory(conn, parent1);
116         if (strcmp(parent1, parent2) != 0) {
117                 syncops_sync_directory(conn, parent2);
118         }
119         talloc_free(tmp_ctx);
120 }
121
122 /*
123   sync two meta data changes for 1 names
124  */
125 static void syncops_smb_fname(connection_struct *conn,
126                               const struct smb_filename *smb_fname)
127 {
128         char *parent = NULL;
129         if (smb_fname != NULL) {
130                 parent = parent_dir(NULL, smb_fname->base_name);
131                 if (parent != NULL) {
132                         syncops_sync_directory(conn, parent);
133                         talloc_free(parent);
134                 }
135         }
136 }
137
138
139 /*
140   renameat needs special handling, as we may need to fsync two directories
141  */
142 static int syncops_renameat(vfs_handle_struct *handle,
143                         files_struct *srcfsp,
144                         const struct smb_filename *smb_fname_src,
145                         files_struct *dstfsp,
146                         const struct smb_filename *smb_fname_dst)
147 {
148
149         int ret;
150         struct smb_filename *full_fname_src = NULL;
151         struct smb_filename *full_fname_dst = NULL;
152         struct syncops_config_data *config;
153
154         SMB_VFS_HANDLE_GET_DATA(handle, config,
155                                 struct syncops_config_data,
156                                 return -1);
157
158         ret = SMB_VFS_NEXT_RENAMEAT(handle,
159                         srcfsp,
160                         smb_fname_src,
161                         dstfsp,
162                         smb_fname_dst);
163         if (ret == -1) {
164                 return ret;
165         }
166         if (config->disable) {
167                 return ret;
168         }
169         if (!config->onmeta) {
170                 return ret;
171         }
172
173         full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
174                                                       srcfsp,
175                                                       smb_fname_src);
176         if (full_fname_src == NULL) {
177                 errno = ENOMEM;
178                 return ret;
179         }
180         full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
181                                                       dstfsp,
182                                                       smb_fname_dst);
183         if (full_fname_dst == NULL) {
184                 TALLOC_FREE(full_fname_src);
185                 errno = ENOMEM;
186                 return ret;
187         }
188         syncops_two_names(handle->conn,
189                           full_fname_src,
190                           full_fname_dst);
191         TALLOC_FREE(full_fname_src);
192         TALLOC_FREE(full_fname_dst);
193         return ret;
194 }
195
196 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
197         int ret; \
198         struct smb_filename *full_fname = NULL; \
199         struct syncops_config_data *config; \
200         SMB_VFS_HANDLE_GET_DATA(handle, config, \
201                                 struct syncops_config_data, \
202                                 return -1); \
203         ret = SMB_VFS_NEXT_ ## op args; \
204         if (ret != 0) { \
205                 return ret; \
206         } \
207         if (config->disable) { \
208                 return ret; \
209         } \
210         if (!config->onmeta) { \
211                 return ret; \
212         } \
213         full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
214                                 dirfsp, \
215                                 smb_fname); \
216         if (full_fname == NULL) { \
217                 return ret; \
218         } \
219         syncops_smb_fname(dirfsp->conn, full_fname); \
220         TALLOC_FREE(full_fname); \
221         return ret; \
222 } while (0)
223
224 static int syncops_symlinkat(vfs_handle_struct *handle,
225                         const struct smb_filename *link_contents,
226                         struct files_struct *dirfsp,
227                         const struct smb_filename *smb_fname)
228 {
229         SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
230                         smb_fname,
231                                 (handle,
232                                 link_contents,
233                                 dirfsp,
234                                 smb_fname));
235 }
236
237 static int syncops_linkat(vfs_handle_struct *handle,
238                         files_struct *srcfsp,
239                         const struct smb_filename *old_smb_fname,
240                         files_struct *dstfsp,
241                         const struct smb_filename *new_smb_fname,
242                         int flags)
243 {
244         int ret;
245         struct syncops_config_data *config;
246         struct smb_filename *old_full_fname = NULL;
247         struct smb_filename *new_full_fname = NULL;
248
249         SMB_VFS_HANDLE_GET_DATA(handle, config,
250                                 struct syncops_config_data,
251                                 return -1);
252
253         ret = SMB_VFS_NEXT_LINKAT(handle,
254                         srcfsp,
255                         old_smb_fname,
256                         dstfsp,
257                         new_smb_fname,
258                         flags);
259
260         if (ret == -1) {
261                 return ret;
262         }
263         if (config->disable) {
264                 return ret;
265         }
266         if (!config->onmeta) {
267                 return ret;
268         }
269
270         old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
271                                                       srcfsp,
272                                                       old_smb_fname);
273         if (old_full_fname == NULL) {
274                 return ret;
275         }
276         new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
277                                                       dstfsp,
278                                                       new_smb_fname);
279         if (new_full_fname == NULL) {
280                 TALLOC_FREE(old_full_fname);
281                 return ret;
282         }
283         syncops_two_names(handle->conn,
284                           old_full_fname,
285                           new_full_fname);
286         TALLOC_FREE(old_full_fname);
287         TALLOC_FREE(new_full_fname);
288         return ret;
289 }
290
291 static int syncops_openat(struct vfs_handle_struct *handle,
292                           const struct files_struct *dirfsp,
293                           const struct smb_filename *smb_fname,
294                           struct files_struct *fsp,
295                           const struct vfs_open_how *how)
296 {
297         SYNCOPS_NEXT_SMB_FNAME(OPENAT, (how->flags & O_CREAT ? smb_fname : NULL),
298                                (handle, dirfsp, smb_fname, fsp, how));
299 }
300
301 static int syncops_unlinkat(vfs_handle_struct *handle,
302                         files_struct *dirfsp,
303                         const struct smb_filename *smb_fname,
304                         int flags)
305 {
306         SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
307                         smb_fname,
308                                 (handle,
309                                 dirfsp,
310                                 smb_fname,
311                                 flags));
312 }
313
314 static int syncops_mknodat(vfs_handle_struct *handle,
315                         files_struct *dirfsp,
316                         const struct smb_filename *smb_fname,
317                         mode_t mode,
318                         SMB_DEV_T dev)
319 {
320         SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
321                         smb_fname,
322                                 (handle,
323                                 dirfsp,
324                                 smb_fname,
325                                 mode,
326                                 dev));
327 }
328
329 static int syncops_mkdirat(vfs_handle_struct *handle,
330                         struct files_struct *dirfsp,
331                         const struct smb_filename *smb_fname,
332                         mode_t mode)
333 {
334         SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
335                         full_fname,
336                                 (handle,
337                                 dirfsp,
338                                 smb_fname,
339                                 mode));
340 }
341
342 /* close needs to be handled specially */
343 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
344 {
345         struct syncops_config_data *config;
346
347         SMB_VFS_HANDLE_GET_DATA(handle, config,
348                                 struct syncops_config_data,
349                                 return -1);
350
351         if (fsp->fsp_flags.can_write && config->onclose) {
352                 /* ideally we'd only do this if we have written some
353                  data, but there is no flag for that in fsp yet. */
354                 fsync(fsp_get_io_fd(fsp));
355         }
356         return SMB_VFS_NEXT_CLOSE(handle, fsp);
357 }
358
359 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
360                            const char *user)
361 {
362
363         struct syncops_config_data *config;
364         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
365         if (ret < 0) {
366                 return ret;
367         }
368
369         config = talloc_zero(handle->conn, struct syncops_config_data);
370         if (!config) {
371                 SMB_VFS_NEXT_DISCONNECT(handle);
372                 DEBUG(0, ("talloc_zero() failed\n"));
373                 return -1;
374         }
375
376         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
377                                         "onclose", true);
378
379         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
380                                         "onmeta", true);
381
382         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
383                                         "disable", false);
384
385         SMB_VFS_HANDLE_SET_DATA(handle, config,
386                                 NULL, struct syncops_config_data,
387                                 return -1);
388
389         return 0;
390
391 }
392
393 static struct vfs_fn_pointers vfs_syncops_fns = {
394         .connect_fn = syncops_connect,
395         .mkdirat_fn = syncops_mkdirat,
396         .openat_fn = syncops_openat,
397         .renameat_fn = syncops_renameat,
398         .unlinkat_fn = syncops_unlinkat,
399         .symlinkat_fn = syncops_symlinkat,
400         .linkat_fn = syncops_linkat,
401         .mknodat_fn = syncops_mknodat,
402         .close_fn = syncops_close,
403 };
404
405 static_decl_vfs;
406 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
407 {
408         NTSTATUS ret;
409
410         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
411                                &vfs_syncops_fns);
412
413         if (!NT_STATUS_IS_OK(ret))
414                 return ret;
415
416         return ret;
417 }