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