s3/modules: cppcheck: Fix ctunullpointer error
[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   rename needs special handling, as we may need to fsync two directories
129  */
130 static int syncops_rename(vfs_handle_struct *handle,
131                           const struct smb_filename *smb_fname_src,
132                           const struct smb_filename *smb_fname_dst)
133 {
134
135         int ret;
136         struct syncops_config_data *config;
137
138         SMB_VFS_HANDLE_GET_DATA(handle, config,
139                                 struct syncops_config_data,
140                                 return -1);
141
142         ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
143         if (ret == 0 && config->onmeta && !config->disable) {
144                 syncops_two_names(smb_fname_src->base_name,
145                                   smb_fname_dst->base_name);
146         }
147         return ret;
148 }
149
150 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
151         int ret; \
152         struct syncops_config_data *config; \
153         SMB_VFS_HANDLE_GET_DATA(handle, config, \
154                                 struct syncops_config_data, \
155                                 return -1); \
156         ret = SMB_VFS_NEXT_ ## op args; \
157         if (ret == 0 \
158         && config->onmeta && !config->disable \
159         && fname) syncops_smb_fname(fname); \
160         return ret; \
161 } while (0)
162
163 static int syncops_symlink(vfs_handle_struct *handle,
164                         const char *link_contents,
165                         const struct smb_filename *new_smb_fname)
166 {
167         int ret;
168         struct syncops_config_data *config;
169
170         SMB_VFS_HANDLE_GET_DATA(handle, config,
171                                 struct syncops_config_data,
172                                 return -1);
173
174         ret = SMB_VFS_NEXT_SYMLINK(handle, link_contents, new_smb_fname);
175         if (ret == 0 && config->onmeta && !config->disable) {
176                 syncops_two_names(link_contents,
177                                   new_smb_fname->base_name);
178         }
179         return ret;
180 }
181
182 static int syncops_link(vfs_handle_struct *handle,
183                         const struct smb_filename *old_smb_fname,
184                         const struct smb_filename *new_smb_fname)
185 {
186         int ret;
187         struct syncops_config_data *config;
188
189         SMB_VFS_HANDLE_GET_DATA(handle, config,
190                                 struct syncops_config_data,
191                                 return -1);
192
193         ret = SMB_VFS_NEXT_LINK(handle, old_smb_fname, new_smb_fname);
194         if (ret == 0 && config->onmeta && !config->disable) {
195                 syncops_two_names(old_smb_fname->base_name,
196                                   new_smb_fname->base_name);
197         }
198         return ret;
199 }
200
201 static int syncops_open(vfs_handle_struct *handle,
202                         struct smb_filename *smb_fname, files_struct *fsp,
203                         int flags, mode_t mode)
204 {
205         SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
206                                (handle, smb_fname, fsp, flags, mode));
207 }
208
209 static int syncops_unlink(vfs_handle_struct *handle,
210                           const struct smb_filename *smb_fname)
211 {
212         SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
213 }
214
215 static int syncops_mknod(vfs_handle_struct *handle,
216                         const struct smb_filename *smb_fname,
217                         mode_t mode,
218                         SMB_DEV_T dev)
219 {
220         SYNCOPS_NEXT_SMB_FNAME(MKNOD,
221                         smb_fname, (handle, smb_fname, mode, dev));
222 }
223
224 static int syncops_mkdir(vfs_handle_struct *handle,
225                         const struct smb_filename *smb_fname,
226                         mode_t mode)
227 {
228         SYNCOPS_NEXT_SMB_FNAME(MKDIR, smb_fname, (handle, smb_fname, mode));
229 }
230
231 static int syncops_rmdir(vfs_handle_struct *handle,
232                         const struct smb_filename *smb_fname)
233 {
234         SYNCOPS_NEXT_SMB_FNAME(RMDIR, smb_fname, (handle, smb_fname));
235 }
236
237 /* close needs to be handled specially */
238 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
239 {
240         struct syncops_config_data *config;
241
242         SMB_VFS_HANDLE_GET_DATA(handle, config,
243                                 struct syncops_config_data,
244                                 return -1);
245
246         if (fsp->can_write && config->onclose) {
247                 /* ideally we'd only do this if we have written some
248                  data, but there is no flag for that in fsp yet. */
249                 fsync(fsp->fh->fd);
250         }
251         return SMB_VFS_NEXT_CLOSE(handle, fsp);
252 }
253
254 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
255                            const char *user)
256 {
257
258         struct syncops_config_data *config;
259         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
260         if (ret < 0) {
261                 return ret;
262         }
263
264         config = talloc_zero(handle->conn, struct syncops_config_data);
265         if (!config) {
266                 SMB_VFS_NEXT_DISCONNECT(handle);
267                 DEBUG(0, ("talloc_zero() failed\n"));
268                 return -1;
269         }
270
271         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
272                                         "onclose", true);
273
274         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
275                                         "onmeta", true);
276
277         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
278                                         "disable", false);
279
280         SMB_VFS_HANDLE_SET_DATA(handle, config,
281                                 NULL, struct syncops_config_data,
282                                 return -1);
283
284         return 0;
285
286 }
287
288 static struct vfs_fn_pointers vfs_syncops_fns = {
289         .connect_fn = syncops_connect,
290         .mkdir_fn = syncops_mkdir,
291         .rmdir_fn = syncops_rmdir,
292         .open_fn = syncops_open,
293         .rename_fn = syncops_rename,
294         .unlink_fn = syncops_unlink,
295         .symlink_fn = syncops_symlink,
296         .link_fn = syncops_link,
297         .mknod_fn = syncops_mknod,
298         .close_fn = syncops_close,
299 };
300
301 static_decl_vfs;
302 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
303 {
304         NTSTATUS ret;
305
306         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
307                                &vfs_syncops_fns);
308
309         if (!NT_STATUS_IS_OK(ret))
310                 return ret;
311
312         return ret;
313 }