LCOV - code coverage report
Current view: top level - source3/printing - nt_printing_tdb.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 21 238 8.8 %
Date: 2023-11-21 12:31:41 Functions: 1 8 12.5 %

          Line data    Source code
       1             : /*
       2             :  *  Unix SMB/CIFS implementation.
       3             :  *  RPC Pipe client / server routines
       4             :  *  Copyright (c) Andrew Tridgell              1992-2000,
       5             :  *  Copyright (c) Jean François Micouleau      1998-2000.
       6             :  *  Copyright (c) Gerald Carter                2002-2005.
       7             :  *  Copyright (c) Andreas Schneider            2010.
       8             :  *
       9             :  *  This program is free software; you can redistribute it and/or modify
      10             :  *  it under the terms of the GNU General Public License as published by
      11             :  *  the Free Software Foundation; either version 3 of the License, or
      12             :  *  (at your option) any later version.
      13             :  *
      14             :  *  This program is distributed in the hope that it will be useful,
      15             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :  *  GNU General Public License for more details.
      18             :  *
      19             :  *  You should have received a copy of the GNU General Public License
      20             :  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
      21             :  */
      22             : 
      23             : #include "includes.h"
      24             : #include "system/filesys.h"
      25             : #include "printing/nt_printing_tdb.h"
      26             : #include "librpc/gen_ndr/spoolss.h"
      27             : #include "librpc/gen_ndr/ndr_security.h"
      28             : #include "libcli/security/security.h"
      29             : #include "util_tdb.h"
      30             : #include "lib/util/string_wrappers.h"
      31             : 
      32             : #define FORMS_PREFIX "FORMS/"
      33             : #define DRIVERS_PREFIX "DRIVERS/"
      34             : #define PRINTERS_PREFIX "PRINTERS/"
      35             : #define SECDESC_PREFIX "SECDESC/"
      36             : 
      37             : #define NTDRIVERS_DATABASE_VERSION_1 1
      38             : #define NTDRIVERS_DATABASE_VERSION_2 2
      39             : #define NTDRIVERS_DATABASE_VERSION_3 3 /* little endian version of v2 */
      40             : #define NTDRIVERS_DATABASE_VERSION_4 4 /* fix generic bits in security descriptors */
      41             : #define NTDRIVERS_DATABASE_VERSION_5 5 /* normalize keys in ntprinters.tdb */
      42             : 
      43             : static TDB_CONTEXT *tdb_forms; /* used for forms files */
      44             : static TDB_CONTEXT *tdb_drivers; /* used for driver files */
      45             : static TDB_CONTEXT *tdb_printers; /* used for printers files */
      46             : 
      47             : /****************************************************************************
      48             :  generate a new TDB_DATA key for storing a printer
      49             : ****************************************************************************/
      50             : 
      51           0 : static TDB_DATA make_printer_tdbkey(TALLOC_CTX *ctx, const char *sharename )
      52             : {
      53           0 :         fstring share;
      54           0 :         char *keystr = NULL;
      55           0 :         TDB_DATA key;
      56             : 
      57           0 :         fstrcpy(share, sharename);
      58           0 :         (void)strlower_m(share);
      59             : 
      60           0 :         keystr = talloc_asprintf(ctx, "%s%s", PRINTERS_PREFIX, share);
      61           0 :         key = string_term_tdb_data(keystr ? keystr : "");
      62             : 
      63           0 :         return key;
      64             : }
      65             : 
      66             : /****************************************************************************
      67             :  generate a new TDB_DATA key for storing a printer security descriptor
      68             : ****************************************************************************/
      69             : 
      70           0 : static TDB_DATA make_printers_secdesc_tdbkey(TALLOC_CTX *ctx,
      71             :                                         const char* sharename  )
      72             : {
      73           0 :         fstring share;
      74           0 :         char *keystr = NULL;
      75           0 :         TDB_DATA key;
      76             : 
      77           0 :         fstrcpy(share, sharename );
      78           0 :         (void)strlower_m(share);
      79             : 
      80           0 :         keystr = talloc_asprintf(ctx, "%s%s", SECDESC_PREFIX, share);
      81           0 :         key = string_term_tdb_data(keystr ? keystr : "");
      82             : 
      83           0 :         return key;
      84             : }
      85             : 
      86             : /****************************************************************************
      87             :  Upgrade the tdb files to version 3
      88             : ****************************************************************************/
      89             : 
      90           0 : static bool upgrade_to_version_3(void)
      91             : {
      92           0 :         TDB_DATA kbuf, newkey, dbuf;
      93             : 
      94           0 :         DEBUG(0,("upgrade_to_version_3: upgrading print tdb's to version 3\n"));
      95             : 
      96           0 :         for (kbuf = tdb_firstkey(tdb_drivers); kbuf.dptr;
      97           0 :              newkey = tdb_nextkey(tdb_drivers, kbuf), free(kbuf.dptr), kbuf=newkey) {
      98             : 
      99           0 :                 dbuf = tdb_fetch(tdb_drivers, kbuf);
     100             : 
     101           0 :                 if (strncmp((const char *)kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) == 0) {
     102           0 :                         DEBUG(0,("upgrade_to_version_3:moving form\n"));
     103           0 :                         if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) {
     104           0 :                                 SAFE_FREE(dbuf.dptr);
     105           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to move form. Error (%s).\n", tdb_errorstr(tdb_forms)));
     106           0 :                                 return False;
     107             :                         }
     108           0 :                         if (tdb_delete(tdb_drivers, kbuf) != 0) {
     109           0 :                                 SAFE_FREE(dbuf.dptr);
     110           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers)));
     111           0 :                                 return False;
     112             :                         }
     113             :                 }
     114             : 
     115           0 :                 if (strncmp((const char *)kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) == 0) {
     116           0 :                         DEBUG(0,("upgrade_to_version_3:moving printer\n"));
     117           0 :                         if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
     118           0 :                                 SAFE_FREE(dbuf.dptr);
     119           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to move printer. Error (%s)\n", tdb_errorstr(tdb_printers)));
     120           0 :                                 return False;
     121             :                         }
     122           0 :                         if (tdb_delete(tdb_drivers, kbuf) != 0) {
     123           0 :                                 SAFE_FREE(dbuf.dptr);
     124           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers)));
     125           0 :                                 return False;
     126             :                         }
     127             :                 }
     128             : 
     129           0 :                 if (strncmp((const char *)kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) {
     130           0 :                         DEBUG(0,("upgrade_to_version_3:moving secdesc\n"));
     131           0 :                         if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
     132           0 :                                 SAFE_FREE(dbuf.dptr);
     133           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to move secdesc. Error (%s)\n", tdb_errorstr(tdb_printers)));
     134           0 :                                 return False;
     135             :                         }
     136           0 :                         if (tdb_delete(tdb_drivers, kbuf) != 0) {
     137           0 :                                 SAFE_FREE(dbuf.dptr);
     138           0 :                                 DEBUG(0,("upgrade_to_version_3: failed to delete secdesc. Error (%s)\n", tdb_errorstr(tdb_drivers)));
     139           0 :                                 return False;
     140             :                         }
     141             :                 }
     142             : 
     143           0 :                 SAFE_FREE(dbuf.dptr);
     144             :         }
     145             : 
     146           0 :         return True;
     147             : }
     148             : 
     149             : /*******************************************************************
     150             :  Fix an issue with security descriptors.  Printer sec_desc must
     151             :  use more than the generic bits that were previously used
     152             :  in <= 3.0.14a.  They must also have a owner and group SID assigned.
     153             :  Otherwise, any printers than have been migrated to a Windows
     154             :  host using printmig.exe will not be accessible.
     155             : *******************************************************************/
     156             : 
     157           0 : static int sec_desc_upg_fn( TDB_CONTEXT *the_tdb, TDB_DATA key,
     158             :                             TDB_DATA data, void *state )
     159             : {
     160           0 :         NTSTATUS status;
     161           0 :         struct sec_desc_buf *sd_orig = NULL;
     162           0 :         struct sec_desc_buf *sd_new, *sd_store;
     163           0 :         struct security_descriptor *sec, *new_sec;
     164           0 :         TALLOC_CTX *ctx = state;
     165           0 :         int result, i;
     166           0 :         size_t size_new_sec;
     167             : 
     168           0 :         if (!data.dptr || data.dsize == 0) {
     169           0 :                 return 0;
     170             :         }
     171             : 
     172           0 :         if ( strncmp((const char *) key.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX) ) != 0 ) {
     173           0 :                 return 0;
     174             :         }
     175             : 
     176             :         /* upgrade the security descriptor */
     177             : 
     178           0 :         status = unmarshall_sec_desc_buf(ctx, data.dptr, data.dsize, &sd_orig);
     179           0 :         if (!NT_STATUS_IS_OK(status)) {
     180             :                 /* delete bad entries */
     181           0 :                 DEBUG(0,("sec_desc_upg_fn: Failed to parse original sec_desc for %si.  Deleting....\n",
     182             :                         (const char *)key.dptr ));
     183           0 :                 tdb_delete( tdb_printers, key );
     184           0 :                 return 0;
     185             :         }
     186             : 
     187           0 :         if (!sd_orig) {
     188           0 :                 return 0;
     189             :         }
     190           0 :         sec = sd_orig->sd;
     191             : 
     192             :         /* is this even valid? */
     193             : 
     194           0 :         if ( !sec->dacl ) {
     195           0 :                 return 0;
     196             :         }
     197             : 
     198             :         /* update access masks */
     199             : 
     200           0 :         for ( i=0; i<sec->dacl->num_aces; i++ ) {
     201           0 :                 switch ( sec->dacl->aces[i].access_mask ) {
     202           0 :                         case (GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS):
     203           0 :                                 sec->dacl->aces[i].access_mask = PRINTER_ACE_PRINT;
     204           0 :                                 break;
     205             : 
     206           0 :                         case GENERIC_ALL_ACCESS:
     207           0 :                                 sec->dacl->aces[i].access_mask = PRINTER_ACE_FULL_CONTROL;
     208           0 :                                 break;
     209             : 
     210           0 :                         case READ_CONTROL_ACCESS:
     211           0 :                                 sec->dacl->aces[i].access_mask = PRINTER_ACE_MANAGE_DOCUMENTS;
     212             : 
     213           0 :                                 break;
     214           0 :                         default:        /* no change */
     215           0 :                                 break;
     216             :                 }
     217             :         }
     218             : 
     219             :         /* create a new struct security_descriptor with the appropriate owner and group SIDs */
     220             : 
     221           0 :         new_sec = make_sec_desc( ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE,
     222             :                                  &global_sid_Builtin_Administrators,
     223             :                                  &global_sid_Builtin_Administrators,
     224             :                                  NULL, NULL, &size_new_sec );
     225           0 :         if (!new_sec) {
     226           0 :                 return 0;
     227             :         }
     228           0 :         sd_new = make_sec_desc_buf( ctx, size_new_sec, new_sec );
     229           0 :         if (!sd_new) {
     230           0 :                 return 0;
     231             :         }
     232             : 
     233           0 :         if ( !(sd_store = sec_desc_merge_buf( ctx, sd_new, sd_orig )) ) {
     234           0 :                 DEBUG(0,("sec_desc_upg_fn: Failed to update sec_desc for %s\n", key.dptr ));
     235           0 :                 return 0;
     236             :         }
     237             : 
     238             :         /* store it back */
     239             : 
     240           0 :         status = marshall_sec_desc_buf(ctx, sd_store, &data.dptr, &data.dsize);
     241           0 :         if (!NT_STATUS_IS_OK(status)) {
     242           0 :                 DEBUG(0,("sec_desc_upg_fn: Failed to parse new sec_desc for %s\n", key.dptr ));
     243           0 :                 return 0;
     244             :         }
     245             : 
     246           0 :         result = tdb_store( tdb_printers, key, data, TDB_REPLACE );
     247             : 
     248             :         /* 0 to continue and non-zero to stop traversal */
     249             : 
     250           0 :         return (result != 0);
     251             : }
     252             : 
     253             : /*******************************************************************
     254             :  Upgrade the tdb files to version 4
     255             : *******************************************************************/
     256             : 
     257           0 : static bool upgrade_to_version_4(void)
     258             : {
     259           0 :         TALLOC_CTX *ctx;
     260           0 :         int result;
     261             : 
     262           0 :         DEBUG(0,("upgrade_to_version_4: upgrading printer security descriptors\n"));
     263             : 
     264           0 :         if ( !(ctx = talloc_init( "upgrade_to_version_4" )) )
     265           0 :                 return False;
     266             : 
     267           0 :         result = tdb_traverse( tdb_printers, sec_desc_upg_fn, ctx );
     268             : 
     269           0 :         talloc_destroy( ctx );
     270             : 
     271           0 :         return ( result >= 0 );
     272             : }
     273             : 
     274             : /*******************************************************************
     275             :  Fix an issue with security descriptors.  Printer sec_desc must
     276             :  use more than the generic bits that were previously used
     277             :  in <= 3.0.14a.  They must also have a owner and group SID assigned.
     278             :  Otherwise, any printers than have been migrated to a Windows
     279             :  host using printmig.exe will not be accessible.
     280             : *******************************************************************/
     281             : 
     282           0 : static int normalize_printers_fn( TDB_CONTEXT *the_tdb, TDB_DATA key,
     283             :                                   TDB_DATA data, void *state )
     284             : {
     285           0 :         TALLOC_CTX *ctx = talloc_tos();
     286           0 :         TDB_DATA new_key;
     287             : 
     288           0 :         if (!data.dptr || data.dsize == 0)
     289           0 :                 return 0;
     290             : 
     291             :         /* upgrade printer records and security descriptors */
     292             : 
     293           0 :         if ( strncmp((const char *) key.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX) ) == 0 ) {
     294           0 :                 new_key = make_printer_tdbkey(ctx, (const char *)key.dptr+strlen(PRINTERS_PREFIX) );
     295             :         }
     296           0 :         else if ( strncmp((const char *) key.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX) ) == 0 ) {
     297           0 :                 new_key = make_printers_secdesc_tdbkey(ctx, (const char *)key.dptr+strlen(SECDESC_PREFIX) );
     298             :         }
     299             :         else {
     300             :                 /* ignore this record */
     301           0 :                 return 0;
     302             :         }
     303             : 
     304             :         /* delete the original record and store under the normalized key */
     305             : 
     306           0 :         if ( tdb_delete( the_tdb, key ) != 0 ) {
     307           0 :                 DEBUG(0,("normalize_printers_fn: tdb_delete for [%s] failed!\n",
     308             :                         key.dptr));
     309           0 :                 return 1;
     310             :         }
     311             : 
     312           0 :         if ( tdb_store( the_tdb, new_key, data, TDB_REPLACE) != 0 ) {
     313           0 :                 DEBUG(0,("normalize_printers_fn: failed to store new record for [%s]!\n",
     314             :                         key.dptr));
     315           0 :                 return 1;
     316             :         }
     317             : 
     318           0 :         return 0;
     319             : }
     320             : 
     321             : /*******************************************************************
     322             :  Upgrade the tdb files to version 5
     323             : *******************************************************************/
     324             : 
     325           0 : static bool upgrade_to_version_5(void)
     326             : {
     327           0 :         TALLOC_CTX *ctx;
     328           0 :         int result;
     329             : 
     330           0 :         DEBUG(0,("upgrade_to_version_5: normalizing printer keys\n"));
     331             : 
     332           0 :         if ( !(ctx = talloc_init( "upgrade_to_version_5" )) )
     333           0 :                 return False;
     334             : 
     335           0 :         result = tdb_traverse( tdb_printers, normalize_printers_fn, NULL );
     336             : 
     337           0 :         talloc_destroy( ctx );
     338             : 
     339           0 :         return ( result >= 0 );
     340             : }
     341             : 
     342          28 : bool nt_printing_tdb_upgrade(void)
     343             : {
     344           0 :         char *drivers_path;
     345           0 :         char *printers_path;
     346           0 :         char *forms_path;
     347           0 :         bool drivers_exists;
     348           0 :         bool printers_exists;
     349           0 :         bool forms_exists;
     350          28 :         const char *vstring = "INFO/version";
     351           0 :         int32_t vers_id;
     352           0 :         bool ret;
     353             : 
     354          28 :         drivers_path = state_path(talloc_tos(), "ntdrivers.tdb");
     355          28 :         if (drivers_path == NULL) {
     356           0 :                 ret = false;
     357           0 :                 goto err_out;
     358             :         }
     359          28 :         printers_path = state_path(talloc_tos(), "ntprinters.tdb");
     360          28 :         if (printers_path == NULL) {
     361           0 :                 ret = false;
     362           0 :                 goto err_drvdb_free;
     363             :         }
     364          28 :         forms_path = state_path(talloc_tos(), "ntforms.tdb");
     365          28 :         if (forms_path == NULL) {
     366           0 :                 ret = false;
     367           0 :                 goto err_prdb_free;
     368             :         }
     369             : 
     370          28 :         drivers_exists = file_exist(drivers_path);
     371          28 :         printers_exists = file_exist(printers_path);
     372          28 :         forms_exists = file_exist(forms_path);
     373             : 
     374          28 :         if (!drivers_exists && !printers_exists && !forms_exists) {
     375          28 :                 ret = true;
     376          28 :                 goto err_formsdb_free;
     377             :         }
     378             : 
     379           0 :         tdb_drivers = tdb_open_log(drivers_path,
     380             :                                    0,
     381             :                                    TDB_DEFAULT,
     382             :                                    O_RDWR|O_CREAT,
     383             :                                    0600);
     384           0 :         if (tdb_drivers == NULL) {
     385           0 :                 DEBUG(0,("nt_printing_init: Failed to open nt drivers "
     386             :                          "database %s (%s)\n",
     387             :                          drivers_path, strerror(errno)));
     388           0 :                 ret = false;
     389           0 :                 goto err_formsdb_free;
     390             :         }
     391             : 
     392           0 :         tdb_printers = tdb_open_log(printers_path,
     393             :                                     0,
     394             :                                     TDB_DEFAULT,
     395             :                                     O_RDWR|O_CREAT,
     396             :                                     0600);
     397           0 :         if (tdb_printers == NULL) {
     398           0 :                 DEBUG(0,("nt_printing_init: Failed to open nt printers "
     399             :                          "database %s (%s)\n",
     400             :                          printers_path, strerror(errno)));
     401           0 :                 ret = false;
     402           0 :                 goto err_drvdb_close;
     403             :         }
     404             : 
     405           0 :         tdb_forms = tdb_open_log(forms_path,
     406             :                                  0,
     407             :                                  TDB_DEFAULT,
     408             :                                  O_RDWR|O_CREAT,
     409             :                                  0600);
     410           0 :         if (tdb_forms == NULL) {
     411           0 :                 DEBUG(0,("nt_printing_init: Failed to open nt forms "
     412             :                          "database %s (%s)\n",
     413             :                          forms_path, strerror(errno)));
     414           0 :                 ret = false;
     415           0 :                 goto err_prdb_close;
     416             :         }
     417             : 
     418             :         /* Samba upgrade */
     419           0 :         vers_id = tdb_fetch_int32(tdb_drivers, vstring);
     420           0 :         if (vers_id == -1) {
     421           0 :                 DEBUG(10, ("Fresh database\n"));
     422           0 :                 tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_5);
     423           0 :                 vers_id = NTDRIVERS_DATABASE_VERSION_5;
     424             :         }
     425             : 
     426           0 :         if (vers_id != NTDRIVERS_DATABASE_VERSION_5) {
     427           0 :                 if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) ||
     428           0 :                     (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) {
     429           0 :                         if (!upgrade_to_version_3()) {
     430           0 :                                 ret = false;
     431           0 :                                 goto err_formsdb_close;
     432             :                         }
     433             : 
     434           0 :                         tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_3);
     435           0 :                         vers_id = NTDRIVERS_DATABASE_VERSION_3;
     436             :                 }
     437             : 
     438           0 :                 if ((vers_id == NTDRIVERS_DATABASE_VERSION_2) ||
     439           0 :                     (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_2)) {
     440             :                         /*
     441             :                          * Written on a bigendian machine with old fetch_int
     442             :                          * code. Save as le. The only upgrade between V2 and V3
     443             :                          * is to save the version in little-endian.
     444             :                          */
     445           0 :                         tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_3);
     446           0 :                         vers_id = NTDRIVERS_DATABASE_VERSION_3;
     447             :                 }
     448             : 
     449           0 :                 if (vers_id == NTDRIVERS_DATABASE_VERSION_3) {
     450           0 :                         if (!upgrade_to_version_4()) {
     451           0 :                                 ret = false;
     452           0 :                                 goto err_formsdb_close;
     453             :                         }
     454           0 :                         tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_4);
     455           0 :                         vers_id = NTDRIVERS_DATABASE_VERSION_4;
     456             :                 }
     457             : 
     458           0 :                 if (vers_id == NTDRIVERS_DATABASE_VERSION_4 ) {
     459           0 :                         if (!upgrade_to_version_5()) {
     460           0 :                                 ret = false;
     461           0 :                                 goto err_formsdb_close;
     462             :                         }
     463           0 :                         tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION_5);
     464           0 :                         vers_id = NTDRIVERS_DATABASE_VERSION_5;
     465             :                 }
     466             : 
     467           0 :                 if (vers_id != NTDRIVERS_DATABASE_VERSION_5) {
     468           0 :                         DEBUG(0,("nt_printing_init: Unknown printer database version [%d]\n", vers_id));
     469           0 :                         ret = false;
     470           0 :                         goto err_formsdb_close;
     471             :                 }
     472             :         }
     473           0 :         ret = true;
     474             : 
     475           0 : err_formsdb_close:
     476           0 :         if (tdb_forms) {
     477           0 :                 tdb_close(tdb_forms);
     478           0 :                 tdb_forms = NULL;
     479             :         }
     480           0 : err_prdb_close:
     481           0 :         if (tdb_printers) {
     482           0 :                 tdb_close(tdb_printers);
     483           0 :                 tdb_printers = NULL;
     484             :         }
     485           0 : err_drvdb_close:
     486           0 :         if (tdb_drivers) {
     487           0 :                 tdb_close(tdb_drivers);
     488           0 :                 tdb_drivers = NULL;
     489             :         }
     490           0 : err_formsdb_free:
     491          28 :         talloc_free(forms_path);
     492          28 : err_prdb_free:
     493          28 :         talloc_free(printers_path);
     494          28 : err_drvdb_free:
     495          28 :         talloc_free(drivers_path);
     496          28 : err_out:
     497          28 :         return ret;
     498             : }

Generated by: LCOV version 1.14