Bläddra i källkod

Kernel & Userland: Allow to mount image files formatted with Ext2FS

Liav A 5 år sedan
förälder
incheckning
a60ea79a41

+ 3 - 3
Base/etc/fstab

@@ -5,6 +5,6 @@
 /dev	/dev	bind	bind,nosuid
 /dev	/dev	bind	bind,nosuid
 /bin	/bin	bind	bind,nodev
 /bin	/bin	bind	bind,nodev
 
 
-proc	/proc	proc	nosuid
-devpts	/dev/pts	devpts	noexec,nosuid
-tmp	/tmp	tmp	nodev,nosuid
+none	/proc	proc	nosuid
+none	/dev/pts	devpts	noexec,nosuid
+none	/tmp	tmp	nodev,nosuid

+ 19 - 9
Base/usr/share/man/man2/mount.md

@@ -7,12 +7,13 @@ mount - mount a filesystem
 ```**c++
 ```**c++
 #include <unistd.h>
 #include <unistd.h>
 
 
-int mount(const char* source, const char* target, const char* fs_type, int flags);
+int mount(int source_fd, const char* target, const char* fs_type, int flags);
 ```
 ```
 
 
 ## Description
 ## Description
 
 
-`mount()` mounts a filesystem stored at `source` by overlaying its contents over `target`.
+`mount()` mounts a filesystem stored at `source_fd` by overlaying its contents
+over `target`.
 
 
 `fs_type` must be one of the following supported filesystems:
 `fs_type` must be one of the following supported filesystems:
 
 
