Skip to content

Commit 8ee7ccb

Browse files
committed
Support opening FD for current directory, for at-related syscalls
1 parent c9640c7 commit 8ee7ccb

File tree

3 files changed

+61
-17
lines changed

3 files changed

+61
-17
lines changed

lib/tinykvm/linux/fds.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace tinykvm
2424
}
2525

2626
FileDescriptors::FileDescriptors(Machine& machine)
27-
: m_machine(machine)
27+
: m_machine(machine),
28+
m_current_working_directory_fd(AT_FDCWD)
2829
{
2930
m_allowed_readable_paths =
3031
std::make_shared<std::unordered_set<std::string>>();
@@ -81,20 +82,25 @@ namespace tinykvm
8182
throw std::runtime_error("TinyKVM: Invalid file descriptor in FileDescriptors::add()");
8283
}
8384
if (this->m_total_fds_opened >= this->m_max_total_fds_opened) {
85+
// We have a limit on the total number of file descriptors,
86+
// so since we aren't going to manage this fd, we need to close it.
87+
close(fd);
8488
throw std::runtime_error("TinyKVM: Too many opened fds in total, max_total_fds_opened = " +
8589
std::to_string(this->m_max_total_fds_opened));
8690
}
8791
this->m_total_fds_opened ++;
8892

8993
if (is_socket) {
9094
if (this->m_fds.size() >= this->m_max_sockets) {
95+
close(fd);
9196
throw std::runtime_error("TinyKVM: Too many open sockets, max_sockets = " +
9297
std::to_string(this->m_max_sockets));
9398
}
9499
m_fds[m_next_socket_fd] = {fd, is_writable, false};
95100
return m_next_socket_fd++;
96101
} else {
97102
if (this->m_fds.size() >= this->m_max_files) {
103+
close(fd);
98104
throw std::runtime_error("TinyKVM: Too many open files, max_files = " +
99105
std::to_string(this->m_max_files));
100106
}
@@ -294,4 +300,28 @@ namespace tinykvm
294300
return false;
295301
}
296302

303+
void FileDescriptors::set_current_working_directory(const std::string& path) noexcept
304+
{
305+
m_current_working_directory = path;
306+
// Set the current working directory fd by opening the path
307+
int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY);
308+
m_current_working_directory_fd = fd;
309+
if (fd < 0) {
310+
if (this->m_verbose) {
311+
fprintf(stderr, "TinyKVM: Failed to set current working directory to %s\n", path.c_str());
312+
}
313+
} else if (this->m_verbose) {
314+
fprintf(stderr, "TinyKVM: Set current working directory to %s with fd %d\n",
315+
path.c_str(), fd);
316+
}
317+
}
318+
319+
int FileDescriptors::transform_relative_fd(int fd) const noexcept
320+
{
321+
if (fd == AT_FDCWD) {
322+
return m_current_working_directory_fd;
323+
}
324+
return fd;
325+
}
326+
297327
} // tinykvm

lib/tinykvm/linux/fds.hpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,7 @@ namespace tinykvm
217217
/// @brief Set the current working directory. This is used in a few system
218218
/// calls, such as getcwd() and chdir().
219219
/// @param path The path to set as the current working directory.
220-
void set_current_working_directory(const std::string& path) noexcept {
221-
m_current_working_directory = path;
222-
}
220+
void set_current_working_directory(const std::string& path) noexcept;
223221

224222
/// @brief Get the current working directory. This is used in a few system
225223
/// calls, such as getcwd() and chdir().
@@ -228,6 +226,24 @@ namespace tinykvm
228226
return m_current_working_directory;
229227
}
230228

229+
/// @brief Transform a file descriptor to a file descriptor that is
230+
/// relative to the current working directory. This is used in a few
231+
/// at-related system calls, such as openat() and fstatat(). Only
232+
/// AT_FDCWD is transformed, all other file descriptors are returned as-is.
233+
/// @param fd The file descriptor to transform.
234+
/// @return The file descriptor that is relative to the current working
235+
/// directory.
236+
int transform_relative_fd(int fd) const noexcept;
237+
238+
/// @brief Get the fd for the current working directory. This is used in a few
239+
/// at-related system calls, such as openat() and fstatat(). Only
240+
/// AT_FDCWD is transformed, all other file descriptors are returned as-is.
241+
/// @return The file descriptor that is relative to the current working
242+
/// directory.
243+
int current_working_directory_fd() const noexcept {
244+
return m_current_working_directory_fd;
245+
}
246+
231247
/// @brief Set verbose mode. This will print out information about
232248
/// file descriptor management.
233249
/// @param verbose True to enable verbose mode, false to disable it.
@@ -253,6 +269,7 @@ namespace tinykvm
253269
int m_next_socket_fd = 0x1000 | SOCKET_BIT;
254270
std::array<int, 3> m_stdout_redirects { 0, 1, 2 };
255271
std::string m_current_working_directory;
272+
int m_current_working_directory_fd = -1;
256273
bool m_verbose = false;
257274
open_readable_t m_open_readable;
258275
open_writable_t m_open_writable;

