LCOV - code coverage report
Current view: top level - source3/modules - vfs_syncops.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 4 115 3.5 %
Date: 2023-11-21 12:31:41 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             :         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          27 : NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
     407             : {
     408             :         NTSTATUS ret;
     409             : 
     410          27 :         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
     411             :                                &vfs_syncops_fns);
     412             : 
     413          27 :         if (!NT_STATUS_IS_OK(ret))
     414           0 :                 return ret;
     415             : 
     416          27 :         return ret;
     417             : }

Generated by: LCOV version 1.14