Compare commits

..

29 Commits

Author SHA1 Message Date
1d973664ed
kernel/ATA: Pass extra information to DeviceRegistry
All checks were successful
continuous-integration/drone/pr Build is passing
This is needed since merging e7d482e from main.
2023-05-20 21:50:45 +02:00
3423599ec7
kernel+init: Create a device node in /dev to access the CDROM from userspace!
Still using PIO, though.
2023-05-20 21:50:45 +02:00
444ac01d01
kernel/ATA: Read the CDROM's first sector using ATAPI PIO!!
Sadly, for some reason, DMA is not working right now.
This is a problem, as PIO is inconvenient. But hey, it works for now!
2023-05-20 21:50:45 +02:00
7a4b36d795
kernel/x86_64: Implement writing to PCI fields 2023-05-20 21:50:45 +02:00
913ce18121
kernel/PCI: Add bit enum for the Command field 2023-05-20 21:50:45 +02:00
6ba8af6a54
kernel: Actually register interrupt handlers properly 2023-05-20 21:50:44 +02:00
599193c1bf
kernel/ATA: Calculate block sizes for ATA devices as well 2023-05-20 21:50:44 +02:00
8449a7e545
kernel/ATA: Send a READ CAPACITY packet to an ATA drive on initialization 2023-05-20 21:50:44 +02:00
8fb9909de9
kernel/ATA: Read the PCI Busmaster registers and start preparing for DMA 2023-05-20 21:50:44 +02:00
2c4d20b30b
kernel/ATA: Read the Busmaster base port and verify it 2023-05-20 21:50:44 +02:00
61f1f16be7
kernel: Handle device BARs properly 2023-05-20 21:50:44 +02:00
d872f78b67
kernel/ATA: Read ATA strings properly instead of backwards
Now we can see the model string. What does it say...

"QEMU DVD-ROM". Let's go!
2023-05-20 21:50:43 +02:00
e8ceab1896
kernel/ATA: Implement enough to send an IDENTIFY command and read the model number :) 2023-05-20 21:50:43 +02:00
f7b7c5574e
kernel/ATA: Handle drive IRQs in compatibility mode 2023-05-20 21:50:43 +02:00
e57e79cb3e
kernel/CPU: Allow passing arbitrary data to interrupt handlers 2023-05-20 21:50:42 +02:00
8f90d5a8b7
kernel/ATA: Start reading/writing registers and detecting drives 2023-05-20 21:50:42 +02:00
2f828f83ee
kernel: Warn if no ATA controller is found 2023-05-20 21:50:42 +02:00
c4428f15bc
kernel: Add a KMutex class and use that for ATA::Controller locking 2023-05-20 21:50:42 +02:00
94569f2171
kernel/x86_64: Add basic ATA controller and channel identification 2023-05-20 21:50:41 +02:00
3586350754
kernel/PCI: Add more PCI field types 2023-05-20 21:50:41 +02:00
f4f0ecdb42
kernel/x86_64: Add a way to register IRQ handlers from other kernel subsystems 2023-05-20 21:50:41 +02:00
597aada09e
ls+stat: Handle symbolic links properly
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 21:49:25 +02:00
4ec1af5e51
apps: Add ln 2023-05-20 21:49:25 +02:00
7a8ef52408
libos: Support not following symlinks 2023-05-20 21:49:25 +02:00
cb205c851c
libc: Add symlink(), symlinkat(), and lstat() 2023-05-20 21:49:25 +02:00
67a9d130e2
kernel: Add initial support for symbolic links :D 2023-05-20 21:49:24 +02:00
b75bd4cd14
libluna: Add PathParser::has_next() 2023-05-20 16:39:18 +02:00
9bb5371e8c
libluna+libc: Add strtok_r() 2023-05-20 16:37:07 +02:00
47d505dcbb
libc: Add getline() and getdelim()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 15:36:30 +02:00
33 changed files with 390 additions and 39 deletions

View File

