Add basic symlink support.

- sys$readlink + readlink()
- Add a /proc/PID/exe symlink to the process's executable.
- Print symlink contents in ls output.
- Some work on plumbing options into VFS::open().
This commit is contained in:
Andreas Kling 2018-10-28 14:11:51 +01:00
parent 1d4af51250
commit 97726862dd
Notes: sideshowbarker 2024-07-19 18:36:54 +09:00
20 changed files with 140 additions and 46 deletions

View File

@ -96,6 +96,16 @@ ByteBuffer procfs$pid_stack(Task& task)
return buffer;
}
ByteBuffer procfs$pid_exe(Task& task)
{
InodeIdentifier inode;
{
InterruptDisabler disabler;
inode = task.executableInode();
}
return VirtualFileSystem::the().absolutePath(inode).toByteBuffer();
}
void ProcFileSystem::addProcess(Task& task)
{
ASSERT_INTERRUPTS_DISABLED();
@ -105,6 +115,8 @@ void ProcFileSystem::addProcess(Task& task)
m_pid2inode.set(task.pid(), dir.index());
addFile(createGeneratedFile("vm", [&task] { return procfs$pid_vm(task); }), dir.index());
addFile(createGeneratedFile("stack", [&task] { return procfs$pid_stack(task); }), dir.index());
if (task.executableInode().isValid())
addFile(createGeneratedFile("exe", [&task] { return procfs$pid_exe(task); }, 00120777), dir.index());
}
void ProcFileSystem::removeProcess(Task& task)

View File

@ -116,6 +116,8 @@ DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3)
return current->sys$uname((utsname*)arg1);
case Syscall::SetMmapName:
return current->sys$set_mmap_name((void*)arg1, (size_t)arg2, (const char*)arg3);
case Syscall::PosixReadlink:
return current->sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3);
default:
kprintf("int0x80: Unknown function %x requested {%x, %x, %x}\n", function, arg1, arg2, arg3);
break;

View File

@ -35,6 +35,7 @@ enum Function {
PosixChdir = 0x2003,
PosixUname = 0x2004,
SetMmapName = 0x2005,
PosixReadlink = 0x2006,
};
void initialize();

View File