@@ -21,9 +22,10 @@ int mount(const char* source, const char* target, const char* fs_type, int flags
 * `DevPtsFS` (or `devpts`): The pseudoterminal pseudo-filesystem (normally mounted at `/dev/pts`).
 * `DevPtsFS` (or `devpts`): The pseudoterminal pseudo-filesystem (normally mounted at `/dev/pts`).
 * `TmpFS` (or `tmp`): A non-persistent filesystem that stores all its data in RAM. An instance of this filesystem is normally mounted at `/tmp`.
 * `TmpFS` (or `tmp`): A non-persistent filesystem that stores all its data in RAM. An instance of this filesystem is normally mounted at `/tmp`.
 
 
-For Ext2FS, `source` must be a path to a block device storing the filesystem contents. All
-the other filesystems ignore the `source` argument (by convention, it should have the same
-value as `fs_type`).
+For Ext2FS, `source_fd` must refer to an open file descriptor to a file containing
+the filesystem image. This may be a device file or any other seekable file. All
+the other filesystems ignore the `source_fd` — you can even pass an invalid file
+descriptor such as -1.
 
 
 The following `flags` are supported:
 The following `flags` are supported:
 
 
@@ -37,14 +39,22 @@ mounted file system.
 
 
 ### Bind mounts
 ### Bind mounts
 
 
-If `MS_BIND` is specified in `flags`, `fs_type` is ignored and a bind mount is performed
-instead. In this case `source` is treated as a path to a file or directory whose contents
-are overlayed over `target`. This can be used  as an alternative to symlinks or hardlinks.
+If `MS_BIND` is specified in `flags`, `fs_type` is ignored and a bind mount is
+performed instead. In this case, the file or directory specified by `source_fd`
+is overlayed over `target` — the target appears to be replaced by a copy of the
+source. This can be used as an alternative to symlinks or hardlinks.
 
 
 ## Errors
 ## Errors
 
 
+* `EFAULT`: The `fs_type` or `target` are invalid strings.
 * `EPERM`: The current process does not have superuser privileges.
 * `EPERM`: The current process does not have superuser privileges.
-* `ENODEV`: The `fs_type` is unrecognized, or the device is not found, or the device doesn't contain a valid filesystem image.
+* `ENODEV`: The `fs_type` is unrecognized, or the file descriptor to source is
+  not found, or the source doesn't contain a valid filesystem image. Also, this
+  error occurs if `fs_type` is valid, but the file descriptor from `source_fd`
+  is not seekable.
+* `EBADF`: If the `source_fd` is not valid, and either `fs_type` specifies a
+  file-backed filesystem (and not a pseudo filesystem), or `MS_BIND` is
+  specified in flags.
 
 
 All of the usual path resolution errors may also occur.
 All of the usual path resolution errors may also occur.
 
 

+ 20 - 12
Base/usr/share/man/man8/mount.md

@@ -12,18 +12,26 @@ $ mount
 
 
 ## Description
 ## Description
 
 
-If invoked without any arguments, `mount` prints a list of all currently mounted filesystems.
-
-If invoked as `mount -a`, `mount` mounts all the filesystems configured in `/etc/fstab`. This
-is normally done on system startup by [`SystemServer`(7)](../man7/SystemServer.md).
-
-Otherwise, `mount` performs a single filesystem mount. Source, target, and fstype have the
-same meaning as in the [`mount`(2)](../man2/mount.md) syscall (if not specified, fstype
-defaults to `ext2`).
-
-Options correspond to the mount flags, and should be specified as a comma-separated list of
-flag names (lowercase and without the `MS_` prefix). Additionally, the name `defaults` is
-accepted and ignored.
+If invoked without any arguments, `mount` prints a list of all currently mounted
+filesystems.
+
+If invoked as `mount -a`, `mount` mounts all the filesystems configured in
+`/etc/fstab`. This is normally done on system startup by
+[`SystemServer`(7)](../man7/SystemServer.md).
+
+Otherwise, `mount` performs a single filesystem mount. Source should be a path
+to a file containing the filesystem image. Target, and fstype have the same
+meaning as in the [`mount`(2)](../man2/mount.md) syscall (if not specified,
+fstype defaults to `ext2`).
+
+A special source value "none" is recognized, in which case
+[`mount`(8)](mount.md) will not attempt to open the source as a file, and will
+pass an invalid file descriptor to [`mount`(2)](../man2/mount.md). This is
+useful for mounting  pseudo filesystems.
+
+Options correspond to the mount flags, and should be specified as a
+comma-separated list of flag names (lowercase and without the `MS_` prefix).
+Additionally, the name `defaults` is accepted and ignored.
 
 
 ## Files
 ## Files
 
 

+ 23 - 20
Kernel/Process.cpp

@@ -4011,14 +4011,18 @@ int Process::sys$mount(const Syscall::SC_mount_params* user_params)
     if (!validate_read_and_copy_typed(&params, user_params))
     if (!validate_read_and_copy_typed(&params, user_params))
         return -EFAULT;
         return -EFAULT;
 
 
-    auto source = validate_and_copy_string_from_user(params.source);
+    auto source_fd = params.source_fd;
     auto target = validate_and_copy_string_from_user(params.target);
     auto target = validate_and_copy_string_from_user(params.target);
     auto fs_type = validate_and_copy_string_from_user(params.fs_type);
     auto fs_type = validate_and_copy_string_from_user(params.fs_type);
 
 
-    if (source.is_null() || target.is_null() || fs_type.is_null())
+    if (target.is_null() || fs_type.is_null())
         return -EFAULT;
         return -EFAULT;
 
 
-    dbg() << "mount " << fs_type << ": source " << source << " @ " << target;
+    auto description = file_description(source_fd);
+    if (!description.is_null())
+        dbg() << "mount " << fs_type << ": source fd " << source_fd << " @ " << target;
+    else
+        dbg() << "mount " << fs_type << " @ " << target;
 
 
     auto custody_or_error = VFS::the().resolve_path(target, current_directory());
     auto custody_or_error = VFS::the().resolve_path(target, current_directory());
     if (custody_or_error.is_error())
     if (custody_or_error.is_error())
@@ -4030,28 +4034,24 @@ int Process::sys$mount(const Syscall::SC_mount_params* user_params)
 
 
     if (params.flags & MS_BIND) {
     if (params.flags & MS_BIND) {
         // We're doing a bind mount.
         // We're doing a bind mount.
-        auto source_or_error = VFS::the().resolve_path(source, current_directory());
-        if (source_or_error.is_error())
-            return source_or_error.error();
-        auto& source_custody = source_or_error.value();
-        return VFS::the().bind_mount(source_custody, target_custody, params.flags);
+        if (description.is_null())
+            return -EBADF;
+        ASSERT(description->custody());
+        return VFS::the().bind_mount(*description->custody(), target_custody, params.flags);
     }
     }
 
 
     if (fs_type == "ext2" || fs_type == "Ext2FS") {
     if (fs_type == "ext2" || fs_type == "Ext2FS") {
-        auto source_or_error = VFS::the().open(source, O_RDWR, 0, current_directory());
-        if (source_or_error.is_error())
-            return source_or_error.error();
-
-        auto* device = source_or_error.value()->device();
-        if (!device || !device->is_block_device()) {
-            dbg() << "mount: this is not a BlockDevice";
+        if (description.is_null())
+            return -EBADF;
+        ASSERT(description->custody());
+        if (!description->file().is_seekable()) {
+            dbg() << "mount: this is not a seekable file";
             return -ENODEV;
             return -ENODEV;
         }
         }
-        auto& block_device = static_cast<BlockDevice&>(*device);
 
 
-        dbg() << "mount: attempting to mount " << block_device.absolute_path() << " on " << target;
+        dbg() << "mount: attempting to mount " << description->absolute_path() << " on " << target;
 
 
-        fs = Ext2FS::create(block_device);
+        fs = Ext2FS::create(*description);
     } else if (fs_type == "proc" || fs_type == "ProcFS") {
     } else if (fs_type == "proc" || fs_type == "ProcFS") {
         fs = ProcFS::create();
         fs = ProcFS::create();
     } else if (fs_type == "devpts" || fs_type == "DevPtsFS") {
     } else if (fs_type == "devpts" || fs_type == "DevPtsFS") {
@@ -4063,12 +4063,15 @@ int Process::sys$mount(const Syscall::SC_mount_params* user_params)
     }
     }
 
 
     if (!fs->initialize()) {
     if (!fs->initialize()) {
-        dbg() << "mount: failed to initialize " << fs_type << " filesystem on " << source;
+        dbg() << "mount: failed to initialize " << fs_type << " filesystem, fd - " << source_fd;
         return -ENODEV;
         return -ENODEV;
     }
     }
 
 
     auto result = VFS::the().mount(fs.release_nonnull(), target_custody, params.flags);
     auto result = VFS::the().mount(fs.release_nonnull(), target_custody, params.flags);
-    dbg() << "mount: successfully mounted " << source << " on " << target;
+    if (!description.is_null())
+        dbg() << "mount: successfully mounted " << description->absolute_path() << " on " << target;
+    else
+        dbg() << "mount: successfully mounted " << target;
     return result;
     return result;
 }
 }
 
 

