LCOV - code coverage report
Current view: top level - source3/modules - vfs_syncops.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 4 114 3.5 %
Date: 2021-09-23 10:06:22 Functions: 1 14 7.1 %

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

Generated by: LCOV version 1.13