Linux Kernel Library: ядро Linux в форм-факторе so или dll

в 12:29, , рубрики: C, fuse, linux file systems, linux kernel library, lkl, open source, Разработка под Linux, системное администрирование, системное программирование

Когда-то читал статью о выборе файловых систем «чтоб везде-везде работало». В ней в очередной раз увидел жалобы, что Ext4 замечательная файловая система, но на Windows есть только кривые неточные проприетарные драйверы. Но отмотаем ленту ещё на пару лет назад: тогда на Хабре (а в те времена — Гиктаймсе) пролетала новость про LibOS — попытку превратить Linux kernel в обычную библиотеку пользовательского режима. Упор там делался на вынос сетевого стека в user space. Как-то раз я решил поглядеть, а жив ли вообще проект, и в их блоге увидел ссылку на своего рода конкурента — проект Linux Kernel Library (LKL). По сути, это порт ядра, так сказать, на аппаратную архитектуру «библиотека пользовательского режима POSIX / Win32».

Чем интересна LKL? Во-первых тем, что она живёт и здравствует, пусть и не в основной кодовой базе ядра. Во-вторых, это более-менее честная поддержка «архитектуры», автоматически делающая доступной бОльшую часть ядра. Более того, прямо в комплекте идут утилиты-примеры: cptofs/cpfromfs, fs2tar, lklfuse. В этой статье мы протестируем LKL на хостовом Linux, заглянем в файл с образом Ext4 без рута и виртуалок и коротко обсудим, как её можно попробовать на Windows.

DISCLAIMER 1: Захотите попробовать — делайте бекапы. Если вы захотите проделать такое с разделом с важными данными — на свой страх и риск. Впрочем, здесь хотя бы драйверы будут реально родные.

DISCLAIMER 2: Уважайте лицензии. Вероятно, линковка с LKL делает вашу программу GPL'ной.

Первичное знакомство

Репозиторий LKL (lkl/linux на GitHub) представляет из себя форк обычного Linux kernel, в котором добавлена поддержка ещё одной архитектуры, в основном мы будет видеть это в каталогах arch/lkl и tools/lkl. Сделаем клон репозитория и попробуем собрать по инструкции. Для экспериментов я буду использовать shallow clone, который содержит не всю историю репозитория, а лишь указанное количество последних коммитов:

$ git clone https://github.com/lkl/linux.git lkl-linux --depth 10
$ cd lkl-linux
$ patch -p 1 <<EOF
diff --git a/tools/lkl/lib/hijack/xlate.c b/tools/lkl/lib/hijack/xlate.c
index 03ccc6294..75368dcc2 100644
--- a/tools/lkl/lib/hijack/xlate.c
+++ b/tools/lkl/lib/hijack/xlate.c
@@ -3,6 +3,7 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <linux/sockios.h>
 #undef st_atime
 #undef st_mtime
 #undef st_ctime
EOF
$ make -C tools/lkl -j4

Пришлось чуточку поправить исходник, но в итоге получилась библиотека tools/lkl/lib/liblkl.so (а ещё — статическая tools/lkl/liblkl.a):

nm -D tools/lkl/lib/liblkl.so
                 U __assert_fail
                 U bind
                 U calloc
                 U clock_gettime
                 U close
                 w __cxa_finalize
0000000000063b30 T dbg_entrance
0000000000063f30 T dbg_handler
                 U __errno_location
                 U fcntl
                 U fdatasync
