On Tue, Feb 19, 2013 at 08:52:49PM -0500, Rick Macklem wrote: > > > > I cannot find how to get information about maximum buffer size for > > the getpwnam_r() function. This information should be returned by > > sysconf(_SC_GETPW_R_SIZE_MAX), but since it does not work on FreeBSD > > it is necessary to guess its size. Original value is 128 and it works > > for somebody, 1024 works for your environment, but it can fail for > > another environment. > > > > SUSv4 specifies "Storage referenced by the structure is allocated from > > the memory provided with the buffer parameter", but then tells about > > groups > > in EXAMPLE for getpwnam_r() "Note that sysconf(_SC_GETPW_R_SIZE_MAX) > > may > > return -1 if there is no hard limit on the size of the buffer needed > > to > > store all the groups returned". > > > > malloc() can give overhead, but that function can try to call > > getpwnam_r() > > with buffer allocated from stack and if getpwnam_r() failed with > > ERANGE > > use dynamically allocated buffer. > > > > #define PWBUF_SIZE_INI (2 * MAXLOGNAME + 2 * MAXPATHLEN + > > _PASSWORD_LEN + 1) > > #define PWBUF_SIZE_INC 128 > > > > char bufs[2 * MAXLOGNAME + MAXPATHLEN + PASSWORD_LEN + 1 + 32]; > > > > error = getpwnam_r(lname, &pwd, bufs, sizeof(bufs), &pw); > > if (pw != NULL) { > > *uidp = pw->pw_uid; > > return (GSS_S_COMPLETE); > > } else if (error != ERANGE) > > return (GSS_S_FAILURE); > > > > size = PWBUF_SIZE_INI; > > for (;;) { > > size += PWBUF_SIZE_INC; > > buf = malloc(size); > > if (buf == NULL) > > return (GSS_S_FAILURE); > > error = getpwnam_r(lname, &pwd, buf, size, &pw); > > free(buf); > > if (pw != NULL) { > > *uidp = pw->pw_uid; > > return (GSS_S_COMPLETE); > > } else { > > if (error == ERANGE && > > size <= SIZE_MAX - PWBUF_SIZE_INC) > > continue; > > return (GSS_S_FAILURE); > > } > > } > > Just my opinion, but I think the above is a good approach. > (ie. First trying a fairly large buffer on the stack that > will succeed 99.99% of the time, but check for ERANGE and > loop trying progressively larger malloc'd buffers until > it stops reporting ERANGE.) > > I suspect the overheads behind getpwnam_r() are larger than > the difference between using a buffer on the stack vs malloc, > so I think it should use a fairly large buffer the first time. > > Personally, I might have coded it as a single do { } while(), > with the first attempt in it, but that's just personal stylistic > taste. (You can check for buf != bufs before doing a free() of it.) > And, if you wanted to be clever, the code could use a static "bufsiz_hint", > which is set to the largest size needed sofar and that is used as > the initial malloc size. That way it wouldn't loop as much for a > site with huge passwd entries. (An entire bio in the GECOS field or ???) > Thanks for the review. Another variant. This is a program that can be used for verifying correctness of the function, just change PWBUF_SIZE_* values and put some printfs to see when buffer is reallocated. sizehint is updated only when malloc() succeeded. --------------------------------- #include <sys/param.h> #include <sys/limits.h> #include <gssapi/gssapi.h> #include <errno.h> #include <limits.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static int getpwnam_r_func(const char *name, uid_t *uidp) { #define PWBUF_SIZE_INI (2 * MAXLOGNAME + MAXPATHLEN + _PASSWORD_LEN) #define PWBUF_SIZE_INC 128 static size_t sizehint = PWBUF_SIZE_INI; struct passwd pwd; struct passwd *pw; char *buf; size_t size; int error; char lname[MAXLOGNAME]; char bufs[PWBUF_SIZE_INI]; strncpy(lname, name, sizeof(lname)); buf = bufs; size = sizeof(bufs); for (;;) { error = getpwnam_r(lname, &pwd, buf, size, &pw); if (buf != bufs) free(buf); if (pw != NULL) { *uidp = pw->pw_uid; return (GSS_S_COMPLETE); } else if (error != ERANGE || size > SIZE_MAX - PWBUF_SIZE_INC) return (GSS_S_FAILURE); if (size != sizehint) size = sizehint; else size += PWBUF_SIZE_INC; buf = malloc(size); if (buf == NULL) return (GSS_S_FAILURE); sizehint = size; } } int main(void) { const char *str[] = { "man", "root", "q", "bin", NULL }; uid_t uid; u_int i; for (i = 0; str[i] != NULL; ++i) { printf("%-20s\t", str[i]); if (getpwnam_r_func(str[i], &uid) == GSS_S_COMPLETE) printf("%u\n", uid); else printf("not found\n"); } return (0); } ---------------------------------Received on Wed Feb 20 2013 - 11:28:30 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:35 UTC