@ -28,3 +28,4 @@ luna_app(mount.cpp mount)
luna_app(umount.cpp umount) luna_app(umount.cpp umount)
luna_app(ps.cpp ps) luna_app(ps.cpp ps)
luna_app(time.cpp time) luna_app(time.cpp time)
luna_app(ln.cpp ln)

24
apps/ln.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv)
{
StringView target;
StringView link;
os::ArgumentParser parser;
parser.add_description("Create symbolic links.");
parser.add_system_program_info("ln"_sv);
parser.add_positional_argument(target, "target", true);
parser.add_positional_argument(link, "linkpath", true);
parser.parse(argc, argv);
if (symlink(target.chars(), link.chars()) < 0)
{
perror("ln");
return 1;
}
return 0;
}

View File

@ -64,6 +64,7 @@ Result<int> luna_main(int argc, char** argv)
bool long_list { false }; bool long_list { false };
bool human_readable { false }; bool human_readable { false };
bool si { false }; bool si { false };
bool follow_symlink_args { false };
os::ArgumentParser parser; os::ArgumentParser parser;
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv); parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
@ -75,13 +76,15 @@ Result<int> luna_main(int argc, char** argv)
parser.add_switch_argument(human_readable, 'h', "human-readable"_sv, parser.add_switch_argument(human_readable, 'h', "human-readable"_sv,
"with -l, show human-readable sizes e.g. 2KiB, 6GiB"_sv); "with -l, show human-readable sizes e.g. 2KiB, 6GiB"_sv);
parser.add_switch_argument(si, ' ', "si"_sv, "same as -h, but show sizes in powers of 10"_sv); parser.add_switch_argument(si, ' ', "si"_sv, "same as -h, but show sizes in powers of 10"_sv);
parser.add_switch_argument(follow_symlink_args, 'H', "dereference-args"_sv,
"follow symbolic links listed as arguments"_sv);
parser.parse(argc, argv); parser.parse(argc, argv);
Vector<String> files; Vector<String> files;
int dirfd = AT_FDCWD; int dirfd = AT_FDCWD;
SharedPtr<os::Directory> dir; SharedPtr<os::Directory> dir;
if (os::FileSystem::is_directory(pathname)) if (os::FileSystem::is_directory(pathname, follow_symlink_args))
{ {
dir = TRY(os::Directory::open(pathname)); dir = TRY(os::Directory::open(pathname));
dirfd = dir->fd(); dirfd = dir->fd();
@ -93,7 +96,7 @@ Result<int> luna_main(int argc, char** argv)
files = TRY(dir->list(filter)); files = TRY(dir->list(filter));
} }
else if (os::FileSystem::exists(pathname)) else if (os::FileSystem::exists(pathname, follow_symlink_args))
{ {
auto str = TRY(String::from_string_view(pathname)); auto str = TRY(String::from_string_view(pathname));
TRY(files.try_append(move(str))); TRY(files.try_append(move(str)));
@ -111,7 +114,7 @@ Result<int> luna_main(int argc, char** argv)
for (const auto& file : files) for (const auto& file : files)
{ {
struct stat st; struct stat st;
TRY(os::FileSystem::stat({ dirfd, file.view() }, st)); TRY(os::FileSystem::stat({ dirfd, file.view() }, st, false));
StringView owner; StringView owner;
StringView group; StringView group;

View File

@ -10,6 +10,7 @@ static const char* file_type(mode_t mode)
case S_IFREG: return "regular file"; case S_IFREG: return "regular file";
case S_IFDIR: return "directory"; case S_IFDIR: return "directory";
case S_IFCHR: return "character special device"; case S_IFCHR: return "character special device";
case S_IFLNK: return "symbolic link";
default: return "unknown file type"; default: return "unknown file type";
} }
} }
@ -17,15 +18,17 @@ static const char* file_type(mode_t mode)
Result<int> luna_main(int argc, char** argv) Result<int> luna_main(int argc, char** argv)
{ {
StringView path; StringView path;
bool follow_symlinks { false };
os::ArgumentParser parser; os::ArgumentParser parser;
parser.add_description("Display file status."); parser.add_description("Display file status.");
parser.add_system_program_info("stat"_sv); parser.add_system_program_info("stat"_sv);
parser.add_positional_argument(path, "path", true); parser.add_positional_argument(path, "path", true);
parser.add_switch_argument(follow_symlinks, 'L', "dereference"_sv, "follow symlinks");
parser.parse(argc, argv); parser.parse(argc, argv);
struct stat st; struct stat st;
TRY(os::FileSystem::stat(path, st)); TRY(os::FileSystem::stat(path, st, follow_symlinks));
printf(" File: %s\n", path.chars()); printf(" File: %s\n", path.chars());
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode)); printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));

