LCOV - code coverage report
Current view: top level - src/plugins/resolver - resolver.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 287 384 74.7 %
Date: 2019-09-12 12:28:41 Functions: 53 92 57.6 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "resolver.h"
      10             : 
      11             : #include <kdbassert.h>
      12             : #include <kdbconfig.h>
      13             : #include <kdbhelper.h>  // elektraStrDup
      14             : #include <kdbprivate.h> // KDB_CACHE_PREFIX
      15             : #include <kdbproposal.h>
      16             : 
      17             : #include "kdbos.h"
      18             : 
      19             : #include <stdlib.h>
      20             : 
      21             : #ifdef HAVE_CTYPE_H
      22             : #include <ctype.h>
      23             : #endif
      24             : 
      25             : /* Needs posix */
      26             : #include <errno.h>
      27             : #include <fcntl.h>
      28             : #include <stdio.h>
      29             : #include <string.h>
      30             : #include <sys/stat.h>
      31             : #include <sys/time.h>
      32             : #include <unistd.h>
      33             : 
      34             : #include <dirent.h>
      35             : 
      36             : #include <kdberrors.h>
      37             : #include <kdblogger.h>
      38             : #include <kdbmacros.h>
      39             : 
      40             : #ifdef ELEKTRA_LOCK_MUTEX
      41             : #include <pthread.h>
      42             : #endif
      43             : 
      44             : #ifdef ELEKTRA_LOCK_MUTEX
      45             : #if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
      46             : static pthread_mutex_t elektraResolverMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
      47             : #elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
      48             : static pthread_mutex_t elektraResolverMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
      49             : #else
      50             : static pthread_mutex_t elektraResolverMutex;
      51             : static pthread_mutex_t elektraResolverInitMutex = PTHREAD_MUTEX_INITIALIZER;
      52             : static unsigned char elektraResolverMutexInitialized = 0;
      53             : #define ELEKTRA_RESOLVER_RECURSIVE_MUTEX_INITIALIZATION
      54             : #endif
      55             : #endif
      56             : 
      57             : static void resolverInit (resolverHandle * p, const char * path)
      58             : {
      59      171060 :         p->fd = -1;
      60      171060 :         p->mtime.tv_sec = 0;
      61      171060 :         p->mtime.tv_nsec = 0;
      62       85530 :         p->filemode = KDB_FILE_MODE;
      63       85530 :         p->dirmode = KDB_FILE_MODE | KDB_DIR_MODE;
      64      171060 :         p->removalNeeded = 0;
      65      171060 :         p->isMissing = 0;
      66      171060 :         p->timeFix = 1;
      67             : 
      68      171060 :         p->filename = 0;
      69      171060 :         p->dirname = 0;
      70      171060 :         p->tempfile = 0;
      71             : 
      72      171060 :         p->path = path;
      73             : 
      74      171060 :         p->uid = 0;
      75      171060 :         p->gid = 0;
      76             : }
      77             : 
      78       66355 : static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey)
      79             : {
      80       66355 :         resolverHandles * pks = elektraPluginGetData (handle);
      81       66355 :         ELEKTRA_ASSERT (pks != NULL, "Unable to retrieve plugin data for handle %p with parentKey %s", (void *) handle,
      82             :                         keyName (parentKey));
      83             : 
      84       66355 :         switch (keyGetNamespace (parentKey))
      85             :         {
      86             :         case KEY_NS_SPEC:
      87       12008 :                 return &pks->spec;
      88             :         case KEY_NS_DIR:
      89       11866 :                 return &pks->dir;
      90             :         case KEY_NS_USER:
      91       15501 :                 return &pks->user;
      92             :         case KEY_NS_SYSTEM:
      93       26980 :                 return &pks->system;
      94             :         case KEY_NS_PROC:
      95             :         case KEY_NS_EMPTY:
      96             :         case KEY_NS_NONE:
      97             :         case KEY_NS_META:
      98             :         case KEY_NS_CASCADING:
      99             :                 return 0;
     100             :         }
     101             : 
     102             :         return 0;
     103             : }
     104             : 
     105             : 
     106      171060 : static void resolverCloseOne (resolverHandle * p)
     107             : {
     108      171060 :         elektraFree (p->filename);
     109      171060 :         p->filename = 0;
     110      171060 :         elektraFree (p->dirname);
     111      171060 :         p->dirname = 0;
     112      171060 :         elektraFree (p->tempfile);
     113      171060 :         p->tempfile = 0;
     114      171060 : }
     115             : 
     116       42765 : static void resolverClose (resolverHandles * p)
     117             : {
     118       42765 :         resolverCloseOne (&p->spec);
     119       42765 :         resolverCloseOne (&p->dir);
     120       42765 :         resolverCloseOne (&p->user);
     121       42765 :         resolverCloseOne (&p->system);
     122       42765 :         elektraFree (p);
     123       42765 : }
     124             : 
     125             : /**
     126             :  * Locks file for exclusive read/write mode.
     127             :  *
     128             :  * This function will not block until all reader
     129             :  * and writer have left the file.
     130             :  * -> conflict with other cooperative process detected,
     131             :  *    but we were later (and lost)
     132             :  *
     133             :  * @exception 27 set if locking failed, most likely a conflict
     134             :  *
     135             :  * @param fd is a valid filedescriptor
     136             :  * @retval 0 on success
     137             :  * @retval -1 on failure
     138             :  * @ingroup backendhelper
     139             :  */
     140        4816 : static int elektraLockFile (int fd ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     141             : {
     142             : #ifdef ELEKTRA_LOCK_FILE
     143             :         struct flock l;
     144        4816 :         l.l_type = F_WRLCK; /*Do exclusive Lock*/
     145        4816 :         l.l_start = 0;      /*Start at begin*/
     146        4816 :         l.l_whence = SEEK_SET;
     147        4816 :         l.l_len = 0; /*Do it with whole file*/
     148        4816 :         int ret = fcntl (fd, F_SETLK, &l);
     149             : 
     150        4816 :         if (ret == -1)
     151             :         {
     152           0 :                 if (errno == EAGAIN || errno == EACCES)
     153             :                 {
     154           0 :                         ELEKTRA_SET_RESOURCE_ERROR (parentKey,
     155             :                                                     "Conflict because other process writes to configuration indicated by file lock");
     156             :                 }
     157             :                 else
     158             :                 {
     159           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Assuming conflict because of failed file lock. Reason: %s",
     160             :                                                      strerror (errno));
     161             :                 }
     162             :                 return -1;
     163             :         }
     164             : 
     165             :         return ret;
     166             : #else
     167             :         return 0;
     168             : #endif
     169             : }
     170             : 
     171             : 
     172             : /**
     173             :  * Unlocks file.
     174             :  *
     175             :  * @param fd is a valid filedescriptor
     176             :  * @retval 0 on success
     177             :  * @retval -1 on failure
     178             :  * @ingroup backendhelper
     179             :  */
     180        4816 : static int elektraUnlockFile (int fd ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     181             : {
     182             : #ifdef ELEKTRA_LOCK_FILE
     183             :         struct flock l;
     184        4816 :         l.l_type = F_UNLCK; /*Give Lock away*/
     185        4816 :         l.l_start = 0;      /*Start at begin*/
     186        4816 :         l.l_whence = SEEK_SET;
     187        4816 :         l.l_len = 0; /*Do it with whole file*/
     188        4816 :         int ret = fcntl (fd, F_SETLK, &l);
     189             : 
     190        4816 :         if (ret == -1)
     191             :         {
     192           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Method 'fcntl' unlocking failed (SETLK). Reason: %s", strerror (errno));
     193             :         }
     194             : 
     195        4816 :         return ret;
     196             : #else
     197             :         return 0;
     198             : #endif
     199             : }
     200             : 
     201             : /**
     202             :  * @brief mutex lock for multithread-safety
     203             :  *
     204             :  * @retval 0 on success
     205             :  * @retval -1 on error
     206             :  */
     207        2445 : static int elektraLockMutex (Key * parentKey ELEKTRA_UNUSED)
     208             : {
     209             : #ifdef ELEKTRA_LOCK_MUTEX
     210        2445 :         int ret = pthread_mutex_trylock (&elektraResolverMutex);
     211        2445 :         if (ret != 0)
     212             :         {
     213           0 :                 if (errno == EBUSY       // for trylock
     214           0 :                     || errno == EDEADLK) // for error checking mutex, if enabled
     215             :                 {
     216           0 :                         ELEKTRA_SET_CONFLICTING_STATE_ERROR (
     217             :                                 parentKey, "Conflict because other thread writes to configuration indicated by mutex lock");
     218             :                 }
     219             :                 else
     220             :                 {
     221           0 :                         ELEKTRA_SET_CONFLICTING_STATE_ERRORF (parentKey, "Assuming conflict because of failed mutex lock. Reason: %s",
     222             :                                                               strerror (errno));
     223             :                 }
     224             :                 return -1;
     225             :         }
     226             :         return 0;
     227             : #else
     228             :         return 0;
     229             : #endif
     230             : }
     231             : 
     232             : /**
     233             :  * @brief mutex unlock for multithread-safety
     234             :  *
     235             :  * @retval 0 on success
     236             :  * @retval -1 on error
     237             :  */
     238        2445 : static int elektraUnlockMutex (Key * parentKey ELEKTRA_UNUSED)
     239             : {
     240             : #ifdef ELEKTRA_LOCK_MUTEX
     241        2445 :         int ret = pthread_mutex_unlock (&elektraResolverMutex);
     242        2445 :         if (ret != 0)
     243             :         {
     244           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Mutex unlock failed. Reason: %s", strerror (errno));
     245           0 :                 return -1;
     246             :         }
     247             :         return 0;
     248             : #else
     249             :         return 0;
     250             : #endif
     251             : }
     252             : 
     253             : 
     254             : /**
     255             :  * @brief Close a file
     256             :  *
     257             :  * @param fd the filedescriptor to close
     258             :  * @param parentKey the key to write warnings to
     259             :  */
     260        4816 : static void elektraCloseFile (int fd, Key * parentKey)
     261             : {
     262        4816 :         if (close (fd) == -1)
     263             :         {
     264           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Close file failed. Reason: %s", strerror (errno));
     265             :         }
     266        4816 : }
     267             : 
     268             : /**
     269             :  * @brief Add error text received from strerror
     270             :  *
     271             :  * @param errorText should have at least ERROR_SIZE bytes in reserve
     272             :  */
     273           0 : static char * elektraAddErrnoText (void)
     274             : {
     275           0 :         if (errno == E2BIG)
     276             :         {
     277             :                 return "could not find a / in the pathname";
     278             :         }
     279           0 :         else if (errno == EINVAL)
     280             :         {
     281             :                 return "went up to root for creating directory";
     282             :         }
     283             :         else
     284             :         {
     285           0 :                 return strerror (errno);
     286             :         }
     287             : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
     288             : #pragma GCC diagnostic push
     289             : #pragma GCC diagnostic ignored "-Wstringop-truncation"
     290             : #endif
     291             : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
     292             : #pragma GCC diagnostic pop
     293             : #endif
     294             : }
     295             : 
     296      171060 : static int needsMapping (Key * testKey, Key * errorKey)
     297             : {
     298      171060 :         elektraNamespace ns = keyGetNamespace (errorKey);
     299             : 
     300      171060 :         if (ns == KEY_NS_NONE) return 1;      // for unit tests
     301      170852 :         if (ns == KEY_NS_EMPTY) return 1;     // for default backend
     302       27844 :         if (ns == KEY_NS_CASCADING) return 1; // init all namespaces for cascading
     303             : 
     304       15924 :         return ns == keyGetNamespace (testKey); // otherwise only init if same ns
     305             : }
     306             : 
     307       42765 : static int mapFilesForNamespaces (resolverHandles * p, Key * errorKey)
     308             : {
     309       42765 :         Key * testKey = keyNew ("", KEY_END);
     310             :         // switch is only present to forget no namespace and to get
     311             :         // a warning whenever a new namespace is present.
     312             :         // In fact its linear code executed:
     313       42765 :         ElektraResolved * resolved = NULL;
     314             :         switch (KEY_NS_SPEC)
     315             :         {
     316             :         case KEY_NS_SPEC:
     317       42765 :                 keySetName (testKey, "spec");
     318       42765 :                 if (needsMapping (testKey, errorKey))
     319             :                 {
     320       40093 :                         if ((resolved = ELEKTRA_PLUGIN_FUNCTION (filename) (KEY_NS_SPEC, (p->spec).path, ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR,
     321             :                                                                             errorKey)) == NULL)
     322             :                         {
     323           0 :                                 resolverClose (p);
     324           0 :                                 keyDel (testKey);
     325           0 :                                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not resolve filename. Could not resolve spec key");
     326           0 :                                 return -1;
     327             :                         }
     328             :                         else
     329             :                         {
     330       40093 :                                 p->spec.tempfile = elektraStrDup (resolved->tmpFile);
     331       40093 :                                 p->spec.filename = elektraStrDup (resolved->fullPath);
     332       40093 :                                 p->spec.dirname = elektraStrDup (resolved->dirname);
     333       40093 :                                 ELEKTRA_PLUGIN_FUNCTION (freeHandle) (resolved);
     334             :                         }
     335             :                 }
     336             :                 // FALLTHROUGH
     337             : 
     338             :         case KEY_NS_DIR:
     339       42765 :                 keySetName (testKey, "dir");
     340       42765 :                 if (needsMapping (testKey, errorKey))
     341             :                 {
     342       38870 :                         if ((resolved = ELEKTRA_PLUGIN_FUNCTION (filename) (KEY_NS_DIR, (p->dir).path, ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR,
     343             :                                                                             errorKey)) == NULL)
     344             :                         {
     345           0 :                                 resolverClose (p);
     346           0 :                                 keyDel (testKey);
     347           0 :                                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not resolve filename. Could not resolve dir key");
     348           0 :                                 return -1;
     349             :                         }
     350             :                         else
     351             :                         {
     352       38870 :                                 p->dir.tempfile = elektraStrDup (resolved->tmpFile);
     353       38870 :                                 p->dir.filename = elektraStrDup (resolved->fullPath);
     354       38870 :                                 p->dir.dirname = elektraStrDup (resolved->dirname);
     355       38870 :                                 ELEKTRA_PLUGIN_FUNCTION (freeHandle) (resolved);
     356             :                         }
     357             :                 }
     358             :         // FALLTHROUGH
     359             :         case KEY_NS_USER:
     360       42765 :                 keySetName (testKey, "user");
     361       42765 :                 if (needsMapping (testKey, errorKey))
     362             :                 {
     363       40458 :                         if ((resolved = ELEKTRA_PLUGIN_FUNCTION (filename) (KEY_NS_USER, (p->user).path, ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR,
     364             :                                                                             errorKey)) == NULL)
     365             :                         {
     366           0 :                                 resolverClose (p);
     367           0 :                                 keyDel (testKey);
     368           0 :                                 ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Could not resolve user key with configuration %s",
     369             :                                                              ELEKTRA_VARIANT_USER);
     370           0 :                                 return -1;
     371             :                         }
     372             :                         else
     373             :                         {
     374       40458 :                                 p->user.tempfile = elektraStrDup (resolved->tmpFile);
     375       40458 :                                 p->user.filename = elektraStrDup (resolved->fullPath);
     376       40458 :                                 p->user.dirname = elektraStrDup (resolved->dirname);
     377       40458 :                                 ELEKTRA_PLUGIN_FUNCTION (freeHandle) (resolved);
     378             :                         }
     379             :                 }
     380             :         // FALLTHROUGH
     381             :         case KEY_NS_SYSTEM:
     382       42765 :                 keySetName (testKey, "system");
     383       42765 :                 if (needsMapping (testKey, errorKey))
     384             :                 {
     385       39696 :                         if ((resolved = ELEKTRA_PLUGIN_FUNCTION (filename) (KEY_NS_SYSTEM, (p->system).path,
     386             :                                                                             ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR, errorKey)) == NULL)
     387             :                         {
     388           0 :                                 resolverClose (p);
     389           0 :                                 keyDel (testKey);
     390           0 :                                 ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Could not resolve system key with configuration %s",
     391             :                                                              ELEKTRA_VARIANT_SYSTEM);
     392           0 :                                 return -1;
     393             :                         }
     394             :                         else
     395             :                         {
     396       39696 :                                 p->system.tempfile = elektraStrDup (resolved->tmpFile);
     397       39696 :                                 p->system.filename = elektraStrDup (resolved->fullPath);
     398       39696 :                                 p->system.dirname = elektraStrDup (resolved->dirname);
     399       39696 :                                 ELEKTRA_PLUGIN_FUNCTION (freeHandle) (resolved);
     400             :                         }
     401             :                 }
     402             :         // FALLTHROUGH
     403             :         case KEY_NS_PROC:
     404             :         case KEY_NS_EMPTY:
     405             :         case KEY_NS_NONE:
     406             :         case KEY_NS_META:
     407             :         case KEY_NS_CASCADING:
     408             :                 break;
     409             :         }
     410       42765 :         keyDel (testKey);
     411       42765 :         return 0;
     412             : }
     413             : 
     414             : /**
     415             :  * @brief Generate key name for the cache
     416             :  *
     417             :  * @param filename the name of the config file
     418             :  * @ret pointer to the generated key name
     419             :  */
     420       17734 : static char * elektraCacheKeyName (char * filename)
     421             : {
     422       17734 :         char * name = 0;
     423       17734 :         size_t len = strlen (KDB_CACHE_PREFIX) + strlen ("/") + strlen (ELEKTRA_PLUGIN_NAME) + strlen (filename) + 1;
     424       17734 :         name = elektraMalloc (len);
     425       17734 :         name = strcpy (name, KDB_CACHE_PREFIX);
     426       17734 :         name = strcat (name, "/");
     427       17734 :         name = strcat (name, ELEKTRA_PLUGIN_NAME);
     428       17734 :         name = strcat (name, filename);
     429             : 
     430             :         ELEKTRA_LOG_DEBUG ("persistent chid key: %s", name);
     431       17734 :         return name;
     432             : }
     433             : 
     434       57789 : int ELEKTRA_PLUGIN_FUNCTION (open) (Plugin * handle, Key * errorKey)
     435             : {
     436       57789 :         KeySet * resolverConfig = elektraPluginGetConfig (handle);
     437       57789 :         if (ksLookupByName (resolverConfig, "/module", 0)) return 0;
     438       42765 :         const char * path = keyString (ksLookupByName (resolverConfig, "/path", 0));
     439             : 
     440       42765 :         if (!path)
     441             :         {
     442           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not find file configuration");
     443           0 :                 return -1;
     444             :         }
     445             : 
     446       42765 :         resolverHandles * p = elektraMalloc (sizeof (resolverHandles));
     447       85530 :         resolverInit (&p->spec, path);
     448       85530 :         resolverInit (&p->dir, path);
     449       85530 :         resolverInit (&p->user, path);
     450       85530 :         resolverInit (&p->system, path);
     451             : 
     452             : #if defined(ELEKTRA_RESOLVER_RECURSIVE_MUTEX_INITIALIZATION)
     453             :         // PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP is available in glibc only
     454             :         // so we use another mutex for the initialization of the recursive mutex,
     455             :         // since this section must be thread safe.
     456             :         pthread_mutex_lock (&elektraResolverInitMutex);
     457             :         if (!elektraResolverMutexInitialized)
     458             :         {
     459             :                 pthread_mutexattr_t mutexAttr;
     460             :                 int mutexError;
     461             : 
     462             :                 if ((mutexError = pthread_mutexattr_init (&mutexAttr)) != 0)
     463             :                 {
     464             :                         ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Could not initialize recursive mutex: pthread_mutexattr_init returned %d",
     465             :                                                      mutexError);
     466             :                         pthread_mutex_unlock (&elektraResolverInitMutex);
     467             :                         return -1;
     468             :                 }
     469             :                 if ((mutexError = pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE)) != 0)
     470             :                 {
     471             :                         ELEKTRA_SET_RESOURCE_ERRORF (
     472             :                                 errorKey, "Could not initialize recursive mutex: pthread_mutexattr_settype returned %d", mutexError);
     473             :                         pthread_mutex_unlock (&elektraResolverInitMutex);
     474             :                         return -1;
     475             :                 }
     476             :                 if ((mutexError = pthread_mutex_init (&elektraResolverMutex, &mutexAttr)) != 0)
     477             :                 {
     478             :                         ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Could not initialize recursive mutex: pthread_mutex_init returned %d",
     479             :                                                      mutexError);
     480             :                         pthread_mutex_unlock (&elektraResolverInitMutex);
     481             :                         return -1;
     482             :                 }
     483             :                 elektraResolverMutexInitialized = 1;
     484             :         }
     485             :         pthread_mutex_unlock (&elektraResolverInitMutex);
     486             : #endif
     487             : 
     488             :         // system and spec files need to be world-readable, otherwise they are
     489             :         // useless
     490       42765 :         p->system.filemode = 0644;
     491       42765 :         p->system.dirmode = 0755;
     492       42765 :         p->spec.filemode = 0644;
     493       42765 :         p->spec.dirmode = 0755;
     494             : 
     495       42765 :         int ret = mapFilesForNamespaces (p, errorKey);
     496             : 
     497       42765 :         if (ret != -1)
     498             :         {
     499       42765 :                 elektraPluginSetData (handle, p);
     500             :         }
     501             : 
     502             :         return ret;
     503             : }
     504             : 
     505       57791 : int ELEKTRA_PLUGIN_FUNCTION (close) (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     506             : {
     507       57791 :         resolverHandles * ps = elektraPluginGetData (handle);
     508             : 
     509       57791 :         if (ps)
     510             :         {
     511       42765 :                 resolverClose (ps);
     512       42765 :                 elektraPluginSetData (handle, 0);
     513             :         }
     514             : 
     515       57791 :         return 0; /* success */
     516             : }
     517             : 
     518             : 
     519       65834 : int ELEKTRA_PLUGIN_FUNCTION (get) (Plugin * handle, KeySet * returned, Key * parentKey)
     520             : {
     521       65834 :         Key * root = keyNew ("system/elektra/modules/" ELEKTRA_PLUGIN_NAME, KEY_END);
     522             : 
     523       65834 :         if (keyRel (root, parentKey) >= 0)
     524             :         {
     525        4777 :                 keyDel (root);
     526        4777 :                 KeySet * info =
     527             : #include "contract.h"
     528        4777 :                         ksAppend (returned, info);
     529        4777 :                 ksDel (info);
     530        4777 :                 return 1;
     531             :         }
     532       61057 :         keyDel (root);
     533             : 
     534       61057 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
     535       61057 :         keySetString (parentKey, pk->filename);
     536             : 
     537       61057 :         int errnoSave = errno;
     538             :         struct stat buf;
     539             : 
     540             :         ELEKTRA_LOG ("stat file %s", pk->filename);
     541             :         /* Start file IO with stat() */
     542      122114 :         if (stat (pk->filename, &buf) == -1)
     543             :         {
     544             :                 // no file, so storage has no job
     545       37249 :                 errno = errnoSave;
     546       37249 :                 pk->isMissing = 1;
     547             : 
     548             :                 // no file, so no metadata:
     549       37249 :                 pk->mtime.tv_sec = 0;
     550       37249 :                 pk->mtime.tv_nsec = 0;
     551       37249 :                 return 0;
     552             :         }
     553             :         else
     554             :         {
     555             :                 // successful, remember mode, uid and gid
     556       23808 :                 pk->filemode = buf.st_mode;
     557       23808 :                 pk->gid = buf.st_gid;
     558       23808 :                 pk->uid = buf.st_uid;
     559       23808 :                 pk->isMissing = 0;
     560             :         }
     561             : 
     562             :         /* Check if update needed */
     563       23808 :         if (pk->mtime.tv_sec == ELEKTRA_STAT_SECONDS (buf) && pk->mtime.tv_nsec == ELEKTRA_STAT_NANO_SECONDS (buf))
     564             :         {
     565             :                 // no update, so storage has no job
     566        6074 :                 errno = errnoSave;
     567        6074 :                 return 0;
     568             :         }
     569             : 
     570             :         /* Check if cache update needed */
     571             :         KeySet * global;
     572       17734 :         char * name = 0;
     573             : 
     574       17734 :         if ((global = elektraPluginGetGlobalKeySet (handle)) != NULL && ELEKTRA_STAT_NANO_SECONDS (buf) != 0)
     575             :         {
     576       17734 :                 name = elektraCacheKeyName (pk->filename);
     577             : 
     578             :                 ELEKTRA_LOG_DEBUG ("global-cache: check cache update needed?");
     579       17734 :                 Key * time = ksLookupByName (global, name, KDB_O_NONE);
     580       17734 :                 if (time && keyGetValueSize (time) == sizeof (struct timespec))
     581             :                 {
     582             :                         struct timespec cached;
     583           0 :                         keyGetBinary (time, &cached, sizeof (struct timespec));
     584           0 :                         if (cached.tv_sec == ELEKTRA_STAT_SECONDS (buf) && cached.tv_nsec == ELEKTRA_STAT_NANO_SECONDS (buf))
     585             :                         {
     586             :                                 ELEKTRA_LOG_DEBUG ("global-cache: no update needed, everything is fine");
     587             :                                 ELEKTRA_LOG_DEBUG ("cached.tv_sec:\t%ld", cached.tv_sec);
     588             :                                 ELEKTRA_LOG_DEBUG ("cached.tv_nsec:\t%ld", cached.tv_nsec);
     589             :                                 ELEKTRA_LOG_DEBUG ("buf.tv_sec:\t%ld", ELEKTRA_STAT_SECONDS (buf));
     590             :                                 ELEKTRA_LOG_DEBUG ("buf.tv_nsec:\t%ld", ELEKTRA_STAT_NANO_SECONDS (buf));
     591             :                                 // update timestamp inside resolver
     592           0 :                                 pk->mtime.tv_sec = ELEKTRA_STAT_SECONDS (buf);
     593           0 :                                 pk->mtime.tv_nsec = ELEKTRA_STAT_NANO_SECONDS (buf);
     594             : 
     595           0 :                                 if (name) elektraFree (name);
     596           0 :                                 errno = errnoSave;
     597           0 :                                 return ELEKTRA_PLUGIN_STATUS_CACHE_HIT;
     598             :                         }
     599             :                 }
     600             :         }
     601             : 
     602       17734 :         pk->mtime.tv_sec = ELEKTRA_STAT_SECONDS (buf);
     603       17734 :         pk->mtime.tv_nsec = ELEKTRA_STAT_NANO_SECONDS (buf);
     604             : 
     605             :         /* Persist modification times for cache */
     606       17734 :         if (global != NULL && ELEKTRA_STAT_NANO_SECONDS (buf) != 0)
     607             :         {
     608             :                 ELEKTRA_LOG_DEBUG ("global-cache: adding file modufication times");
     609       17734 :                 Key * time = keyNew (name, KEY_BINARY, KEY_SIZE, sizeof (struct timespec), KEY_VALUE, &(pk->mtime), KEY_END);
     610       17734 :                 ksAppendKey (global, time);
     611             :         }
     612             : 
     613       17734 :         if (name) elektraFree (name);
     614       17734 :         errno = errnoSave;
     615       17734 :         return 1;
     616             : }
     617             : 
     618             : 
     619             : /**
     620             :  * @brief Open a file and yield an error on conflicts
     621             :  *
     622             :  * @param pk->filename will be used
     623             :  * @param parentKey to yield the error to
     624             :  *
     625             :  * @retval 0 on success (might be an error for creating a missing file)
     626             :  * @retval -1 on conflict
     627             :  */
     628        2457 : static int elektraOpenFile (resolverHandle * pk, Key * parentKey)
     629             : {
     630        2457 :         int flags = 0;
     631             : 
     632        2457 :         if (pk->isMissing)
     633             :         {
     634             :                 // it must be created newly, otherwise we have an conflict
     635             :                 flags = O_RDWR | O_CREAT | O_EXCL;
     636             : 
     637             :                 // only works when using NFSv3 or later on kernel 2.6 or later
     638             :                 // TODO: add variant with linkat?
     639             :         }
     640             :         else
     641             :         {
     642             :                 // file was there before, so opening should work!
     643        2211 :                 flags = O_RDWR;
     644             :         }
     645             : 
     646        2457 :         errno = 0;
     647        2457 :         pk->fd = open (pk->filename, flags, pk->filemode);
     648             : 
     649        2457 :         if (!pk->isMissing)
     650             :         {
     651        2211 :                 if (errno == ENOENT)
     652             :                 {
     653           2 :                         ELEKTRA_SET_INTERNAL_ERRORF (parentKey,
     654             :                                                      "The configuration file '%s' was there earlier, "
     655             :                                                      "now it is missing",
     656             :                                                      pk->filename);
     657           2 :                         return -1;
     658             :                 }
     659        2209 :                 else if (pk->fd == -1)
     660             :                 {
     661           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not reopen configuration file '%s' for writing. Reason: %s",
     662             :                                                      pk->filename, strerror (errno));
     663           0 :                         return -1;
     664             :                 }
     665             :                 // successfully reopened
     666             :         }
     667             :         else
     668             :         {
     669         246 :                 if (pk->fd != -1)
     670             :                 {
     671             :                         // successfully created a file
     672         233 :                         pk->removalNeeded = 1;
     673         233 :                         return 0;
     674             :                 }
     675          13 :                 else if (errno == EEXIST)
     676             :                 {
     677          10 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey,
     678             :                                                      "No configuration file was there earlier. "
     679             :                                                      "Now configuration file '%s' exists",
     680             :                                                      pk->filename);
     681          10 :                         return -1;
     682             :                 }
     683             : 
     684             :                 // ignore errors for attempts to create a new file, we will try it again later
     685             :         }
     686             : 
     687        2212 :         errno = 0;
     688             : 
     689        2212 :         return 0;
     690             : }
     691             : 
     692             : 
     693             : /**
     694             :  * @brief Create a file and yield an error if it did not work
     695             :  *
     696             :  * @param pk->filename will be used
     697             :  * @param parentKey to yield the error to
     698             :  *
     699             :  * @retval 0 on success
     700             :  * @retval -1 on error
     701             :  */
     702           3 : static int elektraCreateFile (resolverHandle * pk, Key * parentKey)
     703             : {
     704           3 :         pk->fd = open (pk->filename, O_RDWR | O_CREAT, pk->filemode);
     705             : 
     706           3 :         if (pk->fd == -1)
     707             :         {
     708           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not create configuration file '%s'. Reason: %s", pk->filename,
     709             :                                              strerror (errno));
     710           0 :                 return -1;
     711             :         }
     712             :         return 0;
     713             : }
     714             : 
     715             : 
     716             : /**
     717             :  * @brief Create pathname recursively.
     718             :  *
     719             :  * Try unless the whole path was
     720             :  * created or it is sure that it cannot be done.
     721             :  *
     722             :  * @param pathname The path to create.
     723             :  *
     724             :  * @retval 0 on success
     725             :  * @retval -1 on error + elektra error will be set
     726             :  */
     727           3 : static int elektraMkdirParents (resolverHandle * pk, const char * pathname, Key * parentKey)
     728             : {
     729           3 :         if (mkdir (pathname, pk->dirmode) == -1)
     730             :         {
     731           0 :                 if (errno != ENOENT)
     732             :                 {
     733             :                         // hopeless, give it up
     734             :                         goto error;
     735             :                 }
     736             : 
     737             :                 // last part of filename component (basename)
     738           0 :                 char * p = strrchr (pathname, '/');
     739             : 
     740             :                 /* nothing found */
     741           0 :                 if (p == NULL)
     742             :                 {
     743             :                         // set any errno, corrected in
     744             :                         // elektraAddErrnoText
     745           0 :                         errno = E2BIG;
     746           0 :                         goto error;
     747             :                 }
     748             : 
     749             :                 /* absolute path */
     750           0 :                 if (p == pathname)
     751             :                 {
     752             :                         // set any errno, corrected in
     753             :                         // elektraAddErrnoText
     754           0 :                         errno = EINVAL;
     755           0 :                         goto error;
     756             :                 }
     757             : 
     758             :                 /* Cut path at last /. */
     759           0 :                 *p = 0;
     760             : 
     761             :                 /* Now call ourselves recursively */
     762           0 :                 if (elektraMkdirParents (pk, pathname, parentKey) == -1)
     763             :                 {
     764             :                         // do not yield an error, was already done
     765             :                         // before
     766           0 :                         *p = '/';
     767           0 :                         return -1;
     768             :                 }
     769             : 
     770             :                 /* Restore path. */
     771           0 :                 *p = '/';
     772             : 
     773           0 :                 if (mkdir (pathname, pk->dirmode) == -1)
     774             :                 {
     775             :                         goto error;
     776             :                 }
     777             :         }
     778             : 
     779             :         return 0;
     780             : 
     781             : error:
     782             : {
     783           0 :         ELEKTRA_SET_RESOURCE_ERRORF (parentKey,
     784             :                                      "Could not create directory '%s'. Reason: %s. Identity: uid: %u, euid: %u, gid: %u, egid: %u",
     785             :                                      pathname, elektraAddErrnoText (), getuid (), geteuid (), getgid (), getegid ());
     786           0 :         return -1;
     787             : }
     788             : }
     789             : 
     790             : /**
     791             :  * @brief Check conflict for the current open file
     792             :  *
     793             :  * Does an fstat and checks if mtime are equal as they were
     794             :  *
     795             :  * @param pk to get mtime and fd from
     796             :  * @param parentKey to write errors&warnings to
     797             :  *
     798             :  * @retval 0 success
     799             :  * @retval -1 error
     800             :  */
     801        2445 : static int elektraCheckConflict (resolverHandle * pk, Key * parentKey)
     802             : {
     803        2445 :         if (pk->isMissing)
     804             :         {
     805             :                 // conflict already handled at file creation time, so just return successfully
     806             :                 return 0;
     807             :         }
     808             : 
     809             :         struct stat buf;
     810             : 
     811        4418 :         if (fstat (pk->fd, &buf) == -1)
     812             :         {
     813           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (
     814             :                         parentKey,
     815             :                         "Could not 'fstat' to check for conflict '%s'. Reason: %s. Identity: uid: %u, euid: %u, gid: %u, egid: %u",
     816             :                         pk->filename, elektraAddErrnoText (), getuid (), geteuid (), getgid (), getegid ());
     817             : 
     818           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Assuming conflict because of failed stat (warning %s for details)",
     819             :                                              ELEKTRA_ERROR_RESOURCE);
     820           0 :                 return -1;
     821             :         }
     822             : 
     823        2209 :         if (ELEKTRA_STAT_SECONDS (buf) != pk->mtime.tv_sec || ELEKTRA_STAT_NANO_SECONDS (buf) != pk->mtime.tv_nsec)
     824             :         {
     825           6 :                 ELEKTRA_SET_CONFLICTING_STATE_ERRORF (
     826             :                         parentKey,
     827             :                         "Conflict, file modification time stamp '%ld.%ld' is different than our time stamp '%ld.%ld', config file "
     828             :                         "name is '%s'. "
     829             :                         "Our identity is uid: %u, euid: %u, gid: %u, egid: %u",
     830             :                         ELEKTRA_STAT_SECONDS (buf), ELEKTRA_STAT_NANO_SECONDS (buf), pk->mtime.tv_sec, pk->mtime.tv_nsec, pk->filename,
     831             :                         getuid (), geteuid (), getgid (), getegid ());
     832           6 :                 return -1;
     833             :         }
     834             : 
     835             : 
     836             :         return 0;
     837             : }
     838             : 
     839             : /**
     840             :  * @brief Does everything needed before the storage plugin will be
     841             :  * invoked.
     842             :  *
     843             :  * @param pk resolver information
     844             :  * @param parentKey parent
     845             :  *
     846             :  * @retval 0 on success
     847             :  * @retval -1 on error
     848             :  */
     849        2457 : static int elektraSetPrepare (resolverHandle * pk, Key * parentKey)
     850             : {
     851        2457 :         pk->removalNeeded = 0;
     852             : 
     853        2457 :         if (elektraOpenFile (pk, parentKey) == -1)
     854             :         {
     855             :                 // file/none-file conflict OR error on previously existing file
     856             :                 return -1;
     857             :         }
     858             : 
     859        2445 :         if (pk->fd == -1)
     860             :         {
     861             :                 // try creation of underlying directory
     862           3 :                 elektraMkdirParents (pk, pk->dirname, parentKey);
     863             : 
     864             :                 // now try to create file
     865           3 :                 if (elektraCreateFile (pk, parentKey) == -1)
     866             :                 {
     867             :                         // no way to be successful
     868             :                         return -1;
     869             :                 }
     870             : 
     871             :                 // the file was created by us, so we need to remove it
     872             :                 // on error:
     873           3 :                 pk->removalNeeded = 1;
     874             :         }
     875             : 
     876        2445 :         if (elektraLockMutex (parentKey) != 0)
     877             :         {
     878           0 :                 elektraCloseFile (pk->fd, parentKey);
     879           0 :                 pk->fd = -1;
     880           0 :                 return -1;
     881             :         }
     882             : 
     883             :         // now we have a file, so lock immediately
     884        2445 :         if (elektraLockFile (pk->fd, parentKey) == -1)
     885             :         {
     886           0 :                 elektraCloseFile (pk->fd, parentKey);
     887           0 :                 elektraUnlockMutex (parentKey);
     888           0 :                 pk->fd = -1;
     889           0 :                 return -1;
     890             :         }
     891             : 
     892        2445 :         if (elektraCheckConflict (pk, parentKey) == -1)
     893             :         {
     894           6 :                 elektraUnlockFile (pk->fd, parentKey);
     895           6 :                 elektraCloseFile (pk->fd, parentKey);
     896           6 :                 elektraUnlockMutex (parentKey);
     897           6 :                 pk->fd = -1;
     898           6 :                 return -1;
     899             :         }
     900             : 
     901             :         return 0;
     902             : }
     903             : 
     904           0 : static void elektraModifyFileTime (resolverHandle * pk)
     905             : {
     906             : #ifdef HAVE_CLOCK_GETTIME
     907             :         // for linux let us calculate a new ns timestamp to use
     908             :         struct timespec ts;
     909           0 :         clock_gettime (CLOCK_MONOTONIC, &ts);
     910             : 
     911           0 :         if (ts.tv_sec == pk->mtime.tv_sec)
     912             :         {
     913             :                 // for filesystems not supporting subseconds, make sure the second is changed, too
     914           0 :                 pk->mtime.tv_sec += pk->timeFix;
     915           0 :                 pk->timeFix *= -1; // toggle timefix
     916             :         }
     917             :         else
     918             :         {
     919           0 :                 pk->mtime.tv_sec = ts.tv_sec;
     920             :         }
     921             : 
     922           0 :         if (ts.tv_nsec == pk->mtime.tv_nsec)
     923             :         {
     924             :                 // also slightly change nsec (same direction as seconds):
     925           0 :                 pk->mtime.tv_nsec += pk->timeFix;
     926             :         }
     927             :         else
     928             :         {
     929           0 :                 pk->mtime.tv_nsec = ts.tv_nsec;
     930             :         }
     931             : #else
     932             :         // otherwise use simple time toggling schema of seconds
     933             :         pk->mtime.tv_sec += pk->timeFix;
     934             :         pk->timeFix *= -1;                                                               // toggle timefix
     935             : #endif
     936           0 : }
     937             : 
     938             : 
     939             : /* Update timestamp of old file to provoke conflicts in
     940             :  * stalling processes that might still wait with the old
     941             :  * filedescriptor */
     942        2371 : static void elektraUpdateFileTime (resolverHandle * pk, int fd, Key * parentKey)
     943             : {
     944             : #ifdef HAVE_FUTIMENS
     945        2371 :         const struct timespec times[2] = { pk->mtime,   // atime
     946             :                                            pk->mtime }; // mtime
     947             : 
     948        2371 :         if (futimens (fd, times) == -1)
     949             :         {
     950           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Could not update time stamp of '%s'. Reason: %s",
     951             :                                                fd == pk->fd ? pk->filename : pk->tempfile, strerror (errno));
     952             :         }
     953             : #elif defined(HAVE_FUTIMES)
     954             :         const struct timeval times[2] = { { pk->mtime.tv_sec, pk->mtime.tv_nsec / 1000 },   // atime
     955             :                                           { pk->mtime.tv_sec, pk->mtime.tv_nsec / 1000 } }; // mtime
     956             : 
     957             :         if (futimes (fd, times) == -1)
     958             :         {
     959             :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Could not update time stamp of \"%s\", because %s",
     960             :                                                fd == pk->fd ? pk->filename : pk->tempfile, strerror (errno));
     961             :         }
     962             : #else
     963             : #warning futimens/futimes not defined
     964             : #endif
     965        2371 : }
     966             : 
     967             : /**
     968             :  * @brief Now commit the temporary file to be final
     969             :  *
     970             :  * @param pk
     971             :  * @param parentKey
     972             :  *
     973             :  * It will also reset pk->fd
     974             :  *
     975             :  * @retval 0 on success
     976             :  * @retval -1 on error
     977             :  */
     978        2371 : static int elektraSetCommit (resolverHandle * pk, Key * parentKey)
     979             : {
     980        2371 :         int ret = 0;
     981             : 
     982        2371 :         int fd = open (pk->tempfile, O_RDWR);
     983        2371 :         if (fd == -1)
     984             :         {
     985           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not open file '%s' again for changing metadata. Reason: %s", pk->tempfile,
     986             :                                              strerror (errno));
     987           0 :                 ret = -1;
     988             :         }
     989             : 
     990        2371 :         elektraLockFile (fd, parentKey);
     991             : 
     992        2371 :         if (rename (pk->tempfile, pk->filename) == -1)
     993             :         {
     994           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not rename file '%s'. Reason: %s", pk->tempfile, strerror (errno));
     995           0 :                 ret = -1;
     996             :         }
     997             : 
     998             :         ELEKTRA_LOG_DEBUG ("old.tv_sec:\t%ld", pk->mtime.tv_sec);
     999             :         ELEKTRA_LOG_DEBUG ("old.tv_nsec:\t%ld", pk->mtime.tv_nsec);
    1000             :         struct stat buf;
    1001        2371 :         if (fstat (fd, &buf) == -1)
    1002             :         {
    1003           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to stat file '%s'. Reason: %s", pk->tempfile, strerror (errno));
    1004             :         }
    1005             :         else
    1006             :         {
    1007        2371 :                 if (!(pk->mtime.tv_sec == ELEKTRA_STAT_SECONDS (buf) && pk->mtime.tv_nsec == ELEKTRA_STAT_NANO_SECONDS (buf)))
    1008             :                 {
    1009             :                         /* Update timestamp */
    1010        2371 :                         pk->mtime.tv_sec = ELEKTRA_STAT_SECONDS (buf);
    1011        2371 :                         pk->mtime.tv_nsec = ELEKTRA_STAT_NANO_SECONDS (buf);
    1012             :                 }
    1013             :                 else
    1014             :                 {
    1015           0 :                         elektraModifyFileTime (pk);
    1016             :                         // update file visible in filesystem:
    1017           0 :                         elektraUpdateFileTime (pk, fd, parentKey);
    1018             : 
    1019             :                         /* @post
    1020             :                            For timejump backwards or time not changed,
    1021             :                            use time + 1ns
    1022             :                            This is needed to fulfill the postcondition
    1023             :                            that the timestamp changed at least slightly
    1024             :                            and makes sure that all processes that stat()ed
    1025             :                            the file will get a conflict. */
    1026             :                 }
    1027             :         }
    1028             : 
    1029        2371 :         elektraUpdateFileTime (pk, pk->fd, parentKey);
    1030             :         ELEKTRA_LOG_DEBUG ("new.tv_sec:\t%ld", pk->mtime.tv_sec);
    1031             :         ELEKTRA_LOG_DEBUG ("new.tv_nsec:\t%ld", pk->mtime.tv_nsec);
    1032             : 
    1033        2371 :         if (buf.st_mode != pk->filemode)
    1034             :         {
    1035             :                 // change mode to what it was before
    1036        1291 :                 if (fchmod (fd, pk->filemode) == -1)
    1037             :                 {
    1038           0 :                         ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey,
    1039             :                                                        "Could not change permissions of temporary file '%s' from '%o' to '%o'. Reason: %s",
    1040             :                                                        pk->tempfile, buf.st_mode, pk->filemode, strerror (errno));
    1041             :                 }
    1042             :         }
    1043             : 
    1044        2371 :         if (!pk->isMissing && (buf.st_uid != pk->uid || buf.st_gid != pk->gid))
    1045             :         {
    1046          48 :                 if (fchown (fd, pk->uid, pk->gid) == -1)
    1047             :                 {
    1048          48 :                         ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey,
    1049             :                                                        "Could not change owner of temporary file '%s' from %d.%d to %d.%d. Reason: %s",
    1050             :                                                        pk->tempfile, buf.st_uid, buf.st_gid, pk->uid, pk->gid, strerror (errno));
    1051             :                 }
    1052             :         }
    1053             : 
    1054             :         // file is present now!
    1055        2371 :         pk->isMissing = 0;
    1056             : 
    1057        2371 :         DIR * dirp = opendir (pk->dirname);
    1058             :         // checking dirp not needed, fsync will have EBADF
    1059        2371 :         if (fsync (dirfd (dirp)) == -1)
    1060             :         {
    1061           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Could not sync directory '%s'. Reason: %s", pk->dirname, strerror (errno));
    1062             :         }
    1063        2371 :         closedir (dirp);
    1064             : 
    1065        2371 :         elektraUnlockFile (pk->fd, parentKey);
    1066        2371 :         elektraCloseFile (pk->fd, parentKey);
    1067        2371 :         elektraUnlockFile (fd, parentKey);
    1068        2371 :         elektraCloseFile (fd, parentKey);
    1069        2371 :         elektraUnlockMutex (parentKey);
    1070             : 
    1071        2371 :         return ret;
    1072             : }
    1073             : 
    1074             : 
    1075        5209 : int ELEKTRA_PLUGIN_FUNCTION (set) (Plugin * handle, KeySet * ks, Key * parentKey)
    1076             : {
    1077        5209 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
    1078             : 
    1079        5209 :         int errnoSave = errno;
    1080        5209 :         int ret = 1;
    1081             : 
    1082             :         ELEKTRA_LOG ("entering resolver::set %d \"%s\"", pk->fd, pk->filename);
    1083        5209 :         if (pk->fd == -1)
    1084             :         {
    1085             :                 // no fd up to now, so we are in first phase
    1086             : 
    1087             :                 // we operate on the tmp file
    1088        2649 :                 keySetString (parentKey, pk->tempfile);
    1089             : 
    1090        2649 :                 if (ksGetSize (ks) == 0)
    1091             :                 {
    1092         192 :                         ret = 0;
    1093             : 
    1094             :                         ELEKTRA_LOG ("check if removal of the configuration file \"%s\" would work later", pk->filename);
    1095         192 :                         if (access (pk->dirname, W_OK | X_OK) == -1)
    1096             :                         {
    1097           0 :                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not remove file '%s'. Reason: %s", pk->filename,
    1098             :                                                              strerror (errno));
    1099           0 :                                 ret = -1;
    1100             :                         }
    1101             : 
    1102             :                         // remove file on commit
    1103         192 :                         pk->fd = -2;
    1104             :                 }
    1105             :                 else
    1106             :                 {
    1107             :                         // prepare phase
    1108        2457 :                         if (elektraSetPrepare (pk, parentKey) == -1)
    1109             :                         {
    1110          18 :                                 ret = -1;
    1111             :                         }
    1112             :                 }
    1113             :         }
    1114        2560 :         else if (pk->fd == -2)
    1115             :         {
    1116             :                 ELEKTRA_LOG ("unlink configuration file \"%s\"", pk->filename);
    1117         189 :                 if (unlink (pk->filename) == -1)
    1118             :                 {
    1119           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not remove file '%s'. Reason: %s", pk->filename, strerror (errno));
    1120           0 :                         ret = -1;
    1121             :                 }
    1122             : 
    1123             :                 // reset for the next time
    1124         189 :                 pk->fd = -1;
    1125             :         }
    1126             :         else
    1127             :         {
    1128             :                 // now we do not operate on the temporary file anymore,
    1129             :                 // but on the real file
    1130        2371 :                 keySetString (parentKey, pk->filename);
    1131             : 
    1132             :                 /* we have an fd, so we are in second phase*/
    1133        2371 :                 if (elektraSetCommit (pk, parentKey) == -1)
    1134             :                 {
    1135           0 :                         ret = -1;
    1136             :                 }
    1137             : 
    1138             :                 // reset for next time
    1139        2371 :                 pk->fd = -1;
    1140             :         }
    1141             : 
    1142             :         ELEKTRA_LOG ("leaving resolver::set %d \"%s\"", pk->fd, pk->filename);
    1143             : 
    1144        5209 :         errno = errnoSave; // maybe some temporary error happened
    1145             : 
    1146        5209 :         return ret;
    1147             : }
    1148             : 
    1149         111 : static void elektraUnlinkFile (char * filename, Key * parentKey)
    1150             : {
    1151         111 :         int errnoSave = errno;
    1152         111 :         if (unlink (filename) == -1)
    1153             :         {
    1154           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Could not unlink the file '%s'. Reason: %s", filename, strerror (errno));
    1155           0 :                 errno = errnoSave;
    1156             :         }
    1157         111 : }
    1158             : 
    1159          89 : int ELEKTRA_PLUGIN_FUNCTION (error) (Plugin * handle, KeySet * r ELEKTRA_UNUSED, Key * parentKey)
    1160             : {
    1161          89 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
    1162             : 
    1163          89 :         if (pk->fd == -2)
    1164             :         { // removal aborted state (= empty keyset, but error)
    1165             :                 // reset for next time
    1166           3 :                 pk->fd = -1;
    1167           3 :                 return 0;
    1168             :         }
    1169             : 
    1170          86 :         elektraUnlinkFile (pk->tempfile, parentKey);
    1171             : 
    1172          86 :         if (pk->fd > -1)
    1173             :         { // with fd
    1174          68 :                 elektraUnlockFile (pk->fd, parentKey);
    1175          68 :                 elektraCloseFile (pk->fd, parentKey);
    1176          68 :                 if (pk->removalNeeded == 1)
    1177             :                 { // removal needed state (= resolver created file, but error)
    1178          25 :                         elektraUnlinkFile (pk->filename, parentKey);
    1179             :                 }
    1180          68 :                 elektraUnlockMutex (parentKey);
    1181             :         }
    1182             : 
    1183             :         // reset for next time
    1184          86 :         pk->fd = -1;
    1185             : 
    1186          86 :         return 0;
    1187             : }
    1188             : 
    1189        2559 : int ELEKTRA_PLUGIN_FUNCTION (commit) (Plugin * handle, KeySet * returned, Key * parentKey)
    1190             : {
    1191        2559 :         return ELEKTRA_PLUGIN_FUNCTION (set) (handle, returned, parentKey);
    1192             : }
    1193             : 
    1194             : 
    1195       57787 : Plugin * ELEKTRA_PLUGIN_EXPORT
    1196             : {
    1197             :         // clang-format off
    1198       57787 :     return elektraPluginExport(ELEKTRA_PLUGIN_NAME,
    1199             :             ELEKTRA_PLUGIN_OPEN,        &ELEKTRA_PLUGIN_FUNCTION(open),
    1200             :             ELEKTRA_PLUGIN_CLOSE,       &ELEKTRA_PLUGIN_FUNCTION(close),
    1201             :             ELEKTRA_PLUGIN_GET, &ELEKTRA_PLUGIN_FUNCTION(get),
    1202             :             ELEKTRA_PLUGIN_SET, &ELEKTRA_PLUGIN_FUNCTION(set),
    1203             :             ELEKTRA_PLUGIN_ERROR,       &ELEKTRA_PLUGIN_FUNCTION(error),
    1204             :             ELEKTRA_PLUGIN_COMMIT, &ELEKTRA_PLUGIN_FUNCTION (commit),
    1205             :             ELEKTRA_PLUGIN_END);
    1206             : }
    1207             : 

Generated by: LCOV version 1.13