diff options
Diffstat (limited to 'nss')
-rw-r--r-- | nss/getXXbyYY_r.c | 110 | ||||
-rw-r--r-- | nss/getnssent_r.c | 27 | ||||
-rw-r--r-- | nss/nsswitch.c | 3 | ||||
-rw-r--r-- | nss/nsswitch.h | 3 |
4 files changed, 137 insertions, 6 deletions
diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c index 113c687e06..93af2538ec 100644 --- a/nss/getXXbyYY_r.c +++ b/nss/getXXbyYY_r.c @@ -131,6 +131,52 @@ # define AF_VAL AF_INET #endif + +/* Set defaults for merge functions that haven't been defined. */ +#ifndef DEEPCOPY_FN +static inline int +__copy_einval (LOOKUP_TYPE a, + const size_t b, + LOOKUP_TYPE *c, + char *d, + char **e) +{ + return EINVAL; +} +# define DEEPCOPY_FN __copy_einval +#endif + +#ifndef MERGE_FN +static inline int +__merge_einval (LOOKUP_TYPE *a, + char *b, + char *c, + size_t d, + LOOKUP_TYPE *e, + char *f) +{ + return EINVAL; +} +# define MERGE_FN __merge_einval +#endif + +#define CHECK_MERGE(err, status) \ + ({ \ + do \ + { \ + if (err) \ + { \ + __set_errno (err); \ + if (err == ERANGE) \ + status = NSS_STATUS_TRYAGAIN; \ + else \ + status = NSS_STATUS_UNAVAIL; \ + break; \ + } \ + } \ + while (0); \ + }) + /* Type of the lookup function we need here. */ typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *, size_t, int * H_ERRNO_PARM @@ -152,13 +198,16 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer, static service_user *startp; static lookup_function start_fct; service_user *nip; + int do_merge = 0; + LOOKUP_TYPE mergegrp; + char *mergebuf = NULL; + char *endptr = NULL; union { lookup_function l; void *ptr; } fct; - - int no_more; + int no_more, err; enum nss_status status = NSS_STATUS_UNAVAIL; #ifdef USE_NSCD int nscd_status; @@ -278,9 +327,66 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer, && errno == ERANGE) break; + if (do_merge) + { + + if (status == NSS_STATUS_SUCCESS) + { + /* The previous loop saved a buffer for merging. + Perform the merge now. */ + err = MERGE_FN (&mergegrp, mergebuf, endptr, buflen, resbuf, + buffer); + CHECK_MERGE (err,status); + do_merge = 0; + } + else + { + /* If the result wasn't SUCCESS, copy the saved buffer back + into the result buffer and set the status back to + NSS_STATUS_SUCCESS to match the previous pass through the + loop. + * If the next action is CONTINUE, it will overwrite the value + currently in the buffer and return the new value. + * If the next action is RETURN, we'll return the previously- + acquired values. + * If the next action is MERGE, then it will be added to the + buffer saved from the previous source. */ + err = DEEPCOPY_FN (mergegrp, buflen, resbuf, buffer, NULL); + CHECK_MERGE (err, status); + status = NSS_STATUS_SUCCESS; + } + } + + /* If we were are configured to merge this value with the next one, + save the current value of the group struct. */ + if (nss_next_action (nip, status) == NSS_ACTION_MERGE + && status == NSS_STATUS_SUCCESS) + { + /* Copy the current values into a buffer to be merged with the next + set of retrieved values. */ + if (mergebuf == NULL) + { + /* Only allocate once and reuse it for as many merges as we need + to perform. */ + mergebuf = malloc (buflen); + if (mergebuf == NULL) + { + __set_errno (ENOMEM); + status = NSS_STATUS_UNAVAIL; + break; + } + } + + err = DEEPCOPY_FN (*resbuf, buflen, &mergegrp, mergebuf, &endptr); + CHECK_MERGE (err, status); + do_merge = 1; + } + no_more = __nss_next2 (&nip, REENTRANT_NAME_STRING, REENTRANT2_NAME_STRING, &fct.ptr, status, 0); } + free (mergebuf); + mergebuf = NULL; #ifdef HANDLE_DIGITS_DOTS done: diff --git a/nss/getnssent_r.c b/nss/getnssent_r.c index 456907b018..f5092482ef 100644 --- a/nss/getnssent_r.c +++ b/nss/getnssent_r.c @@ -79,7 +79,18 @@ __nss_setent (const char *func_name, db_lookup_function lookup_fct, else status = DL_CALL_FCT (fct.f, (0)); - no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0); + + /* This is a special-case. When [SUCCESS=merge] is in play, + _nss_next2() will skip to the next database. Due to the + implementation of that function, we can't know whether we're + in an enumeration or an individual lookup, which behaves + differently with regards to merging. We'll treat SUCCESS as + an indication to start the enumeration at this database. */ + if (nss_next_action (*nip, status) == NSS_ACTION_MERGE) + no_more = 1; + else + no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0); + if (is_last_nip) *last_nip = *nip; } @@ -175,8 +186,18 @@ __nss_getent_r (const char *getent_func_name, do { - no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr, - status, 0); + /* This is a special-case. When [SUCCESS=merge] is in play, + _nss_next2() will skip to the next database. Due to the + implementation of that function, we can't know whether we're + in an enumeration or an individual lookup, which behaves + differently with regards to merging. We'll treat SUCCESS as + an indication to return the results here. */ + if (status == NSS_STATUS_SUCCESS + && nss_next_action (*nip, status) == NSS_ACTION_MERGE) + no_more = 1; + else + no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr, + status, 0); if (is_last_nip) *last_nip = *nip; diff --git a/nss/nsswitch.c b/nss/nsswitch.c index bb644cb373..d7706506f0 100644 --- a/nss/nsswitch.c +++ b/nss/nsswitch.c @@ -712,6 +712,9 @@ nss_parse_service_list (const char *line) else if (line - name == 8 && __strncasecmp (name, "CONTINUE", 8) == 0) action = NSS_ACTION_CONTINUE; + else if (line - name == 5 + && __strncasecmp (name, "MERGE", 5) == 0) + action = NSS_ACTION_MERGE; else goto finish; diff --git a/nss/nsswitch.h b/nss/nsswitch.h index 0074ee1d65..54c8b656f7 100644 --- a/nss/nsswitch.h +++ b/nss/nsswitch.h @@ -32,7 +32,8 @@ typedef enum { NSS_ACTION_CONTINUE, - NSS_ACTION_RETURN + NSS_ACTION_RETURN, + NSS_ACTION_MERGE } lookup_actions; |