View File

@ -33,7 +33,7 @@ char* getpass()
return nullptr; return nullptr;
} }
static char buf[1024]; static char buf[BUFSIZ];
char* rc = fgets(buf, sizeof(buf), stdin); char* rc = fgets(buf, sizeof(buf), stdin);
restore_terminal(); restore_terminal();

View File

@ -15,28 +15,60 @@ namespace VFS
return *root_fs->root_inode(); return *root_fs->root_inode();
} }
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<Inode> working_directory) static constexpr int MAX_SYMLINKS = 8;
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, SharedPtr<Inode> current_inode,
bool follow_last_symlink, int& symlinks_followed)
{ {
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
if (*path == '\0') return err(ENOENT); if (*path == '\0') return err(ENOENT);
auto parser = TRY(PathParser::create(path)); auto parser = TRY(PathParser::create(path));
SharedPtr<Inode> current_inode; SharedPtr<Inode> parent_inode = current_inode;
if (parser.is_absolute() || !working_directory) current_inode = root_fs->root_inode();
else
current_inode = working_directory;
const char* section; const char* section;
while (parser.next().try_set_value(section)) while (parser.next().try_set_value(section))
{ {
if (!can_execute(current_inode, auth)) return err(EACCES); if (!can_execute(current_inode, auth)) return err(EACCES);
current_inode = TRY(current_inode->find(section)); current_inode = TRY(current_inode->find(section));
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
{
auto link = TRY(current_inode->readlink());
SharedPtr<VFS::Inode> symlink_root;
if (PathParser::is_absolute(link.chars())) symlink_root = root_fs->root_inode();
else
symlink_root = parent_inode;
symlinks_followed++;
current_inode = TRY(resolve_path_impl(link.chars(), auth, symlink_root, true, symlinks_followed));
symlinks_followed--;
}
parent_inode = current_inode;
} }
return current_inode; return current_inode;
} }
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
bool follow_last_symlink)
{
SharedPtr<Inode> current_inode;
if (PathParser::is_absolute(path) || !working_directory) current_inode = root_fs->root_inode();
else
current_inode = working_directory;
int symlinks_followed = 0;
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
}
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory) Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
{ {
auto parser = TRY(PathParser::create(path)); auto parser = TRY(PathParser::create(path));

View File

@ -12,7 +12,8 @@ namespace VFS
{ {
RegularFile, RegularFile,
Directory, Directory,
Device Device,
Symlink
}; };
class Inode; class Inode;
@ -28,6 +29,8 @@ namespace VFS
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0; virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
virtual Result<SharedPtr<Inode>> create_symlink_inode(StringView link) = 0;
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0; virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
virtual u64 handles() const virtual u64 handles() const
@ -92,6 +95,12 @@ namespace VFS
virtual bool blocking() const = 0; virtual bool blocking() const = 0;
// Symlink-specific methods
virtual Result<StringView> readlink()
{
return StringView {};
}
// Metadata accessors // Metadata accessors
virtual usize size() const = 0; virtual usize size() const = 0;
@ -250,7 +259,8 @@ namespace VFS
}; };
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {}); SharedPtr<VFS::Inode> working_directory = {},
bool follow_last_symlink = true);
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {}); SharedPtr<VFS::Inode> working_directory = {});