lib/tinykvm/linux/system_calls.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,7 @@ void Machine::setup_linux_system_calls()
14931493
// Check if the path is writable (path can be modified)
14941494
if (cpu.machine().fds().is_writable_path(path))
14951495
{
1496-
int dirfd = AT_FDCWD;
1496+
int dirfd = cpu.machine().fds().current_working_directory_fd();
14971497
if (vdirfd != AT_FDCWD)
14981498
{
14991499
dirfd = cpu.machine().fds().translate_writable_vfd(vdirfd);
@@ -1715,7 +1715,7 @@ void Machine::setup_linux_system_calls()
17151715
if (!write_flags)
17161716
{
17171717
try {
1718-
int pfd = AT_FDCWD;
1718+
int pfd = cpu.machine().fds().current_working_directory_fd();
17191719
if (vfd != AT_FDCWD) {
17201720
pfd = cpu.machine().fds().translate(vfd);
17211721
}
@@ -1744,7 +1744,7 @@ void Machine::setup_linux_system_calls()
17441744
if (write_flags || regs.rax == (__u64)-1)
17451745
{
17461746
try {
1747-
int pfd = AT_FDCWD;
1747+
int pfd = cpu.machine().fds().current_working_directory_fd();
17481748
if (vfd != AT_FDCWD) {
17491749
pfd = cpu.machine().fds().translate(vfd);
17501750
}
@@ -1776,25 +1776,23 @@ void Machine::setup_linux_system_calls()
17761776
const auto vpath = regs.rsi;
17771777
const auto buffer = regs.rdx;
17781778
int flags = AT_SYMLINK_NOFOLLOW; // regs.r10;
1779-
int fd = AT_FDCWD;
1779+
const int vfd = regs.rdi;
1780+
int fd = cpu.machine().fds().current_working_directory_fd();
17801781
std::string path;
17811782

17821783
try {
17831784
path = cpu.machine().memcstring(vpath, PATH_MAX);
17841785

1785-
if (int(regs.rdi) >= 0) {
1786+
if (vfd != AT_FDCWD) {
17861787
// Use existing vfd
17871788
fd = cpu.machine().fds().translate(int(regs.rdi));
1788-
} else {
1789-
// Use AT_FDCWD
1790-
fd = AT_FDCWD;
17911789
}
17921790

17931791
if (!cpu.machine().fds().is_readable_path(path) && !path.empty()) {
17941792
regs.rax = -EPERM;
17951793
} else {
17961794
// If path is empty, use AT_EMPTY_PATH to operate on the fd
1797-
flags = (path.empty() && fd != AT_FDCWD) ? AT_EMPTY_PATH : 0;
1795+
flags = (path.empty() && vfd != AT_FDCWD) ? AT_EMPTY_PATH : 0;
17981796

17991797
struct stat64 vstat;
18001798
// Path is in allow-list
@@ -2103,7 +2101,7 @@ void Machine::setup_linux_system_calls()
21032101
const auto mask = regs.r10;
21042102
const auto buffer = regs.r8;
21052103
std::string path;
2106-
int fd = AT_FDCWD;
2104+
int fd = cpu.machine().fds().current_working_directory_fd();
21072105

21082106
try {
21092107
path = cpu.machine().memcstring(vpath, PATH_MAX);
@@ -2139,14 +2137,13 @@ void Machine::setup_linux_system_calls()
21392137
const int vfd = regs.rdi;
21402138
const auto vpath = regs.rsi;
21412139
const auto g_buffer = regs.rdx;
2142-
//const int flags = regs.r8;
21432140
std::string path;
21442141
try {
21452142
path = cpu.machine().memcstring(vpath, PATH_MAX);
21462143
if (!cpu.machine().fds().is_readable_path(path)) {
21472144
regs.rax = -EPERM;
21482145
} else {
2149-
int fd = vfd;
2146+
int fd = cpu.machine().fds().current_working_directory_fd();
21502147
// Translate from vfd when fd != AT_FDCWD
21512148
if (vfd != AT_FDCWD)
21522149
fd = cpu.machine().fds().translate(vfd);
@@ -2187,7 +2184,7 @@ void Machine::setup_linux_system_calls()
21872184
times[1].tv_nsec = UTIME_NOW;
21882185
}
21892186
// Translate from vfd when fd != AT_FDCWD
2190-
int fd = vfd;
2187+
int fd = cpu.machine().fds().current_working_directory_fd();
21912188
if (vfd != AT_FDCWD)
21922189
fd = cpu.machine().fds().translate_writable_vfd(vfd);
21932190
// Path is in allow-list

0 commit comments

Comments
 (0)