+ 1 - 1
Kernel/Syscall.h

@@ -395,7 +395,7 @@ struct SC_rename_params {
 };
 };
 
 
 struct SC_mount_params {
 struct SC_mount_params {
-    StringArgument source;
+    int source_fd;
     StringArgument target;
     StringArgument target;
     StringArgument fs_type;
     StringArgument fs_type;
     int flags;
     int flags;

+ 4 - 3
Libraries/LibC/unistd.cpp

@@ -582,14 +582,15 @@ int reboot()
     __RETURN_WITH_ERRNO(rc, rc, -1);
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }
 }
 
 
-int mount(const char* source, const char* target, const char* fs_type, int flags)
+int mount(int source_fd, const char* target, const char* fs_type, int flags)
 {
 {
-    if (!source || !target || !fs_type) {
+    if (!target || !fs_type) {
         errno = EFAULT;
         errno = EFAULT;
         return -1;
         return -1;
     }
     }
+
     Syscall::SC_mount_params params {
     Syscall::SC_mount_params params {
-        { source, strlen(source) },
+        source_fd,
         { target, strlen(target) },
         { target, strlen(target) },
         { fs_type, strlen(fs_type) },
         { fs_type, strlen(fs_type) },
         flags
         flags

+ 1 - 1
Libraries/LibC/unistd.h

@@ -129,7 +129,7 @@ int fchown(int fd, uid_t, gid_t);
 int ftruncate(int fd, off_t length);
 int ftruncate(int fd, off_t length);
 int halt();
 int halt();
 int reboot();
 int reboot();
-int mount(const char* source, const char* target, const char* fs_type, int flags);
+int mount(int source_fd, const char* target, const char* fs_type, int flags);
 int umount(const char* mountpoint);
 int umount(const char* mountpoint);
 int pledge(const char* promises, const char* execpromises);
 int pledge(const char* promises, const char* execpromises);
 int unveil(const char* path, const char* permissions);
 int unveil(const char* path, const char* permissions);

+ 33 - 5
Userland/mount.cpp

@@ -27,8 +27,10 @@
 #include <AK/JsonArray.h>
 #include <AK/JsonArray.h>
 #include <AK/JsonObject.h>
 #include <AK/JsonObject.h>
 #include <AK/JsonValue.h>
 #include <AK/JsonValue.h>
+#include <AK/Optional.h>
 #include <LibCore/ArgsParser.h>
 #include <LibCore/ArgsParser.h>
 #include <LibCore/File.h>
 #include <LibCore/File.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <unistd.h>
 #include <unistd.h>
@@ -54,6 +56,27 @@ int parse_options(const StringView& options)
     return flags;
     return flags;
 }
 }
 
 
+bool is_source_none(const char* source)
+{
+    return !strcmp("none", source);
+}
+
+int get_source_fd(const char* source)
+{
+    if (is_source_none(source))
+        return -1;
+    int fd = open(source, O_RDWR);
+    if (fd < 0)
+        fd = open(source, O_RDONLY);
+    if (fd < 0) {
+        int saved_errno = errno;
+        auto message = String::format("Failed to open: %s\n", source);
+        errno = saved_errno;
+        perror(message.characters());
+    }
+    return fd;
+}
+
 bool mount_all()
 bool mount_all()
 {
 {
     // Mount all filesystems listed in /etc/fstab.
     // Mount all filesystems listed in /etc/fstab.
@@ -86,7 +109,6 @@ bool mount_all()
             continue;
             continue;
         }
         }
 
 
-        const char* devname = parts[0].characters();
         const char* mountpoint = parts[1].characters();
         const char* mountpoint = parts[1].characters();
         const char* fstype = parts[2].characters();
         const char* fstype = parts[2].characters();
         int flags = parts.size() >= 4 ? parse_options(parts[3]) : 0;
         int flags = parts.size() >= 4 ? parse_options(parts[3]) : 0;
@@ -96,11 +118,15 @@ bool mount_all()
             continue;
             continue;
         }
         }
 
 