0000000000639580 D fd_net_ops
                 U fgets
                 U __fprintf_chk
                 U free
                 U fwrite
                 U getc
                 U getenv
                 w __gmon_start__
                 U if_nametoindex
                 U inet_pton
                 U ioctl
                 U __isoc99_scanf
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000061750 T jmp_buf_longjmp
0000000000061720 T jmp_buf_set
0000000000065470 T jsmn_init
0000000000065060 T jsmn_parse
0000000000065490 T jsmn_strerror
00000000000614c0 T lkl_add_gateway
0000000000061290 T lkl_add_neighbor
00000000000621a0 T lkl_bug
000000000005f070 T lkl_closedir
0000000000639520 D lkl_dev_blk_ops
000000000005fa10 T lkl_dirfd
0000000000062640 T lkl_disk_add
0000000000062780 T lkl_disk_remove
000000000005ec50 T lkl_encode_dev_from_sysfs
000000000005f9f0 T lkl_errdir
000000000005ef80 T lkl_fdopendir
0000000000067f10 T lkl_get_free_irq
000000000005f2c0 T lkl_get_virtio_blkdev
00000000006395c0 D lkl_host_ops
00000000000614b0 T lkl_if_add_gateway
00000000000613e0 T lkl_if_add_ip
00000000000614a0 T lkl_if_add_linklocal
0000000000061520 T lkl_if_add_rule_from_saddr
0000000000061480 T lkl_if_del_ip
0000000000060d70 T lkl_if_down
0000000000060b10 T lkl_ifname_to_ifindex
0000000000061400 T lkl_if_set_ipv4
0000000000061530 T lkl_if_set_ipv4_gateway
0000000000061430 T lkl_if_set_ipv6
00000000000615b0 T lkl_if_set_ipv6_gateway
0000000000060ef0 T lkl_if_set_mtu
0000000000060bf0 T lkl_if_up
0000000000061160 T lkl_if_wait_ipv6_dad
000000000005fba0 T lkl_iomem_access
000000000005fb50 T lkl_ioremap
0000000000067730 T lkl_is_running
0000000000066150 T lkl_load_config_env
0000000000065950 T lkl_load_config_json
0000000000066880 T lkl_load_config_post
0000000000066510 T lkl_load_config_pre
000000000005f470 T lkl_mount_dev
000000000005eae0 T lkl_mount_fs
00000000000642a0 T lkl_netdev_add
00000000000645c0 T lkl_netdev_free
0000000000061030 T lkl_netdev_get_ifindex
0000000000064e70 T lkl_netdev_macvtap_create
0000000000064ed0 T lkl_netdev_pipe_create
0000000000064ce0 T lkl_netdev_raw_create
00000000000644c0 T lkl_netdev_remove
0000000000064c60 T lkl_netdev_tap_create
0000000000064a10 T lkl_netdev_tap_init
000000000005eea0 T lkl_opendir
0000000000062170 T lkl_perror
00000000000620b0 T lkl_printf
0000000000067f90 T lkl_put_irq
0000000000061620 T lkl_qdisc_add
0000000000061630 T lkl_qdisc_parse_add
000000000005f0f0 T lkl_readdir
0000000000063f80 T lkl_register_dbg_handler
0000000000064930 T lkl_register_netdev_fd
000000000005efe0 T lkl_rewinddir
000000000005fa20 T lkl_set_fd_limit
00000000000614e0 T lkl_set_ipv4_gateway
0000000000061500 T lkl_set_ipv6_gateway
0000000000065f60 T lkl_show_config
00000000004f51ad T lkl_start_kernel
0000000000062080 T lkl_strerror
00000000000685f0 T lkl_syscall
0000000000062270 T lkl_sysctl
0000000000062410 T lkl_sysctl_parse_write
0000000000067770 T lkl_sys_halt
00000000000680e0 T lkl_trigger_irq
000000000005f870 T lkl_umount_dev
000000000005edc0 T lkl_umount_timeout
0000000000066ed0 T lkl_unload_config
00000000008186a0 B lkl_virtio_devs
                 U __longjmp_chk
                 U lseek64
                 U malloc
                 U memchr
                 U memcpy
                 U memset
                 U open
                 U perror
                 U pipe
                 U poll
0000000000064070 T poll_thread
                 U pread64
                 U __printf_chk
                 U pthread_create
                 U pthread_detach
                 U pthread_exit
                 U pthread_getspecific
                 U pthread_join
                 U pthread_key_create
                 U pthread_key_delete
                 U pthread_mutexattr_init
                 U pthread_mutexattr_settype
                 U pthread_mutex_destroy
                 U pthread_mutex_init
                 U pthread_mutex_lock
                 U pthread_mutex_unlock
                 U pthread_self
                 U pthread_setspecific
                 U puts
                 U pwrite64
                 U read
                 U readv
