mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 13:12:46 +00:00 
			
		
		
		
	LibC: Implement mkstemps() in stdlib and add a test
				
					
				
			`mkstemps` generates a unique temporary file name from a pattern like `prefixXXXXXXsuffix` where `prefix` and `suffix` can be any string with only characters that are valid in a filename. The second parameter is the length of the suffix. `mkstemp` is `mkstemps` with suffix length 0, so to avoid code duplication it calls `mkstemps`. It is unlikely this has any significant performance impact on SerenityOS. `generate_unique_filename` now takes the suffix length as a `size_t`. The original behavior of this function is preserved when specifying a suffix length of 0. All original uses of this function have been adapted. `mkstemps()` was added because it is required by version 4.6.3 of the ccache port.
This commit is contained in:
		
							parent
							
								
									678d385a3f
								
							
						
					
					
						commit
						7c93eabffe
					
				
					 3 changed files with 68 additions and 6 deletions
				
			
		|  | @ -121,3 +121,57 @@ TEST_CASE(test_mkstemp_unique_filename) | |||
| 
 | ||||
|     munmap(ptr, sizeof(*ptr)); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE(test_mkstemps_unique_filename) | ||||
| { | ||||
|     u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); | ||||
|     EXPECT_NE(ptr, MAP_FAILED); | ||||
| 
 | ||||
|     if (fork() == 0) { | ||||
|         char path[] = "/tmp/test.mkstemps.prefixXXXXXXsuffix"; | ||||
|         auto fd = mkstemps(path, 6); | ||||
|         EXPECT_NE(fd, -1); | ||||
| 
 | ||||
|         auto temp_path_or_error = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); | ||||
|         EXPECT(!temp_path_or_error.is_error()); | ||||
| 
 | ||||
|         auto temp_path = temp_path_or_error.release_value(); | ||||
|         EXPECT(temp_path.characters()); | ||||
| 
 | ||||
|         close(fd); | ||||
|         unlink(path); | ||||
| 
 | ||||
|         EXPECT(temp_path.starts_with("/tmp/test.mkstemps.prefix"sv)); | ||||
|         EXPECT(temp_path.ends_with("suffix"sv)); | ||||
|         EXPECT_EQ(strlen(path), temp_path.length()); | ||||
| 
 | ||||
|         memcpy(&ptr[0], temp_path.characters(), temp_path.length()); | ||||
| 
 | ||||
|         exit(EXIT_SUCCESS); | ||||
|     } else { | ||||
|         wait(NULL); | ||||
| 
 | ||||
|         auto path1 = String::formatted("{}", reinterpret_cast<char const*>(ptr)); | ||||
| 
 | ||||
|         char path[] = "/tmp/test.mkstemps.prefixXXXXXXsuffix"; | ||||
|         auto fd = mkstemps(path, 6); | ||||
|         EXPECT(fd != -1); | ||||
| 
 | ||||
|         auto path2_or_error = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); | ||||
|         EXPECT(!path2_or_error.is_error()); | ||||
| 
 | ||||
|         auto path2 = path2_or_error.release_value(); | ||||
|         EXPECT(path2.characters()); | ||||
| 
 | ||||
|         close(fd); | ||||
|         unlink(path); | ||||
| 
 | ||||
|         EXPECT(path2.starts_with("/tmp/test.mkstemps.prefix"sv)); | ||||
|         EXPECT(path2.ends_with("suffix"sv)); | ||||
|         EXPECT_EQ(strlen(path), path2.length()); | ||||
| 
 | ||||
|         EXPECT_NE(path1, path2); | ||||
|     } | ||||
| 
 | ||||
|     munmap(ptr, sizeof(*ptr)); | ||||
| } | ||||
|  |  | |||
|  | @ -161,14 +161,14 @@ static bool is_either(char* str, int offset, char lower, char upper) | |||
| } | ||||
| 
 | ||||
| template<typename Callback> | ||||
| inline int generate_unique_filename(char* pattern, Callback callback) | ||||
| inline int generate_unique_filename(char* pattern, size_t suffix_length, Callback callback) | ||||
| { | ||||
|     size_t length = strlen(pattern); | ||||
| 
 | ||||
|     if (length < 6 || memcmp(pattern + length - 6, "XXXXXX", 6)) | ||||
|     if (length < 6 + suffix_length || memcmp(pattern + length - 6 - suffix_length, "XXXXXX", 6)) | ||||
|         return EINVAL; | ||||
| 
 | ||||
|     size_t start = length - 6; | ||||
|     size_t start = length - 6 - suffix_length; | ||||
| 
 | ||||
|     constexpr char random_characters[] = "abcdefghijklmnopqrstuvwxyz0123456789"; | ||||
| 
 | ||||
|  | @ -828,7 +828,7 @@ int system(char const* command) | |||
| // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mktemp.html
 | ||||
| char* mktemp(char* pattern) | ||||
| { | ||||
|     auto error = generate_unique_filename(pattern, [&] { | ||||
|     auto error = generate_unique_filename(pattern, 0, [&] { | ||||
|         struct stat st; | ||||
|         int rc = lstat(pattern, &st); | ||||
|         if (rc < 0 && errno == ENOENT) | ||||
|  | @ -845,8 +845,15 @@ char* mktemp(char* pattern) | |||
| // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html
 | ||||
| int mkstemp(char* pattern) | ||||
| { | ||||
|     return mkstemps(pattern, 0); | ||||
| } | ||||
| 
 | ||||
| // https://man7.org/linux/man-pages/man3/mkstemps.3.html
 | ||||
| int mkstemps(char* pattern, int suffix_length) | ||||
| { | ||||
|     VERIFY(suffix_length >= 0); | ||||
|     int fd = -1; | ||||
|     auto error = generate_unique_filename(pattern, [&] { | ||||
|     auto error = generate_unique_filename(pattern, static_cast<size_t>(suffix_length), [&] { | ||||
|         fd = open(pattern, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); // I'm using the flags I saw glibc using.
 | ||||
|         if (fd >= 0) | ||||
|             return IterationDecision::Break; | ||||
|  | @ -862,7 +869,7 @@ int mkstemp(char* pattern) | |||
| // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html
 | ||||
| char* mkdtemp(char* pattern) | ||||
| { | ||||
|     auto error = generate_unique_filename(pattern, [&] { | ||||
|     auto error = generate_unique_filename(pattern, 0, [&] { | ||||
|         if (mkdir(pattern, 0700) == 0) | ||||
|             return IterationDecision::Break; | ||||
|         return IterationDecision::Continue; | ||||
|  |  | |||
|  | @ -58,6 +58,7 @@ double atof(char const*); | |||
| int system(char const* command); | ||||
| char* mktemp(char*); | ||||
| int mkstemp(char*); | ||||
| int mkstemps(char*, int); | ||||
| char* mkdtemp(char*); | ||||
| void* bsearch(void const* key, void const* base, size_t nmemb, size_t size, int (*compar)(void const*, void const*)); | ||||
| int mblen(char const*, size_t); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 EWouters
						EWouters