-        dbg() << "Mounting " << devname << "(" << fstype << ")"
+        const char* filename = parts[0].characters();
+
+        int fd = get_source_fd(filename);
+
+        dbg() << "Mounting " << filename << "(" << fstype << ")"
               << " on " << mountpoint;
               << " on " << mountpoint;
-        int rc = mount(devname, mountpoint, fstype, flags);
+        int rc = mount(fd, mountpoint, fstype, flags);
         if (rc != 0) {
         if (rc != 0) {
-            fprintf(stderr, "Failed to mount %s (%s) on %s: %s\n", devname, fstype, mountpoint, strerror(errno));
+            fprintf(stderr, "Failed to mount %s (FD: %d) (%s) on %s: %s\n", filename, fd, fstype, mountpoint, strerror(errno));
             all_ok = false;
             all_ok = false;
             continue;
             continue;
         }
         }
@@ -179,7 +205,9 @@ int main(int argc, char** argv)
             fs_type = "ext2";
             fs_type = "ext2";
         int flags = options ? parse_options(options) : 0;
         int flags = options ? parse_options(options) : 0;
 
 
-        if (mount(source, mountpoint, fs_type, flags) < 0) {
+        int fd = get_source_fd(source);
+
+        if (mount(fd, mountpoint, fs_type, flags) < 0) {
             perror("mount");
             perror("mount");
             return 1;
             return 1;
         }
         }