View File

@ -24,6 +24,15 @@ namespace TmpFS
return (SharedPtr<VFS::Inode>)inode; return (SharedPtr<VFS::Inode>)inode;
} }
Result<SharedPtr<VFS::Inode>> FileSystem::create_symlink_inode(StringView link)
{
SharedPtr<SymlinkInode> inode = TRY(make_shared<SymlinkInode>());
inode->set_fs(*this, {});
TRY(inode->set_link(link, {}));
inode->set_inode_number(m_next_inode_number++, {});
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent) Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
{ {
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>()); SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());

View File

@ -5,6 +5,7 @@
#include <luna/Badge.h> #include <luna/Badge.h>
#include <luna/Buffer.h> #include <luna/Buffer.h>
#include <luna/StaticString.h> #include <luna/StaticString.h>
#include <luna/String.h>
#include <luna/Vector.h> #include <luna/Vector.h>
namespace TmpFS namespace TmpFS
@ -20,6 +21,7 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_file_inode() override; Result<SharedPtr<VFS::Inode>> create_file_inode() override;
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override; Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override; Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView link) override;
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override; Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
@ -120,6 +122,115 @@ namespace TmpFS
u32 m_nlinks { 0 }; u32 m_nlinks { 0 };
}; };
class SymlinkInode : public VFS::FileInode
{
public:
SymlinkInode() = default;
VFS::InodeType type() const override
{
return VFS::InodeType::Symlink;
}
void set_fs(FileSystem& fs, Badge<FileSystem>)
{
m_fs = &fs;
}
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_inode_number = inum;
}
Result<void> set_link(StringView link, Badge<FileSystem>)
{
m_link = TRY(String::from_string_view(link));
return {};
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8*, usize, usize) const override
{
return err(ENOTSUP);
}
Result<usize> write(const u8*, usize, usize) override
{
return err(ENOTSUP);
}
Result<void> truncate(usize) override
{
return err(ENOTSUP);
}
usize size() const override
{
return m_link.length();
}
mode_t mode() const override
{
return 0777;
}
u32 uid() const override
{
return m_uid;
}
u32 gid() const override
{
return m_gid;
}
Result<void> chmod(mode_t) override
{
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
void did_link() override
{
m_nlinks++;
}
void did_unlink() override
{
m_nlinks--;
}
Result<StringView> readlink() override
{
return m_link.view();
}
virtual ~SymlinkInode() = default;
private:
VFS::FileSystem* m_fs;
String m_link;
usize m_inode_number;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
};
class DeviceInode : public VFS::DeviceInode class DeviceInode : public VFS::DeviceInode
{ {
public: public:

View File

@ -104,7 +104,7 @@ Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
auto* current = Scheduler::current(); auto* current = Scheduler::current();
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
if (current->auth.euid != 0 && current->auth.euid != inode->uid()) return err(EPERM); if (current->auth.euid != 0 && current->auth.euid != inode->uid()) return err(EPERM);
@ -123,7 +123,7 @@ Result<u64> sys_fchownat(Registers*, SyscallArgs args)
auto* current = Scheduler::current(); auto* current = Scheduler::current();
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
if (current->auth.euid != 0) return err(EPERM); if (current->auth.euid != 0) return err(EPERM);

View File

@ -21,7 +21,7 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd); kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false)); auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
if (!VFS::can_write(inode, current->auth)) return err(EACCES); if (!VFS::can_write(inode, current->auth)) return err(EACCES);
if (flags > 0) if (flags > 0)
@ -34,3 +34,31 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
return 0; return 0;
} }
Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
{
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
int dirfd = (int)args[1];
auto linkpath = TRY(MemoryManager::strdup_from_user(args[2]));
if (target.is_empty()) return err(ENOENT);
auto* current = Scheduler::current();
auto parser = TRY(PathParser::create(linkpath.chars()));
auto parent = TRY(parser.dirname());
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
auto child_name = TRY(parser.basename());
TRY(VFS::validate_filename(child_name.view()));
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
TRY(inode->chown(current->auth.euid, current->auth.egid));
TRY(parent_inode->add_entry(inode, child_name.chars()));
return 0;
}

