LCOV - code coverage report
Current view: top level - source3/modules - vfs_acl_xattr.c (source / functions) Hit Total Coverage
Test: coverage report for master 70ed9daf Lines: 163 227 71.8 %
Date: 2024-01-11 09:59:51 Functions: 14 17 82.4 %

          Line data    Source code
       1             : /*
       2             :  * Store Windows ACLs in xattrs.
       3             :  *
       4             :  * Copyright (C) Volker Lendecke, 2008
       5             :  * Copyright (C) Jeremy Allison, 2008
       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 3 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, see <http://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : #include "includes.h"
      22             : #include "smbd/smbd.h"
      23             : #include "system/filesys.h"
      24             : #include "librpc/gen_ndr/xattr.h"
      25             : #include "auth.h"
      26             : #include "vfs_acl_common.h"
      27             : #include "lib/util/tevent_ntstatus.h"
      28             : #include "lib/util/tevent_unix.h"
      29             : 
      30             : /* Pull in the common functions. */
      31             : #define ACL_MODULE_NAME "acl_xattr"
      32             : 
      33             : #undef DBGC_CLASS
      34             : #define DBGC_CLASS DBGC_VFS
      35             : 
      36             : /*******************************************************************
      37             :  Pull a security descriptor into a DATA_BLOB from a xattr.
      38             : *******************************************************************/
      39             : 
      40      948749 : static ssize_t getxattr_do(vfs_handle_struct *handle,
      41             :                            files_struct *fsp,
      42             :                            const char *xattr_name,
      43             :                            uint8_t *val,
      44             :                            size_t size)
      45             : {
      46        2317 :         ssize_t sizeret;
      47      948749 :         int saved_errno = 0;
      48             : 
      49      948749 :         set_effective_capability(DAC_OVERRIDE_CAPABILITY);
      50      948749 :         sizeret = SMB_VFS_FGETXATTR(fsp, xattr_name, val, size);
      51      948749 :         if (sizeret == -1) {
      52      192270 :                 saved_errno = errno;
      53             :         }
      54      948749 :         drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
      55             : 
      56      948749 :         if (saved_errno != 0) {
      57      192270 :                 errno = saved_errno;
      58             :         }
      59             : 
      60      948749 :         return sizeret;
      61             : }
      62             : 
      63      948721 : static NTSTATUS fget_acl_blob(TALLOC_CTX *ctx,
      64             :                         vfs_handle_struct *handle,
      65             :                         files_struct *fsp,
      66             :                         DATA_BLOB *pblob)
      67             : {
      68      948721 :         size_t size = 4096;
      69      948721 :         uint8_t *val = NULL;
      70        2317 :         uint8_t *tmp;
      71        2317 :         ssize_t sizeret;
      72             : 
      73      948721 :         ZERO_STRUCTP(pblob);
      74             : 
      75      946404 :   again:
      76             : 
      77      948735 :         tmp = talloc_realloc(ctx, val, uint8_t, size);
      78      948735 :         if (tmp == NULL) {
      79           0 :                 TALLOC_FREE(val);
      80           0 :                 return NT_STATUS_NO_MEMORY;
      81             :         }
      82      948735 :         val = tmp;
      83             : 
      84        2317 :         sizeret =
      85      948735 :             getxattr_do(handle, fsp, XATTR_NTACL_NAME, val, size);
      86             : 
      87      948735 :         if (sizeret >= 0) {
      88      756465 :                 pblob->data = val;
      89      756465 :                 pblob->length = sizeret;
      90      756465 :                 return NT_STATUS_OK;
      91             :         }
      92             : 
      93      192270 :         if (errno != ERANGE) {
      94      192256 :                 goto err;
      95             :         }
      96             : 
      97             :         /* Too small, try again. */
      98           0 :         sizeret =
      99          14 :             getxattr_do(handle, fsp, XATTR_NTACL_NAME, NULL, 0);
     100          14 :         if (sizeret < 0) {
     101           0 :                 goto err;
     102             :         }
     103             : 
     104          14 :         if (size < sizeret) {
     105          14 :                 size = sizeret;
     106             :         }
     107             : 
     108          14 :         if (size > 65536) {
     109             :                 /* Max ACL size is 65536 bytes. */
     110           0 :                 errno = ERANGE;
     111           0 :                 goto err;
     112             :         }
     113             : 
     114          14 :         goto again;
     115      192256 :   err:
     116             :         /* Real error - exit here. */
     117      192256 :         TALLOC_FREE(val);
     118      192256 :         return map_nt_error_from_unix(errno);
     119             : }
     120             : 
     121             : /*******************************************************************
     122             :  Store a DATA_BLOB into an xattr given an fsp pointer.
     123             : *******************************************************************/
     124             : 
     125      157692 : static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle,
     126             :                                 files_struct *fsp,
     127             :                                 DATA_BLOB *pblob)
     128             : {
     129         431 :         int ret;
     130      157692 :         int saved_errno = 0;
     131             : 
     132      157692 :         DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n",
     133             :                   (unsigned int)pblob->length, fsp_str_dbg(fsp)));
     134             : 
     135      157692 :         set_effective_capability(DAC_OVERRIDE_CAPABILITY);
     136      157692 :         ret = SMB_VFS_FSETXATTR(fsp, XATTR_NTACL_NAME,
     137             :                         pblob->data, pblob->length, 0);
     138      157692 :         if (ret) {
     139           0 :                 saved_errno = errno;
     140             :         }
     141      157692 :         drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
     142      157692 :         if (ret) {
     143           0 :                 DEBUG(5, ("store_acl_blob_fsp: setting attr failed for file %s"
     144             :                         "with error %s\n",
     145             :                         fsp_str_dbg(fsp),
     146             :                         strerror(saved_errno) ));
     147           0 :                 errno = saved_errno;
     148           0 :                 return map_nt_error_from_unix(saved_errno);
     149             :         }
     150      157692 :         return NT_STATUS_OK;
     151             : }
     152             : 
     153             : /*********************************************************************
     154             :  Remove a Windows ACL - we're setting the underlying POSIX ACL.
     155             : *********************************************************************/
     156             : 
     157      164254 : static int sys_acl_set_fd_xattr(vfs_handle_struct *handle,
     158             :                                 files_struct *fsp,
     159             :                                 SMB_ACL_TYPE_T type,
     160             :                                 SMB_ACL_T theacl)
     161             : {
     162         515 :         struct acl_common_fsp_ext *ext = (struct acl_common_fsp_ext *)
     163      164254 :                 VFS_FETCH_FSP_EXTENSION(handle, fsp);
     164         515 :         int ret;
     165             : 
     166      164254 :         ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle,
     167             :                                           fsp,
     168             :                                           type,
     169             :                                           theacl);
     170      164254 :         if (ret == -1) {
     171           0 :                 return -1;
     172             :         }
     173             : 
     174      164254 :         if (ext != NULL && ext->setting_nt_acl) {
     175      163558 :                 return 0;
     176             :         }
     177             : 
     178         185 :         set_effective_capability(DAC_OVERRIDE_CAPABILITY);
     179         185 :         SMB_VFS_FREMOVEXATTR(fsp, XATTR_NTACL_NAME);
     180         185 :         drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
     181             : 
     182         185 :         return 0;
     183             : }
     184             : 
     185       52985 : static int connect_acl_xattr(struct vfs_handle_struct *handle,
     186             :                                 const char *service,
     187             :                                 const char *user)
     188             : {
     189       52985 :         const char *security_acl_xattr_name = NULL;
     190       52985 :         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
     191         851 :         bool ok;
     192       52985 :         struct acl_common_config *config = NULL;
     193             : 
     194       52985 :         if (ret < 0) {
     195          16 :                 return ret;
     196             :         }
     197             : 
     198       52969 :         ok = init_acl_common_config(handle, ACL_MODULE_NAME);
     199       52969 :         if (!ok) {
     200           0 :                 DBG_ERR("init_acl_common_config failed\n");
     201           0 :                 return -1;
     202             :         }
     203             : 
     204             :         /* Ensure we have the parameters correct if we're
     205             :          * using this module. */
     206       52969 :         DEBUG(2,("connect_acl_xattr: setting 'inherit acls = true' "
     207             :                 "'dos filemode = true' and "
     208             :                 "'force unknown acl user = true' for service %s\n",
     209             :                 service ));
     210             : 
     211       52969 :         lp_do_parameter(SNUM(handle->conn), "inherit acls", "true");
     212       52969 :         lp_do_parameter(SNUM(handle->conn), "dos filemode", "true");
     213       52969 :         lp_do_parameter(SNUM(handle->conn), "force unknown acl user", "true");
     214             : 
     215       52969 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     216             :                                 struct acl_common_config,
     217         851 :                                 return -1);
     218             : 
     219       52969 :         if (config->ignore_system_acls) {
     220         210 :                 mode_t create_mask = lp_create_mask(SNUM(handle->conn));
     221         210 :                 char *create_mask_str = NULL;
     222             : 
     223         210 :                 if ((create_mask & 0666) != 0666) {
     224         210 :                         create_mask |= 0666;
     225         210 :                         create_mask_str = talloc_asprintf(handle, "0%o",
     226             :                                                           create_mask);
     227         210 :                         if (create_mask_str == NULL) {
     228           0 :                                 DBG_ERR("talloc_asprintf failed\n");
     229           0 :                                 return -1;
     230             :                         }
     231             : 
     232         210 :                         DBG_NOTICE("setting 'create mask = %s'\n", create_mask_str);
     233             : 
     234         210 :                         lp_do_parameter (SNUM(handle->conn),
     235             :                                         "create mask", create_mask_str);
     236             : 
     237         210 :                         TALLOC_FREE(create_mask_str);
     238             :                 }
     239             : 
     240         210 :                 DBG_NOTICE("setting 'directory mask = 0777', "
     241             :                            "'store dos attributes = yes' and all "
     242             :                            "'map ...' options to 'no'\n");
     243             : 
     244         210 :                 lp_do_parameter(SNUM(handle->conn), "directory mask", "0777");
     245         210 :                 lp_do_parameter(SNUM(handle->conn), "map archive", "no");
     246         210 :                 lp_do_parameter(SNUM(handle->conn), "map hidden", "no");
     247         210 :                 lp_do_parameter(SNUM(handle->conn), "map readonly", "no");
     248         210 :                 lp_do_parameter(SNUM(handle->conn), "map system", "no");
     249         210 :                 lp_do_parameter(SNUM(handle->conn), "store dos attributes",
     250             :                                 "yes");
     251             :         }
     252             : 
     253       52969 :         security_acl_xattr_name = lp_parm_const_string(SNUM(handle->conn),
     254             :                                           "acl_xattr",
     255             :                                           "security_acl_name",
     256             :                                           NULL);
     257       52969 :         if (security_acl_xattr_name != NULL) {
     258          34 :                 config->security_acl_xattr_name = talloc_strdup(config, security_acl_xattr_name);
     259          34 :                 if (config->security_acl_xattr_name == NULL) {
     260           0 :                         return -1;
     261             :                 }
     262             :         }
     263             : 
     264       52118 :         return 0;
     265             : }
     266             : 
     267      148528 : static int acl_xattr_unlinkat(vfs_handle_struct *handle,
     268             :                         struct files_struct *dirfsp,
     269             :                         const struct smb_filename *smb_fname,
     270             :                         int flags)
     271             : {
     272         337 :         int ret;
     273             : 
     274      148528 :         if (flags & AT_REMOVEDIR) {
     275       10007 :                 ret = rmdir_acl_common(handle,
     276             :                                 dirfsp,
     277             :                                 smb_fname);
     278             :         } else {
     279      138521 :                 ret = unlink_acl_common(handle,
     280             :                                 dirfsp,
     281             :                                 smb_fname,
     282             :                                 flags);
     283             :         }
     284      148528 :         return ret;
     285             : }
     286             : 
     287      791027 : static NTSTATUS acl_xattr_fget_nt_acl(vfs_handle_struct *handle,
     288             :                                       files_struct *fsp,
     289             :                                       uint32_t security_info,
     290             :                                       TALLOC_CTX *mem_ctx,
     291             :                                       struct security_descriptor **ppdesc)
     292             : {
     293        1886 :         NTSTATUS status;
     294      791027 :         status = fget_nt_acl_common(fget_acl_blob, handle, fsp,
     295             :                                    security_info, mem_ctx, ppdesc);
     296      791027 :         return status;
     297             : }
     298             : 
     299      157694 : static NTSTATUS acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
     300             :                                       files_struct *fsp,
     301             :                                       uint32_t security_info_sent,
     302             :                                       const struct security_descriptor *psd)
     303             : {
     304         431 :         NTSTATUS status;
     305      157694 :         status = fset_nt_acl_common(fget_acl_blob, store_acl_blob_fsp,
     306             :                                     ACL_MODULE_NAME,
     307             :                                     handle, fsp, security_info_sent, psd);
     308      157694 :         return status;
     309             : }
     310             : 
     311             : struct acl_xattr_getxattrat_state {
     312             :         struct vfs_aio_state aio_state;
     313             :         ssize_t xattr_size;
     314             :         uint8_t *xattr_value;
     315             : };
     316             : 
     317             : static void acl_xattr_getxattrat_done(struct tevent_req *subreq);
     318             : 
     319           0 : static struct tevent_req *acl_xattr_getxattrat_send(
     320             :                                 TALLOC_CTX *mem_ctx,
     321             :                                 struct tevent_context *ev,
     322             :                                 struct vfs_handle_struct *handle,
     323             :                                 files_struct *dirfsp,
     324             :                                 const struct smb_filename *smb_fname,
     325             :                                 const char *xattr_name,
     326             :                                 size_t alloc_hint)
     327             : {
     328           0 :         struct tevent_req *req = NULL;
     329           0 :         struct tevent_req *subreq = NULL;
     330           0 :         struct acl_xattr_getxattrat_state *state = NULL;
     331           0 :         struct acl_common_config *config = NULL;
     332             : 
     333           0 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     334             :                                 struct acl_common_config,
     335           0 :                                 return NULL);
     336             : 
     337           0 :         req = tevent_req_create(mem_ctx, &state,
     338             :                                 struct acl_xattr_getxattrat_state);
     339           0 :         if (req == NULL) {
     340           0 :                 return NULL;
     341             :         }
     342             : 
     343           0 :         if (strequal(xattr_name, config->security_acl_xattr_name)) {
     344           0 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     345           0 :                 return tevent_req_post(req, ev);
     346             :         }
     347           0 :         if (config->security_acl_xattr_name != NULL &&
     348           0 :             strequal(xattr_name, XATTR_NTACL_NAME))
     349             :         {
     350           0 :                 xattr_name = config->security_acl_xattr_name;
     351             :         }
     352             : 
     353           0 :         subreq = SMB_VFS_NEXT_GETXATTRAT_SEND(state,
     354             :                                               ev,
     355             :                                               handle,
     356             :                                               dirfsp,
     357             :                                               smb_fname,
     358             :                                               xattr_name,
     359             :                                               alloc_hint);
     360           0 :         if (tevent_req_nomem(subreq, req)) {
     361           0 :                 return tevent_req_post(req, ev);
     362             :         }
     363           0 :         tevent_req_set_callback(subreq, acl_xattr_getxattrat_done, req);
     364             : 
     365           0 :         return req;
     366             : }
     367             : 
     368           0 : static void acl_xattr_getxattrat_done(struct tevent_req *subreq)
     369             : {
     370           0 :         struct tevent_req *req = tevent_req_callback_data(
     371             :                 subreq, struct tevent_req);
     372           0 :         struct acl_xattr_getxattrat_state *state = tevent_req_data(
     373             :                 req, struct acl_xattr_getxattrat_state);
     374             : 
     375           0 :         state->xattr_size = SMB_VFS_NEXT_GETXATTRAT_RECV(subreq,
     376             :                                                          &state->aio_state,
     377             :                                                          state,
     378             :                                                          &state->xattr_value);
     379           0 :         TALLOC_FREE(subreq);
     380           0 :         if (state->xattr_size == -1) {
     381           0 :                 tevent_req_error(req, state->aio_state.error);
     382           0 :                 return;
     383             :         }
     384             : 
     385           0 :         tevent_req_done(req);
     386             : }
     387             : 
     388           0 : static ssize_t acl_xattr_getxattrat_recv(struct tevent_req *req,
     389             :                                          struct vfs_aio_state *aio_state,
     390             :                                          TALLOC_CTX *mem_ctx,
     391             :                                          uint8_t **xattr_value)
     392             : {
     393           0 :         struct acl_xattr_getxattrat_state *state = tevent_req_data(
     394             :                 req, struct acl_xattr_getxattrat_state);
     395           0 :         ssize_t xattr_size;
     396             : 
     397           0 :         if (tevent_req_is_unix_error(req, &aio_state->error)) {
     398           0 :                 tevent_req_received(req);
     399           0 :                 return -1;
     400             :         }
     401             : 
     402           0 :         *aio_state = state->aio_state;
     403           0 :         xattr_size = state->xattr_size;
     404           0 :         if (xattr_value != NULL) {
     405           0 :                 *xattr_value = talloc_move(mem_ctx, &state->xattr_value);
     406             :         }
     407             : 
     408           0 :         tevent_req_received(req);
     409           0 :         return xattr_size;
     410             : }
     411             : 
     412     2342358 : static ssize_t acl_xattr_fgetxattr(struct vfs_handle_struct *handle,
     413             :                                    struct files_struct *fsp,
     414             :                                    const char *name,
     415             :                                    void *value,
     416             :                                    size_t size)
     417             : {
     418     2342358 :         struct acl_common_config *config = NULL;
     419             : 
     420     2342358 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     421             :                                 struct acl_common_config,
     422        5150 :                                 return -1);
     423             : 
     424     2342358 :         if (strequal(name, config->security_acl_xattr_name)) {
     425           0 :                 errno = EACCES;
     426           0 :                 return -1;
     427             :         }
     428     2342558 :         if (config->security_acl_xattr_name != NULL &&
     429         200 :             strequal(name, XATTR_NTACL_NAME))
     430             :         {
     431          96 :                 name = config->security_acl_xattr_name;
     432             :         }
     433             : 
     434     2342358 :         return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
     435             : }
     436             : 
     437      724030 : static ssize_t acl_xattr_flistxattr(struct vfs_handle_struct *handle,
     438             :                                     struct files_struct *fsp,
     439             :                                     char *listbuf,
     440             :                                     size_t bufsize)
     441             : {
     442      724030 :         struct acl_common_config *config = NULL;
     443        1071 :         ssize_t size;
     444      724030 :         char *p = NULL;
     445        1071 :         size_t nlen, consumed;
     446             : 
     447      724030 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     448             :                                 struct acl_common_config,
     449        1071 :                                 return -1);
     450             : 
     451      724030 :         size = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, listbuf, bufsize);
     452      724030 :         if (size < 0) {
     453           0 :                 return -1;
     454             :         }
     455             : 
     456      722959 :         p = listbuf;
     457     4157640 :         while (p - listbuf < size) {
     458     3433626 :                 nlen = strlen(p) + 1;
     459     3433626 :                 if (strequal(p, config->security_acl_xattr_name)) {
     460          16 :                         break;
     461             :                 }
     462     3433610 :                 p += nlen;
     463             :         }
     464      724030 :         if (p - listbuf >= size) {
     465             :                 /* No match */
     466      722943 :                 return size;
     467             :         }
     468             : 
     469             :         /*
     470             :          * The consumed helper variable just makes the math
     471             :          * a bit more digestible.
     472             :          */
     473          16 :         consumed = p - listbuf;
     474          16 :         if (consumed + nlen < size) {
     475             :                 /* If not the last name move, else just skip */
     476           6 :                 memmove(p, p + nlen, size - consumed - nlen);
     477             :         }
     478          16 :         size -= nlen;
     479             : 
     480          16 :         return size;
     481             : }
     482             : 
     483        1703 : static int acl_xattr_fremovexattr(struct vfs_handle_struct *handle,
     484             :                                   struct files_struct *fsp,
     485             :                                   const char *name)
     486             : {
     487        1703 :         struct acl_common_config *config = NULL;
     488             : 
     489        1703 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     490             :                                 struct acl_common_config,
     491         216 :                                 return -1);
     492             : 
     493        1703 :         if (strequal(name, config->security_acl_xattr_name)) {
     494           0 :                 errno = EACCES;
     495           0 :                 return -1;
     496             :         }
     497        1705 :         if (config->security_acl_xattr_name != NULL &&
     498           2 :             strequal(name, XATTR_NTACL_NAME))
     499             :         {
     500           0 :                 name = config->security_acl_xattr_name;
     501             :         }
     502             : 
     503        1703 :         return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
     504             : }
     505             : 
     506      315075 : static int acl_xattr_fsetxattr(struct vfs_handle_struct *handle,
     507             :                                struct files_struct *fsp,
     508             :                                const char *name,
     509             :                                const void *value,
     510             :                                size_t size,
     511             :                                int flags)
     512             : {
     513      315075 :         struct acl_common_config *config = NULL;
     514             : 
     515      315075 :         SMB_VFS_HANDLE_GET_DATA(handle, config,
     516             :                                 struct acl_common_config,
     517        1180 :                                 return -1);
     518             : 
     519      315075 :         if (strequal(name, config->security_acl_xattr_name)) {
     520           2 :                 errno = EACCES;
     521           2 :                 return -1;
     522             :         }
     523      315103 :         if (config->security_acl_xattr_name != NULL &&
     524          30 :             strequal(name, XATTR_NTACL_NAME))
     525             :         {
     526          14 :                 name = config->security_acl_xattr_name;
     527             :         }
     528             : 
     529      315073 :         return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
     530             : }
     531             : 
     532             : static struct vfs_fn_pointers vfs_acl_xattr_fns = {
     533             :         .connect_fn = connect_acl_xattr,
     534             :         .unlinkat_fn = acl_xattr_unlinkat,
     535             :         .fchmod_fn = fchmod_acl_module_common,
     536             :         .fget_nt_acl_fn = acl_xattr_fget_nt_acl,
     537             :         .fset_nt_acl_fn = acl_xattr_fset_nt_acl,
     538             :         .sys_acl_set_fd_fn = sys_acl_set_fd_xattr,
     539             :         .getxattrat_send_fn = acl_xattr_getxattrat_send,
     540             :         .getxattrat_recv_fn = acl_xattr_getxattrat_recv,
     541             :         .fgetxattr_fn = acl_xattr_fgetxattr,
     542             :         .flistxattr_fn = acl_xattr_flistxattr,
     543             :         .fremovexattr_fn = acl_xattr_fremovexattr,
     544             :         .fsetxattr_fn = acl_xattr_fsetxattr,
     545             : };
     546             : 
     547             : static_decl_vfs;
     548       28129 : NTSTATUS vfs_acl_xattr_init(TALLOC_CTX *ctx)
     549             : {
     550       28129 :         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_xattr",
     551             :                                 &vfs_acl_xattr_fns);
     552             : }

Generated by: LCOV version 1.14