00000000008196a0 B registered_devs
000000000005fa90 T register_iomem
                 U sem_destroy
                 U sem_init
                 U sem_post
                 U sem_wait
                 U _setjmp
                 U setsockopt
                 U sigaction
                 U sigemptyset
                 U __snprintf_chk
                 U socket
                 U __stack_chk_fail
                 U stderr
                 U stdin
                 U stpcpy
                 U strchr
                 U strcpy
                 U __strcpy_chk
                 U strdup
                 U strerror
                 U strlen
                 U strncat
                 U __strncat_chk
                 U strncmp
                 U strncpy
                 U strrchr
                 U strtok
                 U strtok_r
                 U strtol
                 U strtoul
                 U syscall
                 U timer_create
                 U timer_delete
                 U timer_settime
000000000005fb00 T unregister_iomem
                 U usleep
0000000000063110 T virtio_dev_cleanup
0000000000062ee0 T virtio_dev_setup
0000000000063100 T virtio_get_num_bootdevs
0000000000062c10 T virtio_process_queue
0000000000062af0 T virtio_req_complete
0000000000062ec0 T virtio_set_queue_max_merge_len
                 U __vsnprintf_chk
                 U write
                 U writev

А где же системные вызовы, спросите вы. Без паники, они спрятаны за общей точкой входа lkl_syscall. Это такой аналог функции syscall для LKL. В реальной же ситуации в большинстве случаев вы будете использовать типизированные обёртки lkl_sys_<name>. Также мы видим всякие функции для настройки «ядра», добавления в него виртуальных устройств, а также обёртки над «сложными» системными вызовами, в обычной системе предоставляемые libc. Например, есть такой системный вызов getdents, но… «These are not the interfaces you are interested in.» — с порога говорит нам man-страница. В обычных же случаях предполагается использовать стандартную библиотечную функцию readdir (3), но не путайте её с readdir (2) — древним системным вызовом, который на x86_64 даже не реализован. В случае же работы с LKL вам потребуются обёртки lkl_opendir / lkl_readdir / lkl_closedir.

Попробуем что-нибудь написать

Напоминаю, уважайте лицензии. Сам Linux kernel распространяется под GPL2, будет ли программа, дёргающая за относительно публичные интерфейсы LKL считаться производной работой — я не знаю.

Что же, давайте попробуем слинковаться с библиотекой. Предполагается, что переменной $LKL присвоен путь до репозитория со скомпилированной LKL.

#include <stdio.h>

#include "lkl_host.h"
#include "lkl.h"

int main()
{
    // lkl_host_ops содержит указатели на функции для различных
    //     "платформенно-зависимых" операций: `printk`, `panic`, ...
    // Строка, передаваемая вторым аргументом -- полноценная командная строка ядра
    lkl_start_kernel(&lkl_host_ops, "mem=128M");

    return 0;
}

Скомпилируем:

$ gcc test.c -o test -I$LKL/tools/lkl/include -L$LKL/tools/lkl/lib -llkl

И оно работает!