View File

@ -31,7 +31,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
int error; int error;
SharedPtr<VFS::Inode> parent_inode; SharedPtr<VFS::Inode> parent_inode;
bool ok = current->resolve_atfile(dirfd, path, false, &parent_inode).try_set_value_or_error(inode, error); bool ok = current->resolve_atfile(dirfd, path, false, !(flags & O_NOFOLLOW), &parent_inode)
.try_set_value_or_error(inode, error);
if (!ok) if (!ok)
{ {
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty()) if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
@ -51,6 +52,9 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES); if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
} }
// This should only be possible if O_NOFOLLOW was in flags.
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
if (flags & O_TMPFILE) if (flags & O_TMPFILE)
{ {
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL); if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);

View File

@ -14,6 +14,7 @@ static mode_t make_mode(mode_t mode, VFS::InodeType type)
case VFS::InodeType::RegularFile: result |= S_IFREG; break; case VFS::InodeType::RegularFile: result |= S_IFREG; break;
case VFS::InodeType::Directory: result |= S_IFDIR; break; case VFS::InodeType::Directory: result |= S_IFDIR; break;
case VFS::InodeType::Device: result |= S_IFCHR; break; case VFS::InodeType::Device: result |= S_IFCHR; break;
case VFS::InodeType::Symlink: result |= S_IFLNK; break;
default: break; default: break;
} }
@ -29,7 +30,7 @@ Result<u64> sys_fstatat(Registers*, SyscallArgs args)
Thread* current = Scheduler::current(); Thread* current = Scheduler::current();
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
stat kstat; stat kstat;

View File

@ -49,13 +49,15 @@ Result<FileDescriptor*> Thread::resolve_fd(int fd)
} }
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path, Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
SharedPtr<VFS::Inode>* parent_inode) bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
{ {
if (parent_inode) *parent_inode = this->current_directory; if (parent_inode) *parent_inode = this->current_directory;
if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this->auth); if (PathParser::is_absolute(path.view()))
return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink);
if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this->auth, this->current_directory); if (dirfd == AT_FDCWD)
return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink);
auto descriptor = TRY(resolve_fd(dirfd)); auto descriptor = TRY(resolve_fd(dirfd));
@ -63,7 +65,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
if (path.is_empty() && allow_empty_path) return descriptor->inode; if (path.is_empty() && allow_empty_path) return descriptor->inode;
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode); return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
} }
bool FileDescriptor::should_append() bool FileDescriptor::should_append()

View File

@ -76,6 +76,7 @@ struct Thread : public LinkedListNode<Thread>
Result<int> allocate_fd(int min); Result<int> allocate_fd(int min);
Result<FileDescriptor*> resolve_fd(int fd); Result<FileDescriptor*> resolve_fd(int fd);
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path, Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
bool follow_last_symlink,
SharedPtr<VFS::Inode>* parent_inode = nullptr); SharedPtr<VFS::Inode>* parent_inode = nullptr);
FPData fp_data; FPData fp_data;

View File

@ -6,6 +6,7 @@
#define AT_FDCWD -100 #define AT_FDCWD -100
#define AT_EMPTY_PATH 1 #define AT_EMPTY_PATH 1
#define AT_SYMLINK_NOFOLLOW 2
#define AT_REMOVEDIR 1 #define AT_REMOVEDIR 1

View File

