LCOV - code coverage report
Current view: top level - src/libs/elektra - keymeta.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 81 88 92.0 %
Date: 2019-09-12 12:28:41 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Methods to do various operations on Key metadata.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : /***************************************************************************
      10             :                       keymeta.c  -  Methods for Key manipulation
      11             :                              -------------------
      12             :     begin                : Fri Sep 26 2008
      13             :     copyright            : (C) 2008 by Markus Raab
      14             :     email                : elektra@markus-raab.org
      15             :  ***************************************************************************/
      16             : 
      17             : /***************************************************************************
      18             :  *                                                                         *
      19             :  *   This program is free software; you can redistribute it and/or modify  *
      20             :  *   it under the terms of the BSD License (revised).                      *
      21             :  *                                                                         *
      22             :  ***************************************************************************/
      23             : 
      24             : 
      25             : /**
      26             :  * @defgroup keymeta Meta Info Manipulation Methods
      27             :  * @ingroup key
      28             :  * @brief Methods to do various operations on Key metadata.
      29             :  *
      30             :  * To use them:
      31             :  * @code
      32             : #include <kdb.h>
      33             :  * @endcode
      34             :  *
      35             :  * Next to \link keyname Name (key and owner) \endlink and
      36             :  * \link keyvalue value (data and comment) \endlink there
      37             :  * is the so called meta information inside every key.
      38             :  *
      39             :  * Key meta information are an unlimited number of key/value
      40             :  * pairs strongly related to a key. It main purpose is to
      41             :  * give keys special semantics, so that plugins can treat
      42             :  * them differently.
      43             :  *
      44             :  * Metakey, as opposed to Key,
      45             :  * deliberately has following limitations:
      46             :  * - no null values
      47             :  * - no binary data
      48             :  * - no modification of references (COW)
      49             :  * - no guarantee of ordering
      50             :  *
      51             :  * ## Examples for metadata
      52             :  *
      53             :  * File system information (see stat(2) for more information):
      54             :  * - uid: the user id (positive number)
      55             :  * - gid: the group id (positive number)
      56             :  * - mode: filesystem-like mode permissions (positive octal number)
      57             :  * - atime: When was the key accessed the last time.
      58             :  * - mtime: When was the key modified the last time.
      59             :  * - ctime: When the uid, gid or mode of a key changes.
      60             :  * (times are represented through a positive number as unix timestamp)
      61             :  *
      62             :  * The comment can contain userdata which directly
      63             :  * belong to that key. The name of the meta information
      64             :  * is "comment" for a general purpose comment about
      65             :  * the key. Multi-Language comments are also supported
      66             :  * by appending [LANG] to the name.
      67             :  *
      68             :  * Validators are regular expressions which are tested
      69             :  * against the key value. The metakey "validator" can
      70             :  * hold a regular expression which will be matched
      71             :  * against.
      72             :  *
      73             :  * Types can be expressed with the meta information
      74             :  * "type".
      75             :  *
      76             :  * The relevance of the key can be tagged with a value
      77             :  * from -20 to 20. Negative numbers are the more important
      78             :  * and must be present in order to start the program.
      79             :  *
      80             :  * A version of a key may be stored with "version".
      81             :  * Its format is full.major.minor where all of these
      82             :  * are integers.
      83             :  *
      84             :  * The order inside a persistent storage can be described
      85             :  * with the tag "order" which contains a positive number.
      86             :  *
      87             :  * The metakey "app" describes to which application a
      88             :  * key belongs. It can be used to remove keys from an
      89             :  * application no longer installed.
      90             :  *
      91             :  * The metakey "path" describes where the key is physically
      92             :  * stored.
      93             :  *
      94             :  * The "owner" is the user that owns the key. It only
      95             :  * works for the user/ hierarchy. It rather says where
      96             :  * the key is stored and says nothing about the
      97             :  * filesystem properties.
      98             :  *
      99             :  */
     100             : 
     101             : 
     102             : #include <kdb.h>
     103             : #include <kdbconfig.h>
     104             : #include <kdbprivate.h>
     105             : 
     106             : #ifdef HAVE_STDIO_H
     107             : #include <stdio.h>
     108             : #endif
     109             : 
     110             : #ifdef HAVE_STDARG_H
     111             : #include <stdarg.h>
     112             : #endif
     113             : 
     114             : #ifdef HAVE_STRING_H
     115             : #include <string.h>
     116             : #endif
     117             : 
     118             : #ifdef HAVE_STDLIB_H
     119             : #include <stdlib.h>
     120             : #endif
     121             : 
     122             : #ifdef HAVE_ERRNO_H
     123             : #include <errno.h>
     124             : #endif
     125             : 
     126             : 
     127             : /**Rewind the internal iterator to first metadata.
     128             :  *
     129             :  * Use it to set the cursor to the beginning of the Key Meta Infos.
     130             :  * keyCurrentMeta() will then always return NULL afterwards. So
     131             :  * you want to keyNextMeta() first.
     132             :  *
     133             :  * @code
     134             : Key *key;
     135             : const Key *meta;
     136             : 
     137             : keyRewindMeta (key);
     138             : while ((meta = keyNextMeta (key))!=0)
     139             : {
     140             :         printf ("name: %s, value: %s", keyName(meta), keyString(meta));
     141             : }
     142             :  * @endcode
     143             :  *
     144             :  * @param key the key object to work with
     145             :  * @retval 0 on success
     146             :  * @retval 0 if there is no meta information for that key
     147             :  *         (keyNextMeta() will always return 0 in that case)
     148             :  * @retval -1 on NULL pointer
     149             :  * @see keyNextMeta(), keyCurrentMeta()
     150             :  * @see ksRewind() for pedant in iterator interface of KeySet
     151             :  * @ingroup keymeta
     152             :  **/
     153      243856 : int keyRewindMeta (Key * key)
     154             : {
     155      243856 :         if (!key) return -1;
     156      243837 :         if (!key->meta) return 0;
     157             : 
     158      180534 :         return ksRewind (key->meta);
     159             : }
     160             : 
     161             : /** Iterate to the next meta information.
     162             :  *
     163             :  * Keys have an internal cursor that can be reset with keyRewindMeta(). Every
     164             :  * time keyNextMeta() is called the cursor is incremented and the new current
     165             :  * Name of Meta Information is returned.
     166             :  *
     167             :  * You'll get a NULL pointer if the meta information after the end of the Key was reached.
     168             :  * On subsequent calls of keyNextMeta() it will still return the NULL pointer.
     169             :  *
     170             :  * The @p key internal cursor will be changed, so it is not const.
     171             :  *
     172             :  * @note That the resulting key is guaranteed to have a value, because
     173             :  *       meta information has no binary or null pointer semantics.
     174             :  *
     175             :  * @note You must not delete or change the returned key,
     176             :  *    use keySetMeta() if you want to delete or change it.
     177             :  *
     178             :  * @param key the key object to work with
     179             :  * @return a key representing meta information
     180             :  * @retval 0 when the end is reached
     181             :  * @retval 0 on NULL pointer
     182             :  *
     183             :  * @see ksNext() for pedant in iterator interface of KeySet
     184             :  * @ingroup keymeta
     185             :  **/
     186      423917 : const Key * keyNextMeta (Key * key)
     187             : {
     188             :         Key * ret;
     189      423917 :         if (!key) return 0;
     190      423898 :         if (!key->meta) return 0;
     191             : 
     192      410168 :         ret = ksNext (key->meta);
     193             : 
     194      410168 :         return ret;
     195             : }
     196             : 
     197             : /**Returns the value of a meta-information which is current.
     198             :  *
     199             :  * The pointer is NULL if you reached the end or after
     200             :  * ksRewind().
     201             :  *
     202             :  * @note You must not delete or change the returned key,
     203             :  *    use keySetMeta() if you want to delete or change it.
     204             :  *
     205             :  * @param key the key object to work with
     206             :  * @return a buffer to the value pointed by @p key's cursor
     207             :  * @retval 0 on NULL pointer
     208             :  * @see keyNextMeta(), keyRewindMeta()
     209             :  *
     210             :  * @see ksCurrent() for pedant in iterator interface of KeySet
     211             :  * @ingroup keymeta
     212             :  **/
     213       64688 : const Key * keyCurrentMeta (const Key * key)
     214             : {
     215             :         Key * ret;
     216       64688 :         if (!key) return 0;
     217       64688 :         if (!key->meta) return 0;
     218             : 
     219       64686 :         ret = ksCurrent (key->meta);
     220             : 
     221       64686 :         return ret;
     222             : }
     223             : 
     224             : /**Do a shallow copy of metadata from source to dest.
     225             :  *
     226             :  * The key dest will have the same metadata referred with
     227             :  * metaName afterwards then source.
     228             :  *
     229             :  * For example the metadata type is copied into the
     230             :  * Key k.
     231             :  *
     232             :  * @code
     233             : void l(Key *k)
     234             : {
     235             :         // receive c
     236             :         keyCopyMeta(k, c, "type");
     237             :         // the caller will see the changed key k
     238             :         // with the metadata "type" from c
     239             : }
     240             :  * @endcode
     241             :  *
     242             :  * The main purpose of this function is for plugins or
     243             :  * applications which want to add the same metadata to
     244             :  * n keys. When you do that with keySetMeta() it will
     245             :  * take n times the memory for the key. This can be
     246             :  * considerable amount of memory for many keys with
     247             :  * some metadata for each.
     248             :  *
     249             :  * To avoid that problem you can use keyCopyAllMeta()
     250             :  * or keyCopyMeta().
     251             :  *
     252             :  * @code
     253             : void o(KeySet *ks)
     254             : {
     255             :         Key *current;
     256             :         Key *shared = keyNew (0);
     257             :         keySetMeta(shared, "shared", "this metadata should be shared among many keys");
     258             : 
     259             :         ksRewind(ks);
     260             :         while ((current = ksNext(ks)) != 0)
     261             :         {
     262             :                 if (needs_shared_data(current)) keyCopyMeta(current, shared, "shared");
     263             :         }
     264             : }
     265             :  * @endcode
     266             :  *
     267             :  * @post keyGetMeta(source, metaName) == keyGetMeta(dest, metaName)
     268             :  *
     269             :  * @retval 1 if was successfully copied
     270             :  * @retval 0 if the metadata in dest was removed too
     271             :  * @retval -1 on null pointers (source or dest)
     272             :  * @retval -1 on memory problems
     273             :  * @param dest the destination where the metadata should be copied too
     274             :  * @param source the key where the metadata should be copied from
     275             :  * @param metaName the name of the metadata which should be copied
     276             :  * @ingroup keymeta
     277             :  */
     278        1454 : int keyCopyMeta (Key * dest, const Key * source, const char * metaName)
     279             : {
     280             :         Key * ret;
     281             : 
     282        1454 :         if (!source) return -1;
     283        1452 :         if (!dest) return -1;
     284        1452 :         if (dest->flags & KEY_FLAG_RO_META) return -1;
     285             : 
     286        1448 :         ret = (Key *) keyGetMeta (source, metaName);
     287             : 
     288        1448 :         if (!ret)
     289             :         {
     290             :                 /*Make sure that dest also does not have metaName*/
     291           2 :                 if (dest->meta)
     292             :                 {
     293             :                         Key * r;
     294           0 :                         r = ksLookup (dest->meta, ret, KDB_O_POP);
     295           0 :                         if (r)
     296             :                         {
     297             :                                 /*It was already there, so lets drop that one*/
     298           0 :                                 keyDel (r);
     299             :                         }
     300             :                 }
     301             :                 return 0;
     302             :         }
     303             : 
     304             :         /*Lets have a look if the key is already inserted.*/
     305        1446 :         if (dest->meta)
     306             :         {
     307             :                 Key * r;
     308        1040 :                 r = ksLookup (dest->meta, ret, KDB_O_POP);
     309        1040 :                 if (r && r != ret)
     310             :                 {
     311             :                         /*It was already there, so lets drop that one*/
     312           6 :                         keyDel (r);
     313             :                 }
     314             :         }
     315             :         else
     316             :         {
     317             :                 /*Create a new place for meta information.*/
     318         406 :                 dest->meta = ksNew (0, KS_END);
     319         406 :                 if (!dest->meta)
     320             :                 {
     321             :                         return -1;
     322             :                 }
     323             :         }
     324             : 
     325             :         // now we can simply append that key
     326        1446 :         ksAppendKey (dest->meta, ret);
     327             : 
     328        1446 :         return 1;
     329             : }
     330             : 
     331             : /**Do a shallow copy of all metadata from source to dest.
     332             :  *
     333             :  * The key dest will additionally have all metadata
     334             :  * the source had.
     335             :  * Meta data not present in source will not be changed.
     336             :  * Meta data which was present in source and dest will
     337             :  * be overwritten.
     338             :  *
     339             :  * For example the metadata type is copied into the
     340             :  * Key k:
     341             :  *
     342             :  * @snippet keyMeta.c Basic Copy All
     343             :  *
     344             :  * The main purpose of this function is for plugins or
     345             :  * applications which want to add the same metadata to
     346             :  * n keys. When you do that with keySetMeta() it will
     347             :  * take n times the memory for the key. This can be
     348             :  * considerable amount of memory for many keys with
     349             :  * some metadata for each.
     350             :  *
     351             :  * To avoid that problem you can use keyCopyAllMeta()
     352             :  * or keyCopyMeta():
     353             :  *
     354             :  * @snippet keyMeta.c Shared Meta All
     355             :  *
     356             :  * @post for every metaName present in source: keyGetMeta(source, metaName) == keyGetMeta(dest, metaName)
     357             :  *
     358             :  * @retval 1 if was successfully copied
     359             :  * @retval 0 if source did not have any metadata
     360             :  * @retval -1 on null pointer of dest or source
     361             :  * @retval -1 on memory problems
     362             :  * @param dest the destination where the metadata should be copied too
     363             :  * @param source the key where the metadata should be copied from
     364             :  * @ingroup keymeta
     365             :  */
     366      115801 : int keyCopyAllMeta (Key * dest, const Key * source)
     367             : {
     368      115801 :         if (!source) return -1;
     369      115778 :         if (!dest) return -1;
     370      115778 :         if (dest->flags & KEY_FLAG_RO_META) return -1;
     371             : 
     372      115774 :         if (source->meta)
     373             :         {
     374             :                 /*Make sure that dest also does not have metaName*/
     375      113838 :                 if (dest->meta)
     376             :                 {
     377      111469 :                         ksAppend (dest->meta, source->meta);
     378             :                 }
     379             :                 else
     380             :                 {
     381        2369 :                         dest->meta = ksDup (source->meta);
     382             :                 }
     383             :                 return 1;
     384             :         }
     385             : 
     386             :         return 0;
     387             : }
     388             : 
     389             : /** Returns the value of a meta-information given by name.
     390             :  *
     391             :  * You are not allowed to modify the resulting key.
     392             :  *
     393             :  * @code
     394             : int f(Key *k)
     395             : {
     396             :         if (!strcmp(keyValue(keyGetMeta(k, "type")), "boolean"))
     397             :         {
     398             :                 // the type of the key is boolean
     399             :         }
     400             : }
     401             :  * @endcode
     402             :  *
     403             :  * @note You must not delete or change the returned key,
     404             :  *    use keySetMeta() if you want to delete or change it.
     405             :  *
     406             :  * @param key the key object to work with
     407             :  * @param metaName the name of the meta information you want the value from
     408             :  * @retval 0 if the key or metaName is 0
     409             :  * @retval 0 if no such metaName is found
     410             :  * @return value of meta-information if meta-information is found
     411             :  * @see keySetMeta()
     412             :  * @ingroup keymeta
     413             :  **/
     414    17072156 : const Key * keyGetMeta (const Key * key, const char * metaName)
     415             : {
     416             :         Key * ret;
     417             :         Key * search;
     418             : 
     419    17072156 :         if (!key) return 0;
     420    17068740 :         if (!metaName) return 0;
     421    17068740 :         if (!key->meta) return 0;
     422             : 
     423     3639527 :         search = keyNew (0);
     424     3639527 :         elektraKeySetName (search, metaName, KEY_META_NAME | KEY_EMPTY_NAME);
     425             : 
     426     3639527 :         ret = ksLookup (key->meta, search, 0);
     427             : 
     428     3639527 :         keyDel (search);
     429             : 
     430     3639527 :         return ret;
     431             : }
     432             : 
     433             : 
     434             : /**Set a new meta-information.
     435             :  *
     436             :  * Will set a new meta-information pair consisting of
     437             :  * metaName and newMetaString.
     438             :  *
     439             :  * Will add a new Pair for meta-information if metaName was
     440             :  * not added up to now.
     441             :  *
     442             :  * It will modify an existing Pair of meta-information if the
     443             :  * the metaName was inserted already.
     444             :  *
     445             :  * It will remove a meta information if newMetaString is 0.
     446             :  *
     447             :  * @param key the key object to work with
     448             :  * @param metaName the name of the meta information where you
     449             :  *                 want to change the value
     450             :  * @param newMetaString the new value for the meta information
     451             :  * @retval -1 on error if key or metaName is 0, out of memory
     452             :  *         or names are not valid
     453             :  * @retval 0 if the meta-information for metaName was removed
     454             :  * @return size (>0) of newMetaString if meta-information was
     455             :  *         successfully added
     456             :  * @see keyGetMeta()
     457             :  * @ingroup keymeta
     458             :  **/
     459     7717351 : ssize_t keySetMeta (Key * key, const char * metaName, const char * newMetaString)
     460             : {
     461             :         Key * toSet;
     462             :         char * metaStringDup;
     463             :         ssize_t metaNameSize;
     464     7717351 :         ssize_t metaStringSize = 0;
     465             : 
     466     7717351 :         if (!key) return -1;
     467     7717273 :         if (key->flags & KEY_FLAG_RO_META) return -1;
     468     7717255 :         if (!metaName) return -1;
     469     7717255 :         metaNameSize = elektraStrLen (metaName);
     470     7717255 :         if (metaNameSize == -1) return -1;
     471     7717255 :         if (newMetaString) metaStringSize = elektraStrLen (newMetaString);
     472             : 
     473             :         // optimization: we have nothing and want to remove something:
     474     7717255 :         if (!key->meta && !newMetaString) return 0;
     475             : 
     476     2538876 :         toSet = keyNew (0);
     477     2538876 :         if (!toSet) return -1;
     478             : 
     479     2538876 :         elektraKeySetName (toSet, metaName, KEY_META_NAME | KEY_EMPTY_NAME);
     480             : 
     481             :         /*Lets have a look if the key is already inserted.*/
     482     2538876 :         if (key->meta)
     483             :         {
     484             :                 Key * ret;
     485     2070134 :                 ret = ksLookup (key->meta, toSet, KDB_O_POP);
     486     2070134 :                 if (ret)
     487             :                 {
     488             :                         /*It was already there, so lets drop that one*/
     489      277907 :                         keyDel (ret);
     490      277907 :                         key->flags |= KEY_FLAG_SYNC;
     491             :                 }
     492             :         }
     493             : 
     494     2538876 :         if (newMetaString)
     495             :         {
     496             :                 /*Add the meta information to the key*/
     497     1335773 :                 metaStringDup = elektraStrNDup (newMetaString, metaStringSize);
     498     1335773 :                 if (!metaStringDup)
     499             :                 {
     500             :                         // TODO: actually we might already have changed
     501             :                         // the key
     502           0 :                         keyDel (toSet);
     503           0 :                         return -1;
     504             :                 }
     505             : 
     506     1335773 :                 if (toSet->data.v && !test_bit (toSet->flags, KEY_FLAG_MMAP_DATA)) elektraFree (toSet->data.v);
     507     1335773 :                 clear_bit (toSet->flags, (keyflag_t) KEY_FLAG_MMAP_DATA);
     508     1335773 :                 toSet->data.c = metaStringDup;
     509     1335773 :                 toSet->dataSize = metaStringSize;
     510             :         }
     511             :         else
     512             :         {
     513             :                 /*The request is to remove the meta string.
     514             :                   So simply drop it.*/
     515     1203103 :                 keyDel (toSet);
     516     1203103 :                 return 0;
     517             :         }
     518             : 
     519     1335773 :         if (!key->meta)
     520             :         {
     521             :                 /*Create a new place for meta information.*/
     522      468742 :                 key->meta = ksNew (0, KS_END);
     523      468742 :                 if (!key->meta)
     524             :                 {
     525           0 :                         keyDel (toSet);
     526           0 :                         return -1;
     527             :                 }
     528             :         }
     529             : 
     530     1335773 :         set_bit (toSet->flags, KEY_FLAG_RO_NAME);
     531     1335773 :         set_bit (toSet->flags, KEY_FLAG_RO_VALUE);
     532     1335773 :         set_bit (toSet->flags, KEY_FLAG_RO_META);
     533             : 
     534     1335773 :         ksAppendKey (key->meta, toSet);
     535     1335773 :         key->flags |= KEY_FLAG_SYNC;
     536     1335773 :         return metaStringSize;
     537             : }

Generated by: LCOV version 1.13