s3: VFS: syncops: Fix syncops_linkat() to cope with real directory fsps.
[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(const char *dname)
75 {
76 #ifdef O_DIRECTORY
77         int fd = open(dname, O_DIRECTORY|O_RDONLY);
78         if (fd != -1) {
79                 fsync(fd);
80                 close(fd);
81         }
82 #else
83         DIR *d = opendir(dname);
84         if (d != NULL) {
85                 fsync(dirfd(d));
86                 closedir(d);
87         }
88 #endif
89 }
90
91 /*
92   sync two meta data changes for 2 names
93  */
94 static void syncops_two_names(const char *name1, const char *name2)
95 {
96         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
97         char *parent1, *parent2;
98         parent1 = parent_dir(tmp_ctx, name1);
99         parent2 = parent_dir(tmp_ctx, name2);
100         if (!parent1 || !parent2) {
101                 talloc_free(tmp_ctx);
102                 return;
103         }
104         syncops_sync_directory(parent1);
105         if (strcmp(parent1, parent2) != 0) {
106                 syncops_sync_directory(parent2);                
107         }
108         talloc_free(tmp_ctx);
109 }
110
111 /*
112   sync two meta data changes for 1 names
113  */
114 static void syncops_smb_fname(const struct smb_filename *smb_fname)
115 {
116         char *parent = NULL;
117         if (smb_fname != NULL) {
118                 parent = parent_dir(NULL, smb_fname->base_name);
119                 if (parent != NULL) {
120                         syncops_sync_directory(parent);
121                         talloc_free(parent);
122                 }
123         }
124 }
125
126
127 /*
128   renameat needs special handling, as we may need to fsync two directories
129  */
130 static int syncops_renameat(vfs_handle_struct *handle,
131                         files_struct *srcfsp,
132                         const struct smb_filename *smb_fname_src,
133                         files_struct *dstfsp,
134                         const struct smb_filename *smb_fname_dst)
135 {
136
137         int ret;
138         struct syncops_config_data *config;
139
140         SMB_VFS_HANDLE_GET_DATA(handle, config,
141                                 struct syncops_config_data,
142                                 return -1);
143
144         ret = SMB_VFS_NEXT_RENAMEAT(handle,
145                         srcfsp,
146                         smb_fname_src,
147                         dstfsp,
148                         smb_fname_dst);
149         if (ret == 0 && config->onmeta && !config->disable) {
150                 syncops_two_names(smb_fname_src->base_name,
151                                   smb_fname_dst->base_name);
152         }
153         return ret;
154 }
155
156 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
157         int ret; \
158         struct smb_filename *full_fname = NULL; \
159         struct syncops_config_data *config; \
160         SMB_VFS_HANDLE_GET_DATA(handle, config, \
161                                 struct syncops_config_data, \
162                                 return -1); \
163         full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
164                                 dirfsp, \
165                                 smb_fname); \
166         if (full_fname == NULL) { \
167                 return -1; \
168         } \
169         ret = SMB_VFS_NEXT_ ## op args; \
170         if (ret == 0 \
171         && config->onmeta && !config->disable \
172         && fname) syncops_smb_fname(full_fname); \
173         TALLOC_FREE(full_fname); \
174         return ret; \
175 } while (0)
176
177 static int syncops_symlinkat(vfs_handle_struct *handle,
178                         const struct smb_filename *link_contents,
179                         struct files_struct *dirfsp,
180                         const struct smb_filename *smb_fname)
181 {
182         SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
183                         smb_fname,
184                                 (handle,
185                                 link_contents,
186                                 dirfsp,
187                                 smb_fname));
188 }
189
190 static int syncops_linkat(vfs_handle_struct *handle,
191                         files_struct *srcfsp,
192                         const struct smb_filename *old_smb_fname,
193                         files_struct *dstfsp,
194                         const struct smb_filename *new_smb_fname,
195                         int flags)
196 {
197         int ret;
198         struct syncops_config_data *config;
199         struct smb_filename *old_full_fname = NULL;
200         struct smb_filename *new_full_fname = NULL;
201
202         SMB_VFS_HANDLE_GET_DATA(handle, config,
203                                 struct syncops_config_data,
204                                 return -1);
205
206         old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
207                                 srcfsp,
208                                 old_smb_fname);
209         if (old_full_fname == NULL) {
210                 return -1;
211         }
212         new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
213                                 dstfsp,
214                                 new_smb_fname);
215         if (new_full_fname == NULL) {
216                 TALLOC_FREE(old_full_fname);
217                 return -1;
218         }
219         ret = SMB_VFS_NEXT_LINKAT(handle,
220                         srcfsp,
221                         old_smb_fname,
222                         dstfsp,
223                         new_smb_fname,
224                         flags);
225
226         if (ret == 0 && config->onmeta && !config->disable) {
227                 syncops_two_names(old_full_fname->base_name,
228                                   new_full_fname->base_name);
229         }
230         TALLOC_FREE(old_full_fname);
231         TALLOC_FREE(new_full_fname);
232         return ret;
233 }
234
235 static int syncops_openat(struct vfs_handle_struct *handle,
236                           const struct files_struct *dirfsp,
237                           const struct smb_filename *smb_fname,
238                           struct files_struct *fsp,
239                           int flags,
240                           mode_t mode)
241 {
242         SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
243                                (handle, dirfsp, smb_fname, fsp, flags, mode));
244 }
245
246 static int syncops_unlinkat(vfs_handle_struct *handle,
247                         files_struct *dirfsp,
248                         const struct smb_filename *smb_fname,
249                         int flags)
250 {
251         SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
252                         smb_fname,
253                                 (handle,
254                                 dirfsp,
255                                 smb_fname,
256                                 flags));
257 }
258
259 static int syncops_mknodat(vfs_handle_struct *handle,
260                         files_struct *dirfsp,
261                         const struct smb_filename *smb_fname,
262                         mode_t mode,
263                         SMB_DEV_T dev)
264 {
265         SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
266                         smb_fname,
267                                 (handle,
268                                 dirfsp,
269                                 smb_fname,
270                                 mode,
271                                 dev));
272 }
273
274 static int syncops_mkdirat(vfs_handle_struct *handle,
275                         struct files_struct *dirfsp,
276                         const struct smb_filename *smb_fname,
277                         mode_t mode)
278 {
279         SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
280                         full_fname,
281                                 (handle,
282                                 dirfsp,
283                                 smb_fname,
284                                 mode));
285 }
286
287 /* close needs to be handled specially */
288 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
289 {
290         struct syncops_config_data *config;
291
292         SMB_VFS_HANDLE_GET_DATA(handle, config,
293                                 struct syncops_config_data,
294                                 return -1);
295
296         if (fsp->fsp_flags.can_write && config->onclose) {
297                 /* ideally we'd only do this if we have written some
298                  data, but there is no flag for that in fsp yet. */
299                 fsync(fsp_get_io_fd(fsp));
300         }
301         return SMB_VFS_NEXT_CLOSE(handle, fsp);
302 }
303
304 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
305                            const char *user)
306 {
307
308         struct syncops_config_data *config;
309         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
310         if (ret < 0) {
311                 return ret;
312         }
313
314         config = talloc_zero(handle->conn, struct syncops_config_data);
315         if (!config) {
316                 SMB_VFS_NEXT_DISCONNECT(handle);
317                 DEBUG(0, ("talloc_zero() failed\n"));
318                 return -1;
319         }
320
321         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
322                                         "onclose", true);
323
324         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
325                                         "onmeta", true);
326
327         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
328                                         "disable", false);
329
330         SMB_VFS_HANDLE_SET_DATA(handle, config,
331                                 NULL, struct syncops_config_data,
332                                 return -1);
333
334         return 0;
335
336 }
337
338 static struct vfs_fn_pointers vfs_syncops_fns = {
339         .connect_fn = syncops_connect,
340         .mkdirat_fn = syncops_mkdirat,
341         .openat_fn = syncops_openat,
342         .renameat_fn = syncops_renameat,
343         .unlinkat_fn = syncops_unlinkat,
344         .symlinkat_fn = syncops_symlinkat,
345         .linkat_fn = syncops_linkat,
346         .mknodat_fn = syncops_mknodat,
347         .close_fn = syncops_close,
348 };
349
350 static_decl_vfs;
351 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
352 {
353         NTSTATUS ret;
354
355         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
356                                &vfs_syncops_fns);
357
358         if (!NT_STATUS_IS_OK(ret))
359                 return ret;
360
361         return ret;
362 }