+
+static size_t getpw_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+static bool errno_is_user_doesnt_exist(int error) {
+ /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+ * not found. */
+ return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
+}
+
+int getpwnam_malloc(const char *name, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
+ * free() the returned sructured! */
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ /* getpwnam() may fail with ENOENT if /etc/passwd is missing. For us that is equivalent to
+ * the name not being defined. */
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+static size_t getgr_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+int getgrnam_malloc(const char *name, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getgrgid_malloc(gid_t gid, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}