From 043b2483585a2d8168e0fde8b37054733a31f263 Mon Sep 17 00:00:00 2001
From: haozi007 <liuhao27@huawei.com>
Date: Mon, 25 Jul 2022 15:36:23 +0800
Subject: [PATCH] fix HOME env of container unset error

Signed-off-by: haozi007 <liuhao27@huawei.com>
---
 src/lxc/isulad_utils.c | 210 ++++++++++++++++++++++++++++++++++++++++-
 src/lxc/isulad_utils.h |   3 +
 src/lxc/start.c        |  14 +--
 3 files changed, 216 insertions(+), 11 deletions(-)

diff --git a/src/lxc/isulad_utils.c b/src/lxc/isulad_utils.c
index 15d9323..cd7fca8 100644
--- a/src/lxc/isulad_utils.c
+++ b/src/lxc/isulad_utils.c
@@ -6,6 +6,10 @@
  * Create: 2020-04-11
 ******************************************************************************/
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -13,6 +17,10 @@
 #include <fcntl.h>
 #include <pwd.h>
 #include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio_ext.h>
 
 #include "isulad_utils.h"
 #include "log.h"
@@ -244,20 +252,34 @@ out:
 // isulad: set env home in container
 int lxc_setup_env_home(uid_t uid)
 {
+#define __PASSWD_FILE__ "/etc/passwd"
 	char *homedir = "/"; // default home dir is /
+	FILE *stream = NULL;
 	struct passwd pw, *pwbufp = NULL;
 	char buf[BUFSIZ];
-	int ret;
 
-	ret = getpwuid_r(uid, &pw, buf, sizeof(buf), &pwbufp);
-	if ((ret == 0) && (pwbufp != NULL) && (pwbufp->pw_uid == uid)) {
-		homedir = pwbufp->pw_dir;
+	stream = fopen_cloexec(__PASSWD_FILE__, "r");
+	if (stream == NULL) {
+		SYSWARN("Failed to open %s", __PASSWD_FILE__);
 		goto set_env;
 	}
 
+#if IS_BIONIC
+	while (util_getpwent_r(stream, &pw, buf, sizeof(buf), &pwbufp) == 0 && pwbufp != NULL) {
+#else
+	while (fgetpwent_r(stream, &pw, buf, sizeof(buf), &pwbufp) == 0 && pwbufp != NULL) {
+#endif
+		if (pwbufp->pw_uid == uid) {
+			homedir = pwbufp->pw_dir;
+			goto set_env;
+		}
+	}
 	WARN("User invalid, can not find user '%u'", uid);
 
 set_env:
+	if (stream)
+		fclose(stream);
+
 	// if we didn't configure HOME, set it based on uid
 	if (setenv("HOME", homedir, 0) < 0) {
 		SYSERROR("Unable to set env 'HOME'");
@@ -317,3 +339,183 @@ bool is_non_negative_num(const char *s)
 	}
 	return true;
 }
+
+static int hold_int(const char delim, bool required, char **src, unsigned int *dst)
+{
+    unsigned long long int res = 0;
+    char *err_str = NULL;
+
+    // ensure *src not a empty string
+    if (**src == '\0') {
+        ERROR("Empty subject on given entrie is not allowed.");
+        return -1;
+    }
+
+    errno = 0;
+    // covert string to long long
+    res = strtoull(*src, &err_str, 0);
+    if (errno != 0 && errno != ERANGE) {
+        ERROR("Parse int from string failed.");
+        return -1;
+    }
+
+    // **src is not a digit
+    if (err_str == *src) {
+        if (!required) {
+            ERROR("Integer part is missing.");
+            return -1;
+        }
+        // if required, just set 0
+        *dst = 0;
+    } else {
+        if (sizeof(void *) > 4 && res > UINT_MAX) { // make sure 64-bit platform behave same as 32-bit
+            res = UINT_MAX;
+        }
+        res = res & UINT_MAX;
+        *dst = (uint32_t)res;
+    }
+
+    // normal case
+    if (*err_str == delim) {
+        err_str++;
+    } else if (*err_str != '\0') {
+        ERROR("Invalid digit string.");
+        return -1;
+    }
+
+    *src = err_str; // update src to next valid context in line.
+    return 0;
+}
+
+static void hold_string(const char delim, char **src, char **dst)
+{
+    for (*dst = *src; **src != delim; ++(*src)) {
+        if (**src == '\0') {
+            break;
+        }
+    }
+
+    if (**src == delim) {
+        **src = '\0';
+        ++(*src);
+    }
+}
+
+static int parse_line_pw(const char delim, char *line, struct passwd *result)
+{
+    int ret = 0;
+    bool required = false;
+    char *walker = NULL;
+
+    walker = strpbrk(line, "\n");
+    if (walker != NULL) {
+        // clear newline char
+        *walker = '\0';
+    }
+
+    hold_string(delim, &line, &result->pw_name);
+
+    required = (result->pw_name[0] == '+' || result->pw_name[0] == '-') ? true : false;
+
+    hold_string(delim, &line, &result->pw_passwd);
+
+    ret = hold_int(delim, required, &line, &result->pw_uid);
+    if (ret != 0) {
+        // a legitimate line must have uid
+        ERROR("Parse uid error.");
+        return ret;
+    }
+
+    ret = hold_int(delim, required, &line, &result->pw_gid);
+    if (ret != 0) {
+        // it's ok to not provide gid
+        ERROR("Parse gid error.");
+        return ret;
+    }
+
+    hold_string(delim, &line, &result->pw_gecos);
+
+    hold_string(delim, &line, &result->pw_dir);
+
+    result->pw_shell = line;
+    return 0;
+}
+
+char *util_left_trim_space(char *str)
+{
+    char *begin = str;
+    char *tmp = str;
+    while (isspace(*begin)) {
+        begin++;
+    }
+    while ((*tmp++ = *begin++)) {
+    }
+    return str;
+}
+
+int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result)
+{
+    const char delim = ':';
+    char *buff_end = NULL;
+    char *walker = NULL;
+    bool got = false;
+    int ret = 0;
+
+    if (stream == NULL || resbuf == NULL || buffer == NULL || result == NULL) {
+        ERROR("Password obj, params is NULL.");
+        return -1;
+    }
+
+    if (buflen <= 1) {
+        ERROR("Inadequate buffer length was given.");
+        return -1;
+    }
+
+    buff_end = buffer + buflen - 1;
+    flockfile(stream);
+
+    while (1) {
+        *buff_end = '\xff';
+        walker = fgets_unlocked(buffer, buflen, stream);
+        // if get NULL string
+        if (walker == NULL) {
+            *result = NULL;
+            // reach end of file, return error
+            if (feof(stream)) {
+                ret = ENOENT;
+                goto out;
+            }
+            // overflow buffer
+            ret = ERANGE;
+            goto out;
+        }
+        // just overflow last char in buffer
+        if (*buff_end != '\xff') {
+            *result = NULL;
+            ret = ERANGE;
+            goto out;
+        }
+
+        (void)util_left_trim_space(buffer);
+        // skip comment line and empty line
+        if (walker[0] == '#' || walker[0] == '\0') {
+            continue;
+        }
+
+        if (parse_line_pw(delim, walker, resbuf) == 0) {
+            got = true;
+            break;
+        }
+    }
+    if (!got) {
+        *result = NULL;
+        ret = ERANGE;
+        goto out;
+    }
+
+    *result = resbuf;
+    ret = 0;
+out:
+    funlockfile(stream);
+    return ret;
+}
\ No newline at end of file
diff --git a/src/lxc/isulad_utils.h b/src/lxc/isulad_utils.h
index 345f511..7a5eb89 100644
--- a/src/lxc/isulad_utils.h
+++ b/src/lxc/isulad_utils.h
@@ -10,6 +10,7 @@
 
 #include <stdio.h>
 #include <stdbool.h>
+#include <pwd.h>
 
 /* isulad: replace space with SPACE_MAGIC_STR */
 #define SPACE_MAGIC_STR "[#)"
@@ -96,4 +97,6 @@ extern bool lxc_process_alive(pid_t pid, unsigned long long start_time);
 
 extern bool is_non_negative_num(const char *s);
 
+int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result);
+
 #endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index f82df34..6fe1203 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1727,6 +1727,13 @@ static int do_start(void *data)
 	new_uid = handler->conf->init_uid;
 	new_gid = handler->conf->init_gid;
 
+#ifdef HAVE_ISULAD
+	// isulad: set env home in container, must before "Avoid unnecessary syscalls."
+	if (lxc_setup_env_home(new_uid) < 0) {
+		goto out_warn_father;
+	}
+#endif
+
 	/* Avoid unnecessary syscalls. */
 	if (new_uid == nsuid)
 		new_uid = LXC_INVALID_UID;
@@ -1734,13 +1741,6 @@ static int do_start(void *data)
 	if (new_gid == nsgid)
 		new_gid = LXC_INVALID_GID;
 
-#ifdef HAVE_ISULAD
-	// isulad: set env home in container
-	if (lxc_setup_env_home(new_uid) < 0) {
-		goto out_warn_father;
-	}
-#endif
-
 	/* Make sure that the processes STDIO is correctly owned by the user that we are switching to */
 	ret = fix_stdio_permissions(new_uid);
 	if (ret)
-- 
2.25.1

