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