Skip to content

Commit cfb51a3

Browse files
Unix: Default to a users-specific temp directory for extracting single-file apps
In .net core 3, single file apps run by extracting the bundled contents to a temp directory. The extraction directory is machine specific, and can be set through DOTNET_BUNDLE_EXTRACT_BASE_DIR environment variable. When this setting is not configured, the host tries to use certain default directories. On windows, extraction is within %TMPDIR%, which is user specific. On Unix systems $TMPDIR/.net if set, which may be user specific (ex: MAC) Otherwise, the extraction directory is within /var/tmp/ or /tmp/ which is common to all users, and may be locked by a specific user on first creation. Therefore, this change fixes this issue by defaulting the extraction base directory in Unix systems to `<temp-dir>/.net/<user-ID>` , where `<temp-dir>/.net/` has permission 0777, and `<temp-dir>/.net/<user-ID>/` has permission 01700. This fix will be migrated to coreclr/3.1 branch for servicing. Testing: Manual testing on Unix/Mac systems, since we don't have the setup to add automated tests with multiple users. Fixes https://github.com/dotnet/core-setup/issues/8882
1 parent 3467e97 commit cfb51a3

File tree

4 files changed

+85
-7
lines changed

4 files changed

+85
-7
lines changed

src/installer/corehost/cli/apphost/bundle/extractor.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ void extractor_t::determine_extraction_dir()
1919
{
2020
if (!pal::getenv(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR"), &m_extraction_dir))
2121
{
22-
if (!pal::get_temp_directory(m_extraction_dir))
22+
if (!pal::get_default_bundle_extraction_base_dir(m_extraction_dir))
2323
{
2424
trace::error(_X("Failure processing application bundle."));
2525
trace::error(_X("Failed to determine location for extracting embedded files."));
26-
trace::error(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR is not set, and temp-directory doesn't exist or is not readable/writable."));
26+
trace::error(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR is not set, and a read-write temp-directory couldn't be created."));
2727
throw StatusCode::BundleExtractionFailure;
2828
}
29-
30-
append_path(&m_extraction_dir, _X(".net"));
3129
}
3230

3331
pal::string_t host_name = strip_executable_ext(get_filename(m_bundle_path));

src/installer/corehost/cli/hostmisc/pal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ namespace pal
289289

290290
bool get_temp_directory(string_t& tmp_dir);
291291

292+
// Returns a platform-specific, user-private directory within get_temp_directory()
293+
// that can be used for extracting out components of a single-file app.
294+
bool get_default_bundle_extraction_base_dir(string_t& extraction_dir);
295+
292296
int xtoi(const char_t* input);
293297

294298
bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path);

src/installer/corehost/cli/hostmisc/pal.unix.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <fcntl.h>
1414
#include <fnmatch.h>
1515
#include <ctime>
16+
#include <pwd.h>
1617

1718
#if defined(__APPLE__)
1819
#include <mach-o/dyld.h>
@@ -306,7 +307,7 @@ bool is_read_write_able_directory(pal::string_t& dir)
306307
bool pal::get_temp_directory(pal::string_t& tmp_dir)
307308
{
308309
// First, check for the POSIX standard environment variable
309-
if (pal::getenv(_X("TMPDIR"), &tmp_dir))
310+
if (getenv(_X("TMPDIR"), &tmp_dir))
310311
{
311312
return is_read_write_able_directory(tmp_dir);
312313
}
@@ -331,6 +332,55 @@ bool pal::get_temp_directory(pal::string_t& tmp_dir)
331332
return false;
332333
}
333334

335+
bool pal::get_default_bundle_extraction_base_dir(pal::string_t& extraction_dir)
336+
{
337+
if (!get_temp_directory(extraction_dir))
338+
{
339+
return false;
340+
}
341+
342+
append_path(&extraction_dir, _X(".net"));
343+
pal::string_t dotnetdir(extraction_dir);
344+
345+
// getuid() is the real user ID, and the call has no defined errors.
346+
struct passwd* passwd = getpwuid(getuid());
347+
if (passwd == nullptr || passwd->pw_name == nullptr)
348+
{
349+
return false;
350+
}
351+
352+
append_path(&extraction_dir, passwd->pw_name);
353+
354+
if (is_read_write_able_directory(extraction_dir))
355+
{
356+
return true;
357+
}
358+
359+
// Create $TMPDIR/.net accessible to everyone
360+
if (::mkdir(dotnetdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0)
361+
{
362+
// In the above mkdir() system call, some permissions are strangely dropped!
363+
// Linux drops S_IWO and Mac drops S_IWG | S_IWO.
364+
// So these are again explicitly set by calling chmod()
365+
if (chmod(dotnetdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0)
366+
{
367+
return false;
368+
}
369+
}
370+
else if (errno != EEXIST)
371+
{
372+
return false;
373+
}
374+
375+
// Create $TMPDIR/.net/username accessible only to the user
376+
if (::mkdir(extraction_dir.c_str(), S_IRWXU | S_ISVTX) != 0 && errno != EEXIST)
377+
{
378+
return false;
379+
}
380+
381+
return is_read_write_able_directory(extraction_dir);
382+
}
383+
334384
bool pal::get_global_dotnet_dirs(std::vector<pal::string_t>* recv)
335385
{
336386
// No support for global directories in Unix.

src/installer/corehost/cli/hostmisc/pal.windows.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,35 @@ bool pal::get_temp_directory(pal::string_t& tmp_dir)
560560
assert(len < max_len);
561561
tmp_dir.assign(temp_path);
562562

563-
return pal::realpath(&tmp_dir);
563+
return realpath(&tmp_dir);
564564
}
565565

566+
bool pal::get_default_bundle_extraction_base_dir(pal::string_t& extraction_dir)
567+
{
568+
if (!get_temp_directory(extraction_dir))
569+
{
570+
return false;
571+
}
572+
573+
append_path(&extraction_dir, _X(".net"));
574+
// Windows Temp-Path is already user-private.
575+
576+
if (realpath(&extraction_dir))
577+
{
578+
return true;
579+
}
580+
581+
// Create the %TEMP%\.net directory
582+
if (CreateDirectoryW(extraction_dir.c_str(), NULL) == 0 &&
583+
GetLastError() != ERROR_ALREADY_EXISTS)
584+
{
585+
return false;
586+
}
587+
588+
return realpath(&extraction_dir);
589+
}
590+
591+
566592
static bool wchar_convert_helper(DWORD code_page, const char* cstr, int len, pal::string_t* out)
567593
{
568594
out->clear();
@@ -779,4 +805,4 @@ void pal::mutex_t::lock()
779805
void pal::mutex_t::unlock()
780806
{
781807
::LeaveCriticalSection(&_impl);
782-
}
808+
}

0 commit comments

Comments
 (0)