LCOV - code coverage report
Current view: top level - source3/modules - vfs_syncops.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 4 115 3.5 %
Date: 2024-04-13 12:30:31 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             : #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             : }

Generated by: LCOV version 1.14