$ ./test
./test: error while loading shared libraries: liblkl.so: cannot open shared object file: No such file or directory
$ LD_LIBRARY_PATH=$LKL/tools/lkl/lib ./test
[    0.000000] Linux version 5.3.0+ (trosinenko@trosinenko-pc) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #1 Tue Dec 3 14:37:02 MSK 2019
[    0.000000] memblock address range: 0x7fba8c000000 - 0x7fba93fff000
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32319
[    0.000000] Kernel command line: mem=128M
[    0.000000] Dentry cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
[    0.000000] Inode-cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory available: 129044k/131068k RAM
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] NR_IRQS: 4096
[    0.000000] lkl: irqs initialized
[    0.000000] clocksource: lkl: mask: 0xffffffffffffffff max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
[    0.000000] lkl: time and timers initialized (irq1)
[    0.000003] pid_max: default: 4096 minimum: 301
[    0.000019] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.000022] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.003622] random: get_random_bytes called from _etext+0xbcdb/0x14b05 with crng_init=0
[    0.003692] printk: console [lkl_console0] enabled
[    0.003707] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.003714] xor: automatically using best checksumming function   8regs
[    0.003783] NET: Registered protocol family 16
[    0.171647] raid6: int64x8  gen()  4489 MB/s
[    0.343119] raid6: int64x8  xor()  3165 MB/s
[    0.514836] raid6: int64x4  gen()  4668 MB/s
[    0.689529] raid6: int64x4  xor()  3256 MB/s
[    0.861155] raid6: int64x2  gen()  6283 MB/s
[    1.032668] raid6: int64x2  xor()  3793 MB/s
[    1.206752] raid6: int64x1  gen()  5185 MB/s
[    1.378219] raid6: int64x1  xor()  2901 MB/s
[    1.378225] raid6: using algorithm int64x2 gen() 6283 MB/s
[    1.378227] raid6: .... xor() 3793 MB/s, rmw enabled
[    1.378229] raid6: using intx1 recovery algorithm
[    1.378333] clocksource: Switched to clocksource lkl
[    1.378427] NET: Registered protocol family 2
[    1.378516] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
[    1.378521] TCP established hash table entries: 1024 (order: 1, 8192 bytes, linear)
[    1.378527] TCP bind hash table entries: 1024 (order: 1, 8192 bytes, linear)
[    1.378532] TCP: Hash tables configured (established 1024 bind 1024)
[    1.378596] UDP hash table entries: 128 (order: 0, 4096 bytes, linear)
[    1.378618] UDP-Lite hash table entries: 128 (order: 0, 4096 bytes, linear)
[    1.379286] workingset: timestamp_bits=62 max_order=16 bucket_order=0
[    1.380271] SGI XFS with ACLs, security attributes, no debug enabled
[    1.380864] io scheduler mq-deadline registered
[    1.380872] io scheduler kyber registered
[    1.383396] NET: Registered protocol family 10
[    1.383763] Segment Routing with IPv6
[    1.383779] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    1.384091] Btrfs loaded, crc32c=crc32c-generic
[    1.384223] Warning: unable to open an initial console.
[    1.384237] This architecture does not have kernel memory protection.
[    1.384239] Run /init as init process

Можно даже видеть по timestamp'ам, что ядро не просто «выплюнуло» в консоль этот текст, а красиво постепенно грузилось как настоящее.

Усложняем эксперимент

Давайте теперь попробуем как-то по-настоящему использовать эту библиотеку — всё-таки целое ядро ОС! Попробуем чисто в user space прочитать файл с Ext4-раздела. Причём «родным» драйвером! За основу возьмём tools/lkl/cptofs.c и реализуем только самое необходимое (для наглядности):

#undef NDEBUG

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#include "lkl_host.h"
#include "lkl.h"

// Обработка ошибок показана не везде и кое как,
// чтобы не загромождать исходник -- не повторяйте этого дома :)

int main(int argc, const char *argv[])
{
  const char * const fsimage = argv[1];
  const char * const fstype = argv[2];
  const char * const file_to_dump = argv[3];

  struct lkl_disk disk;
  int disk_id, ret;
  char mpoint[128];

  // Открываем файл с образом диска
  memset(&disk, 0, sizeof(disk));
  disk.fd = open(fsimage, O_RDONLY);
  assert(disk.fd >= 0);

  // Подцепляем его как блочное устройство
  disk_id = lkl_disk_add(&disk);
  assert(disk_id >= 0);

  // Запускаем ядро
  lkl_start_kernel(&lkl_host_ops, "mem=128M");

  // Монтируем раздел на блочном устройстве
  ret = lkl_mount_dev(disk_id, 0 /* part */, fstype,
                      LKL_MS_RDONLY, NULL,
                      mpoint, sizeof(mpoint));

  if (ret < 0) {
    fprintf(stderr, "lkl_mount_dev failed: %sn", lkl_strerror(ret));
    close(disk.fd);
    exit(1);
  }

  // Так, что тут у нас...
  // (воспользуемся псевдо-libc обёртками)
  struct lkl_dir *dir = lkl_opendir(mpoint, &ret);
  struct lkl_linux_dirent64 *dent;
  while ((dent = lkl_readdir(dir)) != NULL) {
    fprintf(stderr, "Directory entry: %sn", dent->d_name);
  }
  // тут нужно было бы понять: NULL -- это ошибка или конец каталога...
  lkl_closedir(dir);

  // Попробуем прочитать что-нибудь
  // Здесь используем обёртки над непосредственными системными вызовами
  char tmp[256];
  uint8_t buffer[65536];
  snprintf(tmp, sizeof(tmp), "%s/%s", mpoint, file_to_dump);
  int fd = lkl_sys_open(tmp, LKL_O_RDONLY, 0);
  fprintf(stderr, "fd = %dn", fd);
  assert(fd >= 0);
  int count = lkl_sys_read(fd, buffer, sizeof(buffer));
  /* хостовый */ write(STDERR_FILENO, buffer, count);
  lkl_sys_close(fd);

  return 0;
}