@ -5,6 +5,7 @@
#define S_IFMT 070000 #define S_IFMT 070000
#define S_IFREG 000000 #define S_IFREG 000000
#define S_IFLNK 010000
#define S_IFDIR 040000 #define S_IFDIR 040000
#define S_IFCHR 050000 #define S_IFCHR 050000
@ -13,6 +14,7 @@
#define S_ISREG(mode) __CHECK_TYPE(mode, S_IFREG) #define S_ISREG(mode) __CHECK_TYPE(mode, S_IFREG)
#define S_ISDIR(mode) __CHECK_TYPE(mode, S_IFDIR) #define S_ISDIR(mode) __CHECK_TYPE(mode, S_IFDIR)
#define S_ISCHR(mode) __CHECK_TYPE(mode, S_IFCHR) #define S_ISCHR(mode) __CHECK_TYPE(mode, S_IFCHR)
#define S_ISLNK(mode) __CHECK_TYPE(mode, S_IFLNK)
#define S_IRWXU 0700 #define S_IRWXU 0700
#define S_IRUSR 0400 #define S_IRUSR 0400

View File

@ -14,6 +14,7 @@
#define O_CLOEXEC 128 #define O_CLOEXEC 128
#define O_DIRECTORY 256 #define O_DIRECTORY 256
#define O_TMPFILE 512 #define O_TMPFILE 512
#define O_NOFOLLOW 1024
#define O_ACCMODE O_RDWR #define O_ACCMODE O_RDWR

View File

@ -26,6 +26,8 @@ extern FILE* stderr;
#define stdout stdout #define stdout stdout
#define stderr stderr #define stderr stderr
#define BUFSIZ 1024
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
@ -96,6 +98,12 @@ extern "C"
/* Read a line from stream. */ /* Read a line from stream. */
char* fgets(char* buf, size_t size, FILE* stream); char* fgets(char* buf, size_t size, FILE* stream);
/* Read a line from stream and store it in a dynamically-allocated buffer. */
ssize_t getline(char** linep, size_t* n, FILE* stream);
/* Read a line from stream and store it in a dynamically-allocated buffer. */
ssize_t getdelim(char** linep, size_t* n, int delim, FILE* stream);
/* Clear the error and end-of-file indicators in stream. */ /* Clear the error and end-of-file indicators in stream. */
void clearerr(FILE* stream); void clearerr(FILE* stream);

View File

@ -71,6 +71,9 @@ extern "C"
/* Separate a string into several tokens. */ /* Separate a string into several tokens. */
char* strtok(char* str, const char* delim); char* strtok(char* str, const char* delim);
/* Separate a string into several tokens. */
char* strtok_r(char* str, const char* delim, char** savep);
/* Return a heap-allocated copy of a fixed-size string. */ /* Return a heap-allocated copy of a fixed-size string. */
char* strndup(const char* str, size_t max); char* strndup(const char* str, size_t max);

View File

@ -29,6 +29,9 @@ extern "C"
#pragma GCC pop_options #pragma GCC pop_options
/* Retrieve information about a file or symbolic link. */
int lstat(const char* path, struct stat* st);
/* Retrieve information about a file. */ /* Retrieve information about a file. */
int fstat(int fd, struct stat* st); int fstat(int fd, struct stat* st);

View File

