Line data Source code
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 0 : static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
64 : {
65 0 : const char *p = strrchr(name, '/');
66 0 : if (p == NULL) {
67 0 : return talloc_strdup(mem_ctx, ".");
68 : }
69 0 : return talloc_strndup(mem_ctx, name, (p+1) - name);
70 : }
71 :
72 : /*
73 : fsync a directory by name
74 : */
75 0 : static void syncops_sync_directory(connection_struct *conn,
76 : char *dname)
77 : {
78 0 : struct smb_Dir *dir_hnd = NULL;
79 0 : struct files_struct *dirfsp = NULL;
80 0 : struct smb_filename smb_dname = { .base_name = dname };
81 : NTSTATUS status;
82 :
83 0 : status = OpenDir(talloc_tos(),
84 : conn,
85 : &smb_dname,
86 : "*",
87 : 0,
88 : &dir_hnd);
89 0 : if (!NT_STATUS_IS_OK(status)) {
90 0 : errno = map_errno_from_nt_status(status);
91 0 : return;
92 : }
93 :
94 0 : dirfsp = dir_hnd_fetch_fsp(dir_hnd);
95 :
96 0 : smb_vfs_fsync_sync(dirfsp);
97 :
98 0 : TALLOC_FREE(dir_hnd);
99 : }
100 :
101 : /*
102 : sync two meta data changes for 2 names
103 : */
104 0 : static void syncops_two_names(connection_struct *conn,
105 : const struct smb_filename *name1,
106 : const struct smb_filename *name2)
107 : {
108 0 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
109 : char *parent1, *parent2;
110 0 : parent1 = parent_dir(tmp_ctx, name1->base_name);
111 0 : parent2 = parent_dir(tmp_ctx, name2->base_name);
112 0 : if (!parent1 || !parent2) {
113 0 : talloc_free(tmp_ctx);
114 0 : return;
115 : }
116 0 : syncops_sync_directory(conn, parent1);
117 0 : if (strcmp(parent1, parent2) != 0) {
118 0 : syncops_sync_directory(conn, parent2);
119 : }
120 0 : talloc_free(tmp_ctx);
121 : }
122 :
123 : /*
124 : sync two meta data changes for 1 names
125 : */
126 0 : static void syncops_smb_fname(connection_struct *conn,
127 : const struct smb_filename *smb_fname)
128 : {
129 0 : char *parent = NULL;
130 0 : if (smb_fname != NULL) {
131 0 : parent = parent_dir(NULL, smb_fname->base_name);
132 0 : if (parent != NULL) {
133 0 : syncops_sync_directory(conn, parent);
134 0 : talloc_free(parent);
135 : }
136 : }
137 0 : }
138 :
139 :
140 : /*
141 : renameat needs special handling, as we may need to fsync two directories
142 : */
143 0 : 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 0 : struct smb_filename *full_fname_src = NULL;
152 0 : struct smb_filename *full_fname_dst = NULL;
153 : struct syncops_config_data *config;
154 :
155 0 : SMB_VFS_HANDLE_GET_DATA(handle, config,
156 : struct syncops_config_data,
157 : return -1);
158 :
159 0 : ret = SMB_VFS_NEXT_RENAMEAT(handle,
160 : srcfsp,
161 : smb_fname_src,
162 : dstfsp,
163 : smb_fname_dst);
164 0 : if (ret == -1) {
165 0 : return ret;
166 : }
167 0 : if (config->disable) {
168 0 : return ret;
169 : }
170 0 : if (!config->onmeta) {
171 0 : return ret;
172 : }
173 :
174 0 : full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
175 : srcfsp,
176 : smb_fname_src);
177 0 : if (full_fname_src == NULL) {
178 0 : errno = ENOMEM;
179 0 : return ret;
180 : }
181 0 : full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
182 : dstfsp,
183 : smb_fname_dst);
184 0 : if (full_fname_dst == NULL) {
185 0 : TALLOC_FREE(full_fname_src);
186 0 : errno = ENOMEM;
187 0 : return ret;
188 : }
189 0 : syncops_two_names(handle->conn,
190 : full_fname_src,
191 : full_fname_dst);
192 0 : TALLOC_FREE(full_fname_src);
193 0 : TALLOC_FREE(full_fname_dst);
194 0 : 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 0 : 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 0 : SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
231 : smb_fname,
232 : (handle,
233 : link_contents,
234 : dirfsp,
235 : smb_fname));
236 : }
237 :
238 0 : 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 0 : struct smb_filename *old_full_fname = NULL;
248 0 : struct smb_filename *new_full_fname = NULL;
249 :
250 0 : SMB_VFS_HANDLE_GET_DATA(handle, config,
251 : struct syncops_config_data,
252 : return -1);
253 :
254 0 : ret = SMB_VFS_NEXT_LINKAT(handle,
255 : srcfsp,
256 : old_smb_fname,
257 : dstfsp,
258 : new_smb_fname,
259 : flags);
260 :
261 0 : if (ret == -1) {
262 0 : return ret;
263 : }
264 0 : if (config->disable) {
265 0 : return ret;
266 : }
267 0 : if (!config->onmeta) {
268 0 : return ret;
269 : }
270 :
271 0 : old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
272 : srcfsp,
273 : old_smb_fname);
274 0 : if (old_full_fname == NULL) {
275 0 : return ret;
276 : }
277 0 : new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
278 : dstfsp,
279 : new_smb_fname);
280 0 : if (new_full_fname == NULL) {
281 0 : TALLOC_FREE(old_full_fname);
282 0 : return ret;
283 : }
284 0 : syncops_two_names(handle->conn,
285 : old_full_fname,
286 : new_full_fname);
287 0 : TALLOC_FREE(old_full_fname);
288 0 : TALLOC_FREE(new_full_fname);
289 0 : return ret;
290 : }
291 :
292 0 : 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 0 : SYNCOPS_NEXT_SMB_FNAME(OPENAT, (how->flags & O_CREAT ? smb_fname : NULL),
299 : (handle, dirfsp, smb_fname, fsp, how));
300 : }
301 :
302 0 : static int syncops_unlinkat(vfs_handle_struct *handle,
303 : files_struct *dirfsp,
304 : const struct smb_filename *smb_fname,
305 : int flags)
306 : {
307 0 : SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
308 : smb_fname,
309 : (handle,
310 : dirfsp,
311 : smb_fname,
312 : flags));
313 : }
314 :
315 0 : 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 0 : SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
322 : smb_fname,
323 : (handle,
324 : dirfsp,
325 : smb_fname,
326 : mode,
327 : dev));
328 : }
329 :
330 0 : 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 0 : 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 0 : static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
345 : {
346 : struct syncops_config_data *config;
347 :
348 0 : SMB_VFS_HANDLE_GET_DATA(handle, config,
349 : struct syncops_config_data,
350 : return -1);
351 :
352 0 : 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 0 : fsync(fsp_get_io_fd(fsp));
356 : }
357 0 : return SMB_VFS_NEXT_CLOSE(handle, fsp);
358 : }
359 :
360 0 : 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 0 : int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
366 0 : if (ret < 0) {
367 0 : return ret;
368 : }
369 :
370 0 : config = talloc_zero(handle->conn, struct syncops_config_data);
371 0 : if (!config) {
372 0 : SMB_VFS_NEXT_DISCONNECT(handle);
373 0 : DEBUG(0, ("talloc_zero() failed\n"));
374 0 : return -1;
375 : }
376 :
377 0 : config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
378 : "onclose", true);
379 :
380 0 : config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
381 : "onmeta", true);
382 :
383 0 : config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
384 : "disable", false);
385 :
386 0 : SMB_VFS_HANDLE_SET_DATA(handle, config,
387 : NULL, struct syncops_config_data,
388 : return -1);
389 :
390 0 : 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 27 : NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
408 : {
409 : NTSTATUS ret;
410 :
411 27 : ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
412 : &vfs_syncops_fns);
413 :
414 27 : if (!NT_STATUS_IS_OK(ret))
415 0 : return ret;
416 :
417 27 : return ret;
418 : }
|