Обратите внимание на переименованные define'ы с префиксами LKL_ (например, LKL_O_RDONLY): на Linux-хосте они, скорее всего, совпадают с теми, что без префиксов, а вот на других системах — не факт.

$ mke2fs ext4.img -t ext4 32M
$ sudo mount ext4.img /mnt
$ echo -e "Hello world!nTEST" | sudo tee /mnt/test.txt
$ sudo umount /mnt
$ LD_LIBRARY_PATH=$LKL/tools/lkl/lib ./read-file ext4.img ext4 test.txt
[    0.000000] Linux version 5.3.0+ (trosinenko@trosinenko-pc) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #1 Tue Dec 3 14:37:02 MSK 2019
// ... //
[    1.378960] Warning: unable to open an initial console.
[    1.378975] This architecture does not have kernel memory protection.
[    1.378977] Run /init as init process
[    1.379852] EXT4-fs (vda): mounted filesystem with ordered data mode. Opts:
Directory entry: test.txt
Directory entry: ..
Directory entry: lost+found
Directory entry: .
fd = 0
Hello world!
TEST

Ух ты, работает! А что-нибудь более экзотическое?

$ mksquashfs test.c read-file.c squashfs.img
$ LD_LIBRARY_PATH=$LKL/tools/lkl/lib ./read-file squashfs.img squashfs test.c
[    0.000000] Linux version 5.3.0+ (trosinenko@trosinenko-pc) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #1 Tue Dec 3 14:37:02 MSK 2019
// ... //
[    1.378472] This architecture does not have kernel memory protection.
[    1.378474] Run /init as init process
lkl_mount_dev failed: No such device

Ой! Хотя, постойте, мы же, наверное, просто не включили в наше ядро-библиотеку поддержку SquashFS!

Настраиваем параметры сборки LKL

Для себя я выработал такую последовательность команд, которая работает для LKL — возможно, её можно сократить вплоть до традиционного make defconfig, make menuconfig, make.

$ make defconfig ARCH=lkl
$ make menuconfig ARCH=lkl
//// тут включаем SquashFS и выходим с сохранением конфигурации
$ cp .config arch/lkl/configs/defconfig
$ make mrproper
$ make -C tools/lkl -j4 # Это мы уже видели

И вуаля!

$ gcc read-file.c -o read-file -I$LKL/tools/lkl/include -L$LKL/tools/lkl/lib -llkl
$ LD_LIBRARY_PATH=$LKL/tools/lkl/lib ./read-file squashfs.img squashfs test.c
[    0.000000] Linux version 5.3.0+ (trosinenko@trosinenko-pc) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #1 Wed Dec 4 12:07:50 MSK 2019
// ... //
[    1.378346] This architecture does not have kernel memory protection.
[    1.378348] Run /init as init process
Directory entry: .
Directory entry: ..
Directory entry: read-file.c
Directory entry: test.c
fd = 0
#include <stdio.h>

#include "lkl_host.h"
#include "lkl.h"

int main()
{
    lkl_start_kernel(&lkl_host_ops, "mem=128M");

    return 0;
}

В данном случае, правда, даже перекомпилировать read-file.c едва ли было нужно — библиотека-то динамическая.

Позвольте, а где обещанные готовые программки?

И действительно, в каталоге tools/lkl лежат cptofs.c, fs2tar.c и ещё много интересного, но оно не собирается! Порывшись в Makefile'ах я обнаружил, что есть некий Makefile.autoconf, который ищет требуемые заголовочные файлы, и Makefile.conf, куда это всё записывается.