@ -133,6 +133,12 @@ extern "C"
/* Create a pipe for inter-process communication. */ /* Create a pipe for inter-process communication. */
int pipe(int pfds[2]); int pipe(int pfds[2]);
/* Create a symbolic link. */
int symlink(const char* target, const char* linkpath);
/* Create a symbolic link relative to a file descriptor. */
int symlinkat(const char* target, int dirfd, const char* linkpath);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -236,6 +236,66 @@ extern "C"
return buf; return buf;
} }
ssize_t getline(char** linep, size_t* n, FILE* stream)
{
return getdelim(linep, n, '\n', stream);
}
ssize_t getdelim(char** linep, size_t* n, int delim, FILE* stream)
{
if (!n || !linep)
{
errno = EINVAL;
return -1;
}
char* buf = *linep;
size_t size = *n;
size_t len = 0;
if (!buf)
{
buf = (char*)malloc(BUFSIZ);
size = BUFSIZ;
if (!buf) return -1;
*linep = buf;
*n = size;
}
while (1)
{
int c = fgetc(stream);
if (c == EOF) break;
if (len == size)
{
buf = (char*)realloc(buf, size + 64);
size += 64;
if (!buf) return -1;
*linep = buf;
*n = size;
}
buf[len++] = (char)c;
if (c == delim) break;
}
if (len == 0) return -1;
if (len == size)
{
buf = (char*)realloc(buf, size + 16);
size += 16;
if (!buf) return -1;
*linep = buf;
*n = size;
}
buf[len] = '\0';
return (ssize_t)len;
}
void clearerr(FILE* stream) void clearerr(FILE* stream)
{ {
stream->_eof = stream->_err = 0; stream->_eof = stream->_err = 0;

View File

@ -36,7 +36,7 @@ static bool try_format(strftime_state& state, const char* format, ...)
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
char buf[1024]; char buf[BUFSIZ];
vsnprintf(buf, sizeof(buf), format, ap); vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap); va_end(ap);

View File

@ -30,6 +30,12 @@ extern "C"
__errno_return(rc, int); __errno_return(rc, int);
} }
int lstat(const char* path, struct stat* st)
{
long rc = syscall(SYS_fstatat, AT_FDCWD, path, st, AT_SYMLINK_NOFOLLOW);
__errno_return(rc, int);
}
int fstat(int fd, struct stat* st) int fstat(int fd, struct stat* st)
{ {
long rc = syscall(SYS_fstatat, fd, "", st, AT_EMPTY_PATH); long rc = syscall(SYS_fstatat, fd, "", st, AT_EMPTY_PATH);

View File

@ -4,6 +4,7 @@
#include <luna/Vector.h> #include <luna/Vector.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
@ -347,17 +348,17 @@ extern "C"
} }
else else
{ {
buf = (char*)malloc(1024); buf = (char*)malloc(BUFSIZ);
if (!buf) return nullptr; if (!buf) return nullptr;
ssize_t rc = __getcwd_wrapper(buf, 1024); ssize_t rc = __getcwd_wrapper(buf, BUFSIZ);
if (rc < 0) if (rc < 0)
{ {
free(buf); free(buf);
return nullptr; return nullptr;
} }
if (rc > 1024) if (rc > BUFSIZ)
{ {
free(buf); free(buf);
@ -405,4 +406,16 @@ extern "C"
long rc = syscall(SYS_pipe, (int*)pfds); long rc = syscall(SYS_pipe, (int*)pfds);
__errno_return(rc, int); __errno_return(rc, int);
} }
int symlink(const char* target, const char* linkpath)
{
long rc = syscall(SYS_symlinkat, target, AT_FDCWD, linkpath);
__errno_return(rc, int);
}
int symlinkat(const char* target, int dirfd, const char* linkpath)
{
long rc = syscall(SYS_symlinkat, target, dirfd, linkpath);
__errno_return(rc, int);
}
} }

View File

@ -18,6 +18,7 @@ extern "C"
usize strcspn(const char* str, const char* reject); usize strcspn(const char* str, const char* reject);
char* strtok(char* str, const char* delim); char* strtok(char* str, const char* delim);
char* strtok_r(char* str, const char* delim, char** savep);
usize wcslen(const wchar_t* str); usize wcslen(const wchar_t* str);
int wcscmp(const wchar_t* a, const wchar_t* b); int wcscmp(const wchar_t* a, const wchar_t* b);

View File

@ -3,6 +3,11 @@
#include <luna/Heap.h> #include <luna/Heap.h>
#include <luna/String.h> #include <luna/String.h>
static inline bool is_not_delim(char c)
{
return c && c != '/';
}
class PathParser class PathParser
{ {
public: public:
@ -27,6 +32,11 @@ class PathParser
return is_absolute(StringView { m_original }); return is_absolute(StringView { m_original });
} }
bool has_next() const
{
return m_already_called_next ? (bool)m_strtok_saved_state : is_not_delim(*m_copy);
}
Result<String> basename(); Result<String> basename();
Result<String> dirname(); Result<String> dirname();
@ -38,4 +48,5 @@ class PathParser
const char* m_original { nullptr }; const char* m_original { nullptr };
char* m_copy { nullptr }; char* m_copy { nullptr };
bool m_already_called_next { false }; bool m_already_called_next { false };
char* m_strtok_saved_state { nullptr };
}; };

