]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/vfscore: Add non-largefile variant for dirent
authorStefan Jumarea <stefanjumarea02@gmail.com>
Thu, 29 Jun 2023 08:59:30 +0000 (11:59 +0300)
committerUnikraft <monkey@unikraft.io>
Wed, 16 Aug 2023 13:12:51 +0000 (13:12 +0000)
Unikraft defines the `dirent` structure as identical to `dirent64`.
This works fine if not using Unikraft in binary compatibility mode,
since we have total control over the internal functions/structures that
are used.

However, if we use Unikraft in binary compatibility mode, we want to use
Linux `dirent` and `dirent64` structures in order to maintain compatibility
with older libc versions that do not use the `*64` calls by default.
Since the filesystems `READDIR` vop uses `dirent64`, we will convert to a
`dirent` structure within the `readdir_r` call.

Signed-off-by: Stefan Jumarea <stefanjumarea02@gmail.com>
GitHub-Closes: #919
Reviewed-by: Radu Nichita <radunichita99@gmail.com>
Reviewed-by: Sergiu Moga <sergiu@unikraft.io>
Reviewed-by: Simon Kuenzer <simon@unikraft.io>
Approved-by: Simon Kuenzer <simon@unikraft.io>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #963

lib/9pfs/9pfs_vnops.c
lib/devfs/devfs_vnops.c
lib/nolibc/include/dirent.h
lib/ramfs/ramfs_vnops.c
lib/vfscore/Config.uk
lib/vfscore/Makefile.uk
lib/vfscore/include/vfscore/vnode.h
lib/vfscore/main.c
lib/vfscore/syscalls.c
lib/vfscore/vfs.h