Так-с, кто-то хочет libarchive, кто-то libfuse — ну что же, поставим libarchive-dev, libfuse-dev (в случае Ubuntu) и пересоберём. Всё равно не получается… А если удалить Makefile.conf… Опа, собралось!

Итак, что же теперь у нас есть? Теперь в каталоге tools/lkl у нас есть cptofs, fs2tar и lklfuse.

Для начала скопируем cptofs под именем cpfromfs:

$ $LKL/tools/lkl/cptofs --help
Usage: cptofs [OPTION...] -t fstype -i fsimage path... fs_path
Copy files to a filesystem image

  -i, --filesystem-image=string   path to the filesystem image - mandatory
  -p, --enable-printk        show Linux printks
  -P, --partition=int        partition number
  -s, --selinux=string       selinux attributes for destination
  -t, --filesystem-type=string   select filesystem type - mandatory
  -?, --help                 Give this help list
      --usage                Give a short usage message

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
$ cp $LKL/tools/lkl/cp{to,from}fs
$ $LKL/tools/lkl/cpfromfs --help
Usage: cpfromfs [OPTION...] -t fstype -i fsimage fs_path... path
Copy files from a filesystem image

  -i, --filesystem-image=string   path to the filesystem image - mandatory
  -p, --enable-printk        show Linux printks
  -P, --partition=int        partition number
  -s, --selinux=string       selinux attributes for destination
  -t, --filesystem-type=string   select filesystem type - mandatory
  -?, --help                 Give this help list
      --usage                Give a short usage message

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Как говорится, «Как вы яхту назовёте...». Запускаем...

$ $LKL/tools/lkl/cpfromfs -t ext4 -i ext4.img test.txt .
error processing entry /mnt/0000fe00/test.txt, aborting

Хмм… Надо посмотреть… Впрочем, для интерактивного использования оно всё равно неудобно, поскольку каждый раз приходится ждать около секунды, пока ядро загрузится. Зато fs2tar работает без проблем:

$ $LKL/tools/lkl/fs2tar -t ext4 ext4.img ext4.tar
$ tar -tf ext4.tar
tar: Удаляется начальный `/' из имен объектов
/test.txt
/lost+found/

Но самая интересная программа здесь на мой взгляд — это lklfuse:

$ mkdir mountpoint
$ $LKL/tools/lkl/lklfuse -o type=ext4 ext4.img mountpoint/
$ ls mountpoint/
lost+found  test.txt
$ mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
... ещё много всего, потому что это таблица монтирования хоста
/dev/fuse on /run/user/1000/doc type fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
lklfuse on /path/to/mountpoint type fuse.lklfuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
$ echo ABC > mountpoint/ABC.XYZ
$ umount mountpoint
$ sudo mount ext4.img /mnt
$ ls /mnt
$ cat /mnt/ABC.XYZ
ABC

Мало того, что lklfuse позволяет обычному пользователю подмонтировать раздел с помощью штатного драйвера ядра. Хостовое ядро вообще не обязано быть собрано с поддержкой этой ФС. Да что уж там, не удивлюсь, если это всё точно так же заведётся на OS X.

Немного про кросс-платформенность

А что же с доступом к линуксовым ФС из других операционных систем? На OS X, думаю, будет попроще: всё-таки она полноценный UNIX, да и поддержка FUSE, вроде, есть. Так что есть надежда, что оно заведётся с ходу. Если нет, я бы посмотрел в сторону проверки того, везде ли в системные вызовы LKL передаются константы с префиксами LKL_, а не их хостовые аналоги.

С Windows несколько сложнее: во-первых, там банально может не быть некоторых привычных в мире UNIX библиотек (например, для разбора аргументов командной строки). Во-вторых, нужно понять, как подмонтироваться к хостовому дереву файловых систем. Самое простое было бы — так же через FUSE. Говорят, когда-то был некий Dokan, сейчас тоже что-то есть, но нужно гуглить. Главное, что сама LKL на Windows собирается, нужно только учесть, что ей требуется 64-битный тип long для работы в 64-битном режиме, поэтому не каждый компилятор подойдёт (по крайней мере, так написано в текущем readme проекта).

Автор: Анатолий Тросиненко

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js