View File

@ -5,7 +5,7 @@
_e(lseek) _e(mkdir) _e(execve) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) _e(geteuid) \ _e(lseek) _e(mkdir) _e(execve) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) _e(geteuid) \
_e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \ _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
_e(umount) _e(pstat) _e(getrusage) _e(umount) _e(pstat) _e(getrusage) _e(symlinkat)
enum Syscalls enum Syscalls
{ {

View File

@ -219,9 +219,9 @@ extern "C"
return (usize)(s - str); return (usize)(s - str);
} }
char* strtok(char* str, const char* delim) char* strtok_r(char* str, const char* delim, char** savep)
{ {
static char* s = nullptr; auto& s = *savep;
if (str) s = str; if (str) s = str;
if (!s) return nullptr; if (!s) return nullptr;
@ -248,6 +248,12 @@ extern "C"
return nullptr; return nullptr;
} }
char* strtok(char* str, const char* delim)
{
static char* s;
return strtok_r(str, delim, &s);
}
char* strpbrk(const char* s, const char* accept) char* strpbrk(const char* s, const char* accept)
{ {
usize index = strcspn(s, accept); usize index = strcspn(s, accept);

View File

@ -27,7 +27,7 @@ PathParser::~PathParser()
Option<const char*> PathParser::next() Option<const char*> PathParser::next()
{ {
char* result = strtok(m_already_called_next ? nullptr : m_copy, "/"); char* result = strtok_r(m_already_called_next ? nullptr : m_copy, "/", &m_strtok_saved_state);
m_already_called_next = true; m_already_called_next = true;
if (!result) return {}; if (!result) return {};

View File

@ -9,11 +9,11 @@ namespace os
{ {
namespace FileSystem namespace FileSystem
{ {
bool exists(const Path& path); bool exists(const Path& path, bool follow_symlinks = true);
bool is_directory(const Path& path); bool is_directory(const Path& path, bool follow_symlinks = false);
Result<void> stat(const Path& path, struct stat& st); Result<void> stat(const Path& path, struct stat& st, bool follow_symlinks = true);
Result<void> create_directory(StringView path, mode_t mode); Result<void> create_directory(StringView path, mode_t mode);

View File

@ -12,28 +12,29 @@
namespace os::FileSystem namespace os::FileSystem
{ {
bool exists(const Path& path) bool exists(const Path& path, bool follow_symlinks)
{ {
struct stat st; struct stat st;
if (stat(path, st).has_error()) return false; if (stat(path, st, follow_symlinks).has_error()) return false;
return true; return true;
} }
bool is_directory(const Path& path) bool is_directory(const Path& path, bool follow_symlinks)
{ {
struct stat st; struct stat st;
if (stat(path, st).has_error()) return false; if (stat(path, st, follow_symlinks).has_error()) return false;
return S_ISDIR(st.st_mode); return S_ISDIR(st.st_mode);
} }
Result<void> stat(const Path& path, struct stat& st) Result<void> stat(const Path& path, struct stat& st, bool follow_symlinks)
{ {
long rc = long rc =
syscall(SYS_fstatat, path.dirfd(), path.name().chars(), &st, path.is_empty_path() ? AT_EMPTY_PATH : 0); syscall(SYS_fstatat, path.dirfd(), path.name().chars(), &st,
(int)((path.is_empty_path() ? AT_EMPTY_PATH : 0) | (follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW)));
return Result<void>::from_syscall(rc); return Result<void>::from_syscall(rc);
} }