index 41b45c7bab3aa18b87bb0d4d1b2266a2e7eb5e24..c6d98d941705ae6d06aece10904e5ba7545e8f02 100644 (file)
@@ -443,7 +443,7 @@ static int uk_9pfs_rmdir(struct vnode *dvp, struct vnode *vp,
 }
 
 static int uk_9pfs_readdir(struct vnode *vp, struct vfscore_file *fp,
-               struct dirent *dir)
+               struct dirent64 *dir)
 {
        struct uk_9pfs_mount_data *md = UK_9PFS_MD(vp->v_mount);
        struct uk_9pdev *dev = md->dev;
index 57d4145fabd19d35d9cb5375d268b0db2af24ea5..14020852916779378d3c4a50425e99085cc3223d 100644 (file)
@@ -181,7 +181,8 @@ devfs_lookup(struct vnode *dvp, const char *name, struct vnode **vpp)
  * @vp: vnode of the directory.
  */
 static int
-devfs_readdir(struct vnode *vp __unused, struct vfscore_file *fp, struct dirent *dir)
+devfs_readdir(struct vnode *vp __unused, struct vfscore_file *fp,
+             struct dirent64 *dir)
 {
        struct devinfo info;
        int error, i;
index 1c2927d1925dd8c12e555463524cc0fa07becc25..e1934745931443fb0f82953a588351237454e390 100644 (file)
@@ -19,7 +19,11 @@ typedef struct __dirstream DIR;
 #define _DIRENT_HAVE_D_OFF
 #define _DIRENT_HAVE_D_TYPE
 
-struct dirent {
+/**
+ * The dirent and dirent64 structures must match the Linux system call ABI
+ * for binary compatibility.
+ */
+struct dirent64 {
        ino_t d_ino;
        off_t d_off;
        unsigned short d_reclen;
@@ -27,13 +31,30 @@ struct dirent {
        char d_name[256];
 };
 
+#if CONFIG_LIBVFSCORE_NONLARGEFILE
+struct dirent {
+       unsigned long d_ino;
+       unsigned long d_off;
+       unsigned short d_reclen;
+       char d_name[256];
+       unsigned char pad;
+       unsigned char d_type;
+};
+#else /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
+#define dirent dirent64
+#endif /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
+
 #define d_fileno d_ino
 
 int            closedir(DIR *);
 DIR           *fdopendir(int);
 DIR           *opendir(const char *);
+struct dirent64 *readdir64(DIR *dir);
+int            readdir64_r(DIR *__restrict, struct dirent64 *__restrict,
+               struct dirent64 **__restrict);
 struct dirent *readdir(DIR *);
 int            readdir_r(DIR *__restrict, struct dirent *__restrict, struct dirent **__restrict);
+
 void           rewinddir(DIR *);
 int            dirfd(DIR *);
 
@@ -57,18 +78,21 @@ long           telldir(DIR *);
 #define DT_WHT 14
 #define IFTODT(x) ((x)>>12 & 017)
 #define DTTOIF(x) ((x)<<12)
-int getdents(int, struct dirent *, size_t);
+int getdents(int fd, struct dirent *dirp, size_t count);
+int getdents64(int fd, struct dirent64 *dirp, size_t count);
 #endif
 
 #ifdef _GNU_SOURCE
-int versionsort(const struct dirent **, const struct dirent **);
+int versionsort(const struct dirent64 **, const struct dirent64 **);
 #endif
 
 #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
-#define dirent64 dirent
-#define readdir64 readdir
-#define readdir64_r readdir_r
-#define scandir64 scandir
+
+/**
+ * `alphasort()` and `versionsort()` are not provided in the Unikraft core,
+ * so we can just leave the largefile functions as aliases to the non largefile
+ * definitions
+ */
 #define alphasort64 alphasort
 #define versionsort64 versionsort
 #define off64_t off_t
index d33774395e9a1fad015411b3fffe883c07c152bd..9c3a552be66aca68526cdf3110cdd061c8f474db 100644 (file)
@@ -539,7 +539,7 @@ ramfs_rename(struct vnode *dvp1, struct vnode *vp1, const char *name1 __unused,
  * @vp: vnode of the directory.
  */
 static int
-ramfs_readdir(struct vnode *vp, struct vfscore_file *fp, struct dirent *dir)
+ramfs_readdir(struct vnode *vp, struct vfscore_file *fp, struct dirent64 *dir)
 {
        struct ramfs_node *np, *dnp;
        int i;
index b7b115a613d720683014b604773aa4fa88064d24..2d347104fe01242426d95bd4b8d37668c0d9caa1 100644 (file)
@@ -15,6 +15,19 @@ config LIBVFSCORE_PIPE_SIZE_ORDER
        help
                The size of the internal buffer for anonymous pipes is 2^order.
 
+config LIBVFSCORE_NONLARGEFILE
+       bool "Non-largefile system calls"
+       default y if LIBSYSCALL_SHIM_HANDLER
+       default n
+       help
+               Add different dirent and dirent64 structure and their respective
+               syscalls (i.e. treat Linux non largefile legacy syscalls properly).
+               If this option is not set, the dirent structure will be aliased to
+               dirent64, and all other nonlargefile functions will be aliased to
+               their largefile variant (e.g. readdir = readdir64).
+               If lib/syscall_shim is enabled and this option is not selected, only
+               the 64-bit version of the system calls are registered.
+
 config LIBVFSCORE_AUTOMOUNT_ROOTFS
 bool "Automatically mount a root filesysytem (/)"
 default n
index 43d0e3df1e874334ae955896d59a74fcbdc46c4c..473a72b01ffb3504caed9c7b250d4e1b7d3b93b6 100644 (file)
@@ -76,7 +76,9 @@ UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += symlink-2
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += unlink-1
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += unlinkat-3
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += chroot-1
+ifeq ($(CONFIG_LIBVFSCORE_NONLARGEFILE),y)
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += getdents-3
+endif
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += getdents64-3
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += newfstatat-4
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBVFSCORE) += open-3
index cb81f1c2bbda20fd3d87baedea7dd4c78df12c15..d698912b3ae7d0aca9d877aff6ac5127e584ee13 100644 (file)
@@ -162,7 +162,8 @@ typedef     int (*vnop_seek_t)      (struct vnode *, struct vfscore_file *,
                                 off_t, off_t);
 typedef        int (*vnop_ioctl_t)     (struct vnode *, struct vfscore_file *, unsigned long, void *);
 typedef        int (*vnop_fsync_t)     (struct vnode *, struct vfscore_file *);
-typedef        int (*vnop_readdir_t)   (struct vnode *, struct vfscore_file *, struct dirent *);
+typedef        int (*vnop_readdir_t)   (struct vnode *, struct vfscore_file *,
+                                struct dirent64 *);
 typedef        int (*vnop_lookup_t)    (struct vnode *, const char *, struct vnode **);
 typedef        int (*vnop_create_t)    (struct vnode *, const char *, mode_t);
 typedef        int (*vnop_remove_t)    (struct vnode *, struct vnode *, const char *);
index 5388376bb43570e241a3619a8b1675a006b7455f..2a0d6c264664b3aaae4d108c8f64a766d4a8db98 100644 (file)
@@ -947,6 +947,10 @@ UK_TRACEPOINT(trace_vfs_readdir, "%d %#x", int, struct dirent*);
 UK_TRACEPOINT(trace_vfs_readdir_ret, "");
 UK_TRACEPOINT(trace_vfs_readdir_err, "%d", int);
 
+UK_TRACEPOINT(trace_vfs_readdir64, "%d %p", int, struct dirent64*);
+UK_TRACEPOINT(trace_vfs_readdir_ret64, "");
+UK_TRACEPOINT(trace_vfs_readdir_err64, "%d", int);
+
 struct __dirstream
 {
        int fd;
@@ -1021,6 +1025,7 @@ int closedir(DIR *dir)
        return 0;
 }
 
+#if CONFIG_LIBVFSCORE_NONLARGEFILE
 int scandir(const char *path, struct dirent ***res,
        int (*sel)(const struct dirent *),
        int (*cmp)(const struct dirent **, const struct dirent **))
@@ -1068,8 +1073,77 @@ int scandir(const char *path, struct dirent ***res,
        *res = names;
        return cnt;
 }
+#else /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
+int scandir(const char *path, struct dirent ***res,
+       int (*sel)(const struct dirent *),
+       int (*cmp)(const struct dirent **, const struct dirent **))
+       __attribute__((alias("scandir64")));
+#endif /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
+
+#ifdef scandir64
+#undef scandir64
+#endif
+int scandir64(const char *path, struct dirent64 ***res,
+       int (*sel)(const struct dirent64 *),
+       int (*cmp)(const struct dirent64 **, const struct dirent64 **))
+{
+       struct dirent64 *de, **names = 0, **tmp;
+       size_t cnt = 0, len = 0;
+       int old_errno = errno;
+       DIR *d;
+
+       d = opendir(path);
+       if (!d)
+               return -1;
+
+       de = readdir64(d);
+       while (de != NULL) {
+               if (sel && !sel(de))
+                       continue;
+
+               if (cnt >= len) {
+                       len = 2 * len + 1;
+                       if (len > SIZE_MAX / sizeof(*names))
+                               break;
+
+                       tmp = realloc(names, len * sizeof(*names));
+                       if (!tmp)
+                               break;
+                       names = tmp;
+               }
+
+               names[cnt] = malloc(de->d_reclen);
+               if (unlikely(!names[cnt]))
+                       break;
+
+               memcpy(names[cnt++], de, de->d_reclen);
+
+               de = readdir64(d);
+       }
+
+       closedir(d);
+
+       if (unlikely(errno)) {
+               if (names) {
+                       while (cnt-- > 0)
+                               free(names[cnt]);
+                       free(names);
+               }
+
+               return -1;
+       }
+       errno = old_errno;
+
+       if (cmp)
+               qsort(names, cnt, sizeof(*names),
+                     (int (*)(const void *, const void *))cmp);
+
+       *res = names;
+       return cnt;
+}
 
-UK_TRACEPOINT(trace_vfs_getdents, "%d %#x %hu", int, struct dirent*, size_t);
+#if CONFIG_LIBVFSCORE_NONLARGEFILE
+UK_TRACEPOINT(trace_vfs_getdents, "%d %p %hu", int, struct dirent*, size_t);
 UK_TRACEPOINT(trace_vfs_getdents_ret, "");
 UK_TRACEPOINT(trace_vfs_getdents_err, "%d", int);
 
@@ -1107,6 +1181,7 @@ UK_SYSCALL_R_DEFINE(int, getdents, int, fd, struct dirent*, dirp,
 
        return (i * sizeof(struct dirent));
 }
+#endif /* CONFIG_LIBVFSCORE_NONLARGEFILE */
 
 UK_TRACEPOINT(trace_vfs_getdents64, "%d %p %hu", int, struct dirent64 *, size_t);
 UK_TRACEPOINT(trace_vfs_getdents64_ret, "");
@@ -1146,6 +1221,17 @@ UK_SYSCALL_R_DEFINE(int, getdents64, int, fd, struct dirent64 *, dirp,
        return (i * sizeof(struct dirent64));
 }
 
+/**
+ * If we use an old libc version, that does not use the largefile syscalls as
+ * the default ones, we want to use proper Linux `dirent` and `dirent64`
+ * structures, in order to maintain compatibility.
+ * Since the filesystem `READDIR` vop uses `dirent64`, we will convert to
+ * a `dirent` structure within the `readdir_r` call.
+ *
+ * When this is not the case, we can simply use `#define dirent dirent64` and
+ * alias the `readdir*` functions to their `*64` equivalent.
+ */
+#if CONFIG_LIBVFSCORE_NONLARGEFILE
 struct dirent *readdir(DIR *dir)
 {
        static __thread struct dirent entry;
@@ -1158,39 +1244,95 @@ struct dirent *readdir(DIR *dir)
 
 int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result)
 {
-       int error;
+       /**
+        * `sys_readdir()` will end up calling `VOP_READDIR`, which expects
+        * a dirent64 structure instead of the `struct dirent` entry parameter.
+        */
+       struct dirent64 entry64;
        struct vfscore_file *fp;
+       int error;
+
+       *result = NULL;
 
-       trace_vfs_readdir(dir->fd, entry);
-       error = fget(dir->fd, &fp);
-       if (!error) {
-               error = sys_readdir(fp, entry);
-               fdrop(fp);
-               if (error) {
-                       trace_vfs_readdir_err(error);
-               } else {
-                       trace_vfs_readdir_ret();
-               }
-       }
        // Our dirent has (like Linux) a d_reclen field, but a constant size.
        entry->d_reclen = sizeof(*entry);
 
-       if (error) {
-               *result = NULL;
-       } else {
-               *result = entry;
+       trace_vfs_readdir(dir->fd, entry);
+       error = fget(dir->fd, &fp);
+       if (unlikely(error))
+               return error;
+
+       error = sys_readdir(fp, &entry64);
+       fdrop(fp);
+       if (unlikely(error)) {
+               trace_vfs_readdir_err(error);
+
+               return error == ENOENT ? 0 : error;
        }
-       return error == ENOENT ? 0 : error;
+
+       trace_vfs_readdir_ret();
+
+       entry->d_ino = entry64.d_ino;
+       entry->d_type = entry64.d_type;
+       entry->d_off = entry64.d_off;
+       memcpy(entry->d_name, entry64.d_name, sizeof(entry64.d_name));
+
+       *result = entry;
+
+       return 0;
 }
+#else /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
+struct dirent *readdir(DIR *dir) __attribute__((alias("readdir64")));
+int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result)
+        __attribute__((alias("readdir64_r")));
+#endif /* !CONFIG_LIBVFSCORE_NONLARGEFILE */
 
-// FIXME: in 64bit dirent64 and dirent are identical, so it's safe to alias
+#ifdef readdir64_r
 #undef readdir64_r
+#endif
 int readdir64_r(DIR *dir, struct dirent64 *entry,
                struct dirent64 **result)
-               __attribute__((alias("readdir_r")));
+{
+       struct vfscore_file *fp;
+       int error;
+
+       *result = NULL;
+
+       // Our dirent has (like Linux) a d_reclen field, but a constant size.
+       entry->d_reclen = sizeof(*entry);
 
+       trace_vfs_readdir64(dir->fd, entry);
+       error = fget(dir->fd, &fp);
+       if (unlikely(error))
+               return error;
+
+       error = sys_readdir(fp, entry);
+       fdrop(fp);
+       if (unlikely(error)) {
+               trace_vfs_readdir_err64(error);
+
+               return error == ENOENT ? 0 : error;
+       }
+
+       trace_vfs_readdir_ret64();
+
+       *result = entry;
+
+       return 0;
+}
+
+#ifdef readdir64
 #undef readdir64
-struct dirent *readdir64(DIR *dir) __attribute__((alias("readdir")));
+#endif
+struct dirent64 *readdir64(DIR *dir)
+{
+       static __thread struct dirent64 entry;
+       struct dirent64 *result;
+
+       errno = readdir64_r(dir, &entry, &result);
+
+       return result;
+}
 
 void rewinddir(DIR *dirp)
 {
index 3ad7a170b3e8c3c5676fff2ea9252f65c067fe83..1d35edf4b202ddb77851ddb475334ed8c9304074 100644 (file)
@@ -468,7 +468,7 @@ check_dir_empty(char *path)
 {
        int error;
        struct vfscore_file *fp;
-       struct dirent dir;
+       struct dirent64 dir;
 
        DPRINTF(VFSDB_SYSCALL, ("check_dir_empty\n"));
 
@@ -495,7 +495,7 @@ out_error:
 }
 
 int
-sys_readdir(struct vfscore_file *fp, struct dirent *dir)
+sys_readdir(struct vfscore_file *fp, struct dirent64 *dir)
 {
        struct vnode *dvp;
        int error;
index 13cbf3498f35d5ecdb78995d8dbea64655ad9967..6dc9a5255ce54ca7dbd7290c4733c12fad932b98 100644 (file)
@@ -225,7 +225,7 @@ int sys_fsync(struct vfscore_file *fp);
  *     - (0):  Completed successfully
  *     - (<0): Negative value with error code
  */
-int sys_readdir(struct vfscore_file *fp, struct dirent *dirent);
+int sys_readdir(struct vfscore_file *fp, struct dirent64 *dirent);
 
 /**
  * Resets the location in the directory stream.