@ -234,7 +234,7 @@ Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t paren
cwd = parentTask->m_cwd.copyRef();
}
auto handle = VirtualFileSystem::the().open(path, cwd ? cwd->inode : InodeIdentifier());
auto handle = VirtualFileSystem::the().open(path, 0, cwd ? cwd->inode : InodeIdentifier());
if (!handle) {
error = -ENOENT; // FIXME: Get a more detailed error from VFS.
return nullptr;
@ -261,7 +261,7 @@ Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t paren
}
InterruptDisabler disabler; // FIXME: Get rid of this, jesus christ. This "critical" section is HUGE.
Task* t = new Task(parts.takeLast(), uid, gid, parentPID, Ring3);
Task* t = new Task(parts.takeLast(), uid, gid, parentPID, Ring3, handle->vnode());
t->m_arguments = move(taskArguments);
@ -362,13 +362,14 @@ Task* Task::createKernelTask(void (*e)(), String&& name)
return task;
}
Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring)
Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring, RetainPtr<VirtualFileSystem::Node>&& executable)
: m_name(move(name))
, m_pid(next_pid++)
, m_uid(uid)
, m_gid(gid)
, m_state(Runnable)
, m_ring(ring)
, m_executable(move(executable))
, m_parentPID(parentPID)
{
m_fileHandles.append(nullptr); // stdin
@ -785,17 +786,39 @@ int Task::sys$close(int fd)
int Task::sys$lstat(const char* path, Unix::stat* statbuf)
{
VALIDATE_USER_BUFFER(statbuf, sizeof(Unix::stat));
auto handle = VirtualFileSystem::the().open(move(path), cwdInode());
auto handle = VirtualFileSystem::the().open(move(path), O_NOFOLLOW_NOERROR, cwdInode());
if (!handle)
return -1;
handle->stat(statbuf);
return 0;
}
int Task::sys$readlink(const char* path, char* buffer, size_t size)
{
VALIDATE_USER_BUFFER(path, strlen(path));
VALIDATE_USER_BUFFER(buffer, size);
auto handle = VirtualFileSystem::the().open(path, O_RDONLY | O_NOFOLLOW_NOERROR, cwdInode());
if (!handle)
return -ENOENT; // FIXME: Get a more detailed error from VFS.
if (!handle->metadata().isSymbolicLink())
return -EINVAL;
auto contents = handle->readEntireFile();
if (!contents)
return -EIO; // FIXME: Get a more detailed error from VFS.
memcpy(buffer, contents.pointer(), min(size, contents.size()));
if (contents.size() + 1 < size)
buffer[contents.size()] = '\0';
return 0;
}
int Task::sys$chdir(const char* path)
{
VALIDATE_USER_BUFFER(path, strlen(path));
auto handle = VirtualFileSystem::the().open(path, cwdInode());
auto handle = VirtualFileSystem::the().open(path, 0, cwdInode());
if (!handle)
return -ENOENT; // FIXME: More detailed error.
if (!handle->isDirectory())
@ -811,17 +834,20 @@ int Task::sys$getcwd(char* buffer, size_t size)
return -ENOTIMPL;
}
int Task::sys$open(const char* path, size_t pathLength)
int Task::sys$open(const char* path, int options)
{
#ifdef DEBUG_IO
kprintf("Task::sys$open(): PID=%u, path=%s {%u}\n", m_pid, path, pathLength);
#endif
VALIDATE_USER_BUFFER(path, pathLength);
VALIDATE_USER_BUFFER(path, strlen(path));
if (m_fileHandles.size() >= m_maxFileHandles)
return -EMFILE;
auto handle = VirtualFileSystem::the().open(String(path, pathLength), cwdInode());
auto handle = VirtualFileSystem::the().open(path, 0, cwdInode());
if (!handle)
return -ENOENT; // FIXME: Detailed error.
if (options & O_DIRECTORY && !handle->isDirectory())
return -ENOTDIR; // FIXME: This should be handled by VFS::open.
int fd = m_fileHandles.size();
handle->setFD(fd);
m_fileHandles.append(move(handle));

View File

@ -87,7 +87,7 @@ public:
uid_t sys$getuid();
gid_t sys$getgid();
pid_t sys$getpid();
int sys$open(const char* path, size_t pathLength);
int sys$open(const char* path, int options);
int sys$close(int fd);
int sys$read(int fd, void* outbuf, size_t nread);
int sys$lstat(const char*, Unix::stat*);
@ -108,6 +108,7 @@ public:
int sys$gethostname(char* name, size_t length);
int sys$get_arguments(int* argc, char*** argv);
int sys$uname(utsname*);
int sys$readlink(const char*, char*, size_t);
static void initialize();
@ -134,12 +135,13 @@ public:
bool isValidAddressForUser(LinearAddress) const;
InodeIdentifier cwdInode() const { return m_cwd ? m_cwd->inode : InodeIdentifier(); }
InodeIdentifier executableInode() const { return m_executable ? m_executable->inode : InodeIdentifier(); }
private:
friend class MemoryManager;
friend bool scheduleNewTask();
Task(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel);
Task(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel, RetainPtr<VirtualFileSystem::Node>&& = nullptr);
void allocateLDT();
@ -171,6 +173,7 @@ private:
size_t m_maxFileHandles { 16 };
RetainPtr<VirtualFileSystem::Node> m_cwd;
RetainPtr<VirtualFileSystem::Node> m_executable;
struct Region : public Retainable<Region> {
Region(LinearAddress, size_t, RetainPtr<Zone>&&, String&&);

View File

@ -35,7 +35,6 @@
#define EDOM 33 // Math argument out of domain of func
#define ERANGE 34 // Math result not representable
#define ENAMETOOLONG 36 // Name too long
#define ELOOP 40 // Too many symbolic links
#define EOVERFLOW 75 // Value too large for defined data type
#define ENOTIMPL 999 // Not implemented

View File

@ -8,8 +8,7 @@ extern "C" {
DIR* opendir(const char* name)
{
// FIXME: Should fail if it's not a directory!
int fd = open(name);
int fd = open(name, O_RDONLY | O_DIRECTORY);
if (fd == -1)
return nullptr;
DIR* dirp = (DIR*)malloc(sizeof(dirp));

View File

@ -20,10 +20,9 @@ pid_t getpid()
return Syscall::invoke(Syscall::PosixGetpid);
}
int open(const char* path)
int open(const char* path, int options)
{
size_t length = strlen(path);
int rc = Syscall::invoke(Syscall::PosixOpen, (dword)path, (dword)length);
int rc = Syscall::invoke(Syscall::PosixOpen, (dword)path, (dword)options);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
@ -74,5 +73,11 @@ int gethostname(char* buffer, size_t size)
__RETURN_WITH_ERRNO(rc, rc, -1);
}
ssize_t readlink(const char* path, char* buffer, size_t size)
{
int rc = Syscall::invoke(Syscall::PosixReadlink, (dword)path, (dword)buffer, (dword)size);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
}

View File

@ -7,7 +7,7 @@ extern "C" {
uid_t getuid();
gid_t getgid();
pid_t getpid();
int open(const char* path);
int open(const char* path, int options);
ssize_t read(int fd, void* buf, size_t count);
int close(int fd);
pid_t waitpid(pid_t, int* wstatus, int options);
@ -16,6 +16,7 @@ char* getcwd(char* buffer, size_t size);
int lstat(const char* path, stat* statbuf);
int sleep(unsigned seconds);
int gethostname(char*, size_t);
ssize_t readlink(const char* path, char* buffer, size_t);
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WTERMSIG(status) ((status) & 0x7f)
@ -52,4 +53,10 @@ int gethostname(char*, size_t);
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_DIRECTORY 00200000
#define O_NOFOLLOW 00400000
}

View File

@ -9,7 +9,7 @@ int main(int argc, char** argv)
printf("usage: cat <file>\n");
return 1;
}
int fd = open(argv[1]);
int fd = open(argv[1], O_RDONLY);
if (fd == -1) {
printf("failed to open %s: %s\n", argv[1], strerror(errno));
return 1;

View File

@ -65,7 +65,9 @@ int main(int c, char** v)
const char* endColor = "";
if (colorize) {
if (S_ISDIR(st.st_mode))
if (S_ISLNK(st.st_mode))
beginColor = "\033[36;1m";
else if (S_ISDIR(st.st_mode))
beginColor = "\033[34;1m";
else if (st.st_mode & 0111)
beginColor = "\033[32;1m";
@ -76,10 +78,19 @@ int main(int c, char** v)
printf("%s%s%s", beginColor, de->d_name, endColor);
if (S_ISDIR(st.st_mode))
if (S_ISLNK(st.st_mode)) {
char linkbuf[256];
ssize_t nread = readlink(pathbuf, linkbuf, sizeof(linkbuf));
if (nread < 0) {
perror("readlink failed");
} else {
printf(" -> %s", linkbuf);
}
} else if (S_ISDIR(st.st_mode)) {
printf("/");
else if (st.st_mode & 0111)
} else if (st.st_mode & 0111) {
printf("*");
}
printf("\n");
}
return 0;

View File

@ -3,7 +3,7 @@
int main(int c, char** v)
{
int fd = open("/proc/mm");
int fd = open("/proc/mm", O_RDONLY);
if (fd == -1) {
perror("failed to open /proc/mm");
return 1;

View File

@ -3,7 +3,7 @@
int main(int c, char** v)
{
int fd = open("/proc/summary");
int fd = open("/proc/summary", O_RDONLY);
if (fd == -1) {
perror("failed to open /proc/summary");
return 1;

View File

@ -147,7 +147,7 @@ int main(int, char**)
int linedx = 0;
linebuf[0] = '\0';
int fd = open("/dev/keyboard");
int fd = open("/dev/keyboard", O_RDONLY);
if (fd == -1) {
printf("failed to open /dev/keyboard :(\n");
return 1;

View File

@ -74,7 +74,8 @@ ByteBuffer FileSystem::readEntireInode(InodeIdentifier inode, FileHandle* handle
return nullptr;
}
auto contents = ByteBuffer::createUninitialized(metadata.size);
size_t initialSize = metadata.size ? metadata.size : 4096;
auto contents = ByteBuffer::createUninitialized(initialSize);
Unix::ssize_t nread;
byte buffer[512];
@ -87,6 +88,7 @@ ByteBuffer FileSystem::readEntireInode(InodeIdentifier inode, FileHandle* handle
memcpy(out, buffer, nread);
out += nread;
offset += nread;
ASSERT(offset <= initialSize); // FIXME: Support dynamically growing the buffer.
}
if (nread < 0) {
kprintf("[fs] readInode: ERROR: %d\n", nread);

View File

@ -60,12 +60,12 @@ auto SyntheticFileSystem::createTextFile(String&& name, String&& text) -> OwnPtr
file->metadata.size = file->data.size();
file->metadata.uid = 100;
file->metadata.gid = 200;
file->metadata.mode = 04;
file->metadata.mode = 0040644;
file->metadata.mtime = mepoch;
return file;
}
auto SyntheticFileSystem::createGeneratedFile(String&& name, Function<ByteBuffer()>&& generator) -> OwnPtr<File>
auto SyntheticFileSystem::createGeneratedFile(String&& name, Function<ByteBuffer()>&& generator, Unix::mode_t mode) -> OwnPtr<File>
{
auto file = make<File>();
file->generator = move(generator);
@ -73,7 +73,7 @@ auto SyntheticFileSystem::createGeneratedFile(String&& name, Function<ByteBuffer
file->metadata.size = 0;
file->metadata.uid = 0;
file->metadata.gid = 0;
file->metadata.mode = 0100644;
file->metadata.mode = mode;
file->metadata.mtime = mepoch;
return file;
}
@ -146,9 +146,8 @@ bool SyntheticFileSystem::enumerateDirectoryInode(InodeIdentifier inode, Functio
callback({ ".", synInode.metadata.inode });
callback({ "..", synInode.parent });
for (auto& child : synInode.children) {
for (auto& child : synInode.children)
callback({ child->name, child->metadata.inode });
}
return true;
}
@ -214,8 +213,8 @@ Unix::ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier inode, Unix::o
generatedData = handle->generatorCache();
}
}
auto* data = generatedData ? &generatedData : &file.data;
auto* data = generatedData ? &generatedData : &file.data;
Unix::ssize_t nread = min(static_cast<Unix::off_t>(data->size() - offset), static_cast<Unix::off_t>(count));
memcpy(buffer, data->pointer() + offset, nread);
if (nread == 0 && handle && handle->generatorCache())

View File

@ -40,7 +40,7 @@ protected:
OwnPtr<File> createDirectory(String&& name);
OwnPtr<File> createTextFile(String&& name, String&& text);
OwnPtr<File> createGeneratedFile(String&& name, Function<ByteBuffer()>&&);
OwnPtr<File> createGeneratedFile(String&& name, Function<ByteBuffer()>&&, Unix::mode_t = 0100644);
InodeIdentifier addFile(OwnPtr<File>&&, InodeIndex parent = RootInodeIndex);
bool removeFile(InodeIndex);

View File

@ -5,6 +5,7 @@
#include <AK/kmalloc.h>
#include <AK/kstdio.h>
#include <AK/ktime.h>
#include "sys-errno.h"
//#define VFS_DEBUG
@ -104,7 +105,8 @@ bool VirtualFileSystem::mount(RetainPtr<FileSystem>&& fileSystem, const String&
{
ASSERT(fileSystem);
auto inode = resolvePath(path);
int error;
auto inode = resolvePath(path, error);
if (!inode.isValid()) {
kprintf("[VFS] mount can't resolve mount point '%s'\n", path.characters());
return false;
@ -172,7 +174,8 @@ void VirtualFileSystem::freeNode(Node* node)
bool VirtualFileSystem::isDirectory(const String& path, InodeIdentifier base)
{
auto inode = resolvePath(path, base);
int error;
auto inode = resolvePath(path, error, base);
if (!inode.isValid())
return false;
@ -226,7 +229,8 @@ void VirtualFileSystem::enumerateDirectoryInode(InodeIdentifier directoryInode,
void VirtualFileSystem::listDirectory(const String& path)
{
auto directoryInode = resolvePath(path);
int error;
auto directoryInode = resolvePath(path, error);
if (!directoryInode.isValid())
return;
@ -326,7 +330,8 @@ void VirtualFileSystem::listDirectory(const String& path)
void VirtualFileSystem::listDirectoryRecursively(const String& path)
{
auto directory = resolvePath(path);
int error;
auto directory = resolvePath(path, error);
if (!directory.isValid())
return;
@ -351,17 +356,19 @@ bool VirtualFileSystem::touch(const String& path)
{
Locker locker(VirtualFileSystem::lock());
auto inode = resolvePath(path);
int error;
auto inode = resolvePath(path, error);
if (!inode.isValid())
return false;
return inode.fileSystem()->setModificationTime(inode, ktime(nullptr));
}
OwnPtr<FileHandle> VirtualFileSystem::open(const String& path, InodeIdentifier base)
OwnPtr<FileHandle> VirtualFileSystem::open(const String& path, int options, InodeIdentifier base)
{
Locker locker(VirtualFileSystem::lock());
auto inode = resolvePath(path, base);
int error;
auto inode = resolvePath(path, error, base, options);
if (!inode.isValid())
return nullptr;
auto vnode = getOrCreateNode(inode);
@ -397,7 +404,8 @@ InodeIdentifier VirtualFileSystem::resolveSymbolicLink(const String& basePath, I
return { };
char buf[4096];
ksprintf(buf, "/%s/%s", basePath.characters(), String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
return resolvePath(buf);
int error;
return resolvePath(buf, error);
}
String VirtualFileSystem::absolutePath(InodeIdentifier inode)
@ -407,6 +415,7 @@ String VirtualFileSystem::absolutePath(InodeIdentifier inode)
if (!inode.isValid())
return String();
int error;
Vector<InodeIdentifier> lineage;
while (inode != m_rootNode->inode) {
if (auto* mount = findMountForGuest(inode))
@ -414,7 +423,7 @@ String VirtualFileSystem::absolutePath(InodeIdentifier inode)
else
lineage.append(inode);
if (inode.metadata().isDirectory()) {
inode = resolvePath("..", inode);
inode = resolvePath("..", error, inode);
} else
inode = inode.fileSystem()->findParentOfInode(inode);
ASSERT(inode.isValid());
@ -434,7 +443,7 @@ String VirtualFileSystem::absolutePath(InodeIdentifier inode)
return builder.build();
}
InodeIdentifier VirtualFileSystem::resolvePath(const String& path, InodeIdentifier base)
InodeIdentifier VirtualFileSystem::resolvePath(const String& path, int& error, InodeIdentifier base, int options)
{
if (path.isEmpty())
return { };
@ -455,12 +464,14 @@ InodeIdentifier VirtualFileSystem::resolvePath(const String& path, InodeIdentifi
#ifdef VFS_DEBUG
kprintf("invalid metadata\n");
#endif
error = -EIO;
return { };
}
if (!metadata.isDirectory()) {
#ifdef VFS_DEBUG
kprintf("not directory\n");
#endif
error = -EIO;
return { };
}
inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, part);
@ -468,6 +479,7 @@ InodeIdentifier VirtualFileSystem::resolvePath(const String& path, InodeIdentifi
#ifdef VFS_DEBUG
kprintf("bad child\n");
#endif
error = -EIO;
return { };
}
#ifdef VFS_DEBUG
@ -489,6 +501,14 @@ InodeIdentifier VirtualFileSystem::resolvePath(const String& path, InodeIdentifi
}
metadata = inode.metadata();
if (metadata.isSymbolicLink()) {
if (i == parts.size() - 1) {
if (options & O_NOFOLLOW) {
error = -ELOOP;
return { };
}
if (options & O_NOFOLLOW_NOERROR)
return inode;
}
char buf[4096] = "";
char* p = buf;
for (unsigned j = 0; j < i; ++j) {
@ -497,6 +517,7 @@ InodeIdentifier VirtualFileSystem::resolvePath(const String& path, InodeIdentifi
inode = resolveSymbolicLink(buf, inode);
if (!inode.isValid()) {
kprintf("Symbolic link resolution failed :(\n");
error = -ENOENT;
return { };
}
}

View File

@ -12,6 +12,13 @@
#include "Limits.h"
#include "FileSystem.h"
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_DIRECTORY 00200000
#define O_NOFOLLOW 00400000
#define O_NOFOLLOW_NOERROR 0x4000000
class CharacterDevice;
class FileHandle;
@ -79,7 +86,7 @@ public:
bool mountRoot(RetainPtr<FileSystem>&&);
bool mount(RetainPtr<FileSystem>&&, const String& path);
OwnPtr<FileHandle> open(const String& path, InodeIdentifier base = InodeIdentifier());
OwnPtr<FileHandle> open(const String& path, int options = 0, InodeIdentifier base = InodeIdentifier());
OwnPtr<FileHandle> create(const String& path, InodeIdentifier base = InodeIdentifier());
OwnPtr<FileHandle> mkdir(const String& path, InodeIdentifier base = InodeIdentifier());
@ -98,7 +105,7 @@ private:
friend class FileHandle;
void enumerateDirectoryInode(InodeIdentifier, Function<bool(const FileSystem::DirectoryEntry&)>);
InodeIdentifier resolvePath(const String& path, InodeIdentifier base = InodeIdentifier());
InodeIdentifier resolvePath(const String& path, int& error, InodeIdentifier base = InodeIdentifier(), int options = 0);
InodeIdentifier resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode);
RetainPtr<Node> allocateNode();

View File

@ -34,5 +34,5 @@
#define EPIPE 32 // Broken pipe
#define EDOM 33 // Math argument out of domain of func
#define ERANGE 34 // Math result not representable
#define ELOOP 40 // Too many symbolic links
#define EOVERFLOW 75 // Value too large for defined data type