forked from Bananymous/banan-os
				
			LibC: Implement getpw{nam,uid}_r
This commit is contained in:
		
							parent
							
								
									654e8bb7f6
								
							
						
					
					
						commit
						fda0ced72e
					
				|  | @ -1,118 +1,142 @@ | ||||||
| #include <BAN/Assert.h> |  | ||||||
| 
 |  | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
| 
 | 
 | ||||||
| static FILE* s_pwent_fp = nullptr; | static FILE* s_pwent_fp = nullptr; | ||||||
| static passwd s_pwent_struct; | static passwd s_pwent_struct; | ||||||
| 
 | 
 | ||||||
| static bool open_pwent() | static char* s_pwent_buffer = nullptr; | ||||||
| { | static size_t s_pwent_buffer_size = 0; | ||||||
| 	if (s_pwent_fp) |  | ||||||
| 		return true; |  | ||||||
| 	s_pwent_fp = fopen("/etc/passwd", "r"); |  | ||||||
| 	return s_pwent_fp; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void clear_pwent(passwd& passwd) |  | ||||||
| { |  | ||||||
| 	if (passwd.pw_name) |  | ||||||
| 		free(passwd.pw_name); |  | ||||||
| 	passwd.pw_name = nullptr; |  | ||||||
| 	if (passwd.pw_dir) |  | ||||||
| 		free(passwd.pw_dir); |  | ||||||
| 	passwd.pw_dir = nullptr; |  | ||||||
| 	if (passwd.pw_shell) |  | ||||||
| 		free(passwd.pw_shell); |  | ||||||
| 	passwd.pw_shell = nullptr; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void endpwent(void) | void endpwent(void) | ||||||
|  | { | ||||||
|  | 	if (s_pwent_fp) | ||||||
|  | 		fclose(s_pwent_fp); | ||||||
|  | 	s_pwent_fp = nullptr; | ||||||
|  | 
 | ||||||
|  | 	if (s_pwent_buffer) | ||||||
|  | 		free(s_pwent_buffer); | ||||||
|  | 	s_pwent_buffer = nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void setpwent(void) | ||||||
| { | { | ||||||
| 	if (!s_pwent_fp) | 	if (!s_pwent_fp) | ||||||
| 		return; | 		return; | ||||||
| 	fclose(s_pwent_fp); | 	fseek(s_pwent_fp, 0, SEEK_SET); | ||||||
| 	s_pwent_fp = nullptr; | } | ||||||
| 	clear_pwent(s_pwent_struct); | 
 | ||||||
|  | static int getpwent_impl(FILE* fp, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) | ||||||
|  | { | ||||||
|  | 	for (;;) | ||||||
|  | 	{ | ||||||
|  | 		if (fgets(buffer, bufsize, fp) == nullptr) | ||||||
|  | 		{ | ||||||
|  | 			if (ferror(fp)) | ||||||
|  | 				return errno; | ||||||
|  | 			*result = nullptr; | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		const size_t line_len = strlen(buffer); | ||||||
|  | 		if (line_len == 0) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (buffer[line_len - 1] == '\n') | ||||||
|  | 			buffer[line_len - 1] = '\0'; | ||||||
|  | 		else if (!feof(fp)) | ||||||
|  | 			return (errno = ERANGE); | ||||||
|  | 
 | ||||||
|  | #define GET_STRING() ({ \ | ||||||
|  | 			ptr = strchr(ptr, ':'); \ | ||||||
|  | 			if (ptr == nullptr) \ | ||||||
|  | 				continue; \ | ||||||
|  | 			*ptr++ = '\0'; \ | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | #define GET_INT() ({ \ | ||||||
|  | 				if (!isdigit(*ptr)) \ | ||||||
|  | 					continue; \ | ||||||
|  | 				long val = 0; \ | ||||||
|  | 				while (isdigit(*ptr)) \ | ||||||
|  | 					val = (val * 10) + (*ptr++ - '0'); \ | ||||||
|  | 				if (*ptr != ':') \ | ||||||
|  | 					continue; \ | ||||||
|  | 				*ptr++ = '\0'; \ | ||||||
|  | 				val; \ | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 		char* ptr = buffer; | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_name = ptr; | ||||||
|  | 		GET_STRING(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_passwd = ptr; | ||||||
|  | 		GET_STRING(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_uid = GET_INT(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_gid = GET_INT(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_gecos = ptr; | ||||||
|  | 		GET_STRING(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_dir = ptr; | ||||||
|  | 		GET_STRING(); | ||||||
|  | 
 | ||||||
|  | 		pwd->pw_shell = ptr; | ||||||
|  | 
 | ||||||
|  | 		*result = pwd; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct passwd* getpwent(void) | struct passwd* getpwent(void) | ||||||
| { | { | ||||||
| 	if (!s_pwent_fp) | 	if (s_pwent_fp == nullptr) | ||||||
| 		if (!open_pwent()) |  | ||||||
| 			return nullptr; |  | ||||||
| 	clear_pwent(s_pwent_struct); |  | ||||||
| 
 |  | ||||||
| 	static char buffer[4096]; |  | ||||||
| 	if (!fgets(buffer, sizeof(buffer), s_pwent_fp)) |  | ||||||
| 		return nullptr; |  | ||||||
| 
 |  | ||||||
| 	size_t buffer_len = strlen(buffer); |  | ||||||
| 
 |  | ||||||
| 	ASSERT(buffer[buffer_len - 1] == '\n'); |  | ||||||
| 	buffer[buffer_len - 1] = '\0'; |  | ||||||
| 	buffer_len--; |  | ||||||
| 
 |  | ||||||
| 	const char* ptr = buffer; |  | ||||||
| 	for (int i = 0; i < 7; i++) |  | ||||||
| 	{ | 	{ | ||||||
| 		char* end = strchr(ptr, ':'); | 		s_pwent_fp = fopen("/etc/passwd", "r"); | ||||||
| 		ASSERT((i < 6) ? end != nullptr : end == nullptr); | 		if (s_pwent_fp == nullptr) | ||||||
| 		if (!end) | 			return nullptr; | ||||||
| 			end = buffer + buffer_len; |  | ||||||
| 		*end = '\0'; |  | ||||||
| 
 |  | ||||||
| 		const size_t field_len = end - ptr; |  | ||||||
| 
 |  | ||||||
| 		switch (i) |  | ||||||
| 		{ |  | ||||||
| 			case 0: |  | ||||||
| 				s_pwent_struct.pw_name = strndup(ptr, field_len + 1); |  | ||||||
| 				if (!s_pwent_struct.pw_name) |  | ||||||
| 					return nullptr; |  | ||||||
| 				break; |  | ||||||
| 			case 1: |  | ||||||
| 				s_pwent_struct.pw_passwd = strndup(ptr, field_len + 1); |  | ||||||
| 				if (!s_pwent_struct.pw_passwd) |  | ||||||
| 					return nullptr; |  | ||||||
| 				break; |  | ||||||
| 			case 2: |  | ||||||
| 				ASSERT(1 <= field_len && field_len <= 9); |  | ||||||
| 				for (size_t j = 0; j < field_len; j++) |  | ||||||
| 					ASSERT(isdigit(ptr[j])); |  | ||||||
| 				s_pwent_struct.pw_uid = atoi(ptr); |  | ||||||
| 				break; |  | ||||||
| 			case 3: |  | ||||||
| 				ASSERT(1 <= field_len && field_len <= 9); |  | ||||||
| 				for (size_t j = 0; j < field_len; j++) |  | ||||||
| 					ASSERT(isdigit(ptr[j])); |  | ||||||
| 				s_pwent_struct.pw_gid = atoi(ptr); |  | ||||||
| 				break; |  | ||||||
| 			case 4: |  | ||||||
| 				s_pwent_struct.pw_gecos = strndup(ptr, field_len + 1); |  | ||||||
| 				if (!s_pwent_struct.pw_gecos) |  | ||||||
| 					return nullptr; |  | ||||||
| 				break; |  | ||||||
| 			case 5: |  | ||||||
| 				s_pwent_struct.pw_dir = strndup(ptr, field_len + 1); |  | ||||||
| 				if (!s_pwent_struct.pw_dir) |  | ||||||
| 					return nullptr; |  | ||||||
| 				break; |  | ||||||
| 			case 6: |  | ||||||
| 				s_pwent_struct.pw_shell = strndup(ptr, field_len + 1); |  | ||||||
| 				if (!s_pwent_struct.pw_shell) |  | ||||||
| 					return nullptr; |  | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		ptr = end + 1; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &s_pwent_struct; | 	if (s_pwent_buffer == nullptr) | ||||||
|  | 	{ | ||||||
|  | 		long size = sysconf(_SC_GETPW_R_SIZE_MAX); | ||||||
|  | 		if (size == -1) | ||||||
|  | 			size = 512; | ||||||
|  | 
 | ||||||
|  | 		s_pwent_buffer = static_cast<char*>(malloc(size)); | ||||||
|  | 		if (s_pwent_buffer == nullptr) | ||||||
|  | 			return nullptr; | ||||||
|  | 		s_pwent_buffer_size = size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const off_t old_offset = ftello(s_pwent_fp); | ||||||
|  | 
 | ||||||
|  | 	passwd* result; | ||||||
|  | 	for (;;) | ||||||
|  | 	{ | ||||||
|  | 		const int error = getpwent_impl(s_pwent_fp, &s_pwent_struct, s_pwent_buffer, s_pwent_buffer_size, &result); | ||||||
|  | 		if (error == 0) | ||||||
|  | 			break; | ||||||
|  | 		fseeko(s_pwent_fp, old_offset, SEEK_SET); | ||||||
|  | 		if (error != ERANGE) | ||||||
|  | 			return nullptr; | ||||||
|  | 
 | ||||||
|  | 		const size_t new_size = s_pwent_buffer_size * 2; | ||||||
|  | 		char* new_buffer = static_cast<char*>(realloc(s_pwent_buffer, new_size)); | ||||||
|  | 		if (new_buffer == nullptr) | ||||||
|  | 			return nullptr; | ||||||
|  | 
 | ||||||
|  | 		s_pwent_buffer = new_buffer; | ||||||
|  | 		s_pwent_buffer_size = new_size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct passwd* getpwnam(const char* name) | struct passwd* getpwnam(const char* name) | ||||||
|  | @ -135,9 +159,44 @@ struct passwd* getpwuid(uid_t uid) | ||||||
| 	return nullptr; | 	return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void setpwent(void) | int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) | ||||||
| { | { | ||||||
| 	if (!s_pwent_fp) | 	FILE* fp = fopen("/etc/passwd", "r"); | ||||||
| 		return; | 	if (fp == nullptr) | ||||||
| 	fseek(s_pwent_fp, 0, SEEK_SET); | 		return errno; | ||||||
|  | 
 | ||||||
|  | 	int ret = 0; | ||||||
|  | 	for (;;) | ||||||
|  | 	{ | ||||||
|  | 		if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result))) | ||||||
|  | 			break; | ||||||
|  | 		if (*result == nullptr) | ||||||
|  | 			break; | ||||||
|  | 		if (pwd->pw_uid == uid) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fclose(fp); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int getpwnam_r(const char* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) | ||||||
|  | { | ||||||
|  | 	FILE* fp = fopen("/etc/passwd", "r"); | ||||||
|  | 	if (fp == nullptr) | ||||||
|  | 		return errno; | ||||||
|  | 
 | ||||||
|  | 	int ret = 0; | ||||||
|  | 	for (;;) | ||||||
|  | 	{ | ||||||
|  | 		if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result))) | ||||||
|  | 			break; | ||||||
|  | 		if (*result == nullptr) | ||||||
|  | 			break; | ||||||
|  | 		if (strcmp(pwd->pw_name, name) == 0) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fclose(fp); | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue