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