Add statfs and fstatfs syscall
This commit is contained in:
		
							parent
							
								
									f2968799ae
								
							
						
					
					
						commit
						789b57c6f7
					
				
							
								
								
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
Subproject commit 633060491f1e2a5c61cd331520c83d588eb5926b
 | 
			
		||||
Subproject commit 6df33e6e415be7b17d6d46babf74ef15c044fba9
 | 
			
		||||
@ -95,6 +95,10 @@ pub trait File: Debug + Sync + Send + Any {
 | 
			
		||||
        return_op_unsupported_error!("fallocate")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fs(&self) -> Result<Arc<dyn FileSystem>> {
 | 
			
		||||
        return_op_unsupported_error!("fs")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: remove this function after all users of this code are removed
 | 
			
		||||
    fn poll(&self) -> Result<(crate::net::PollEventFlags)> {
 | 
			
		||||
        return_op_unsupported_error!("poll")
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,11 @@ use super::*;
 | 
			
		||||
pub use self::chdir::do_chdir;
 | 
			
		||||
pub use self::getcwd::do_getcwd;
 | 
			
		||||
pub use self::mount::do_mount_rootfs;
 | 
			
		||||
pub use self::statfs::{do_fstatfs, do_statfs, Statfs};
 | 
			
		||||
pub use self::sync::do_sync;
 | 
			
		||||
 | 
			
		||||
mod chdir;
 | 
			
		||||
mod getcwd;
 | 
			
		||||
mod mount;
 | 
			
		||||
mod statfs;
 | 
			
		||||
mod sync;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										80
									
								
								src/libos/src/fs/fs_ops/statfs.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										80
									
								
								src/libos/src/fs/fs_ops/statfs.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
use super::*;
 | 
			
		||||
use rcore_fs::vfs::FsInfo;
 | 
			
		||||
 | 
			
		||||
pub fn do_fstatfs(fd: FileDesc) -> Result<Statfs> {
 | 
			
		||||
    debug!("fstatfs: fd: {}", fd);
 | 
			
		||||
 | 
			
		||||
    let file_ref = current!().file(fd)?;
 | 
			
		||||
    let statfs = Statfs::from(file_ref.fs()?.info());
 | 
			
		||||
    trace!("fstatfs result: {:?}", statfs);
 | 
			
		||||
    Ok(statfs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn do_statfs(path: &str) -> Result<Statfs> {
 | 
			
		||||
    debug!("statfs: path: {:?}", path);
 | 
			
		||||
 | 
			
		||||
    let inode = {
 | 
			
		||||
        let current = current!();
 | 
			
		||||
        let fs = current.fs().read().unwrap();
 | 
			
		||||
        fs.lookup_inode(path)?
 | 
			
		||||
    };
 | 
			
		||||
    let statfs = Statfs::from(inode.fs().info());
 | 
			
		||||
    trace!("statfs result: {:?}", statfs);
 | 
			
		||||
    Ok(statfs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct Statfs {
 | 
			
		||||
    /// Type of filesystem
 | 
			
		||||
    f_type: usize,
 | 
			
		||||
    /// Optimal transfer block size
 | 
			
		||||
    f_bsize: usize,
 | 
			
		||||
    /// Total data blocks in filesystem
 | 
			
		||||
    f_blocks: usize,
 | 
			
		||||
    /// Free blocks in filesystem
 | 
			
		||||
    f_bfree: usize,
 | 
			
		||||
    /// Free blocks available to unprivileged user
 | 
			
		||||
    f_bavail: usize,
 | 
			
		||||
    /// Total inodes in filesystem
 | 
			
		||||
    f_files: usize,
 | 
			
		||||
    /// Free inodes in filesystem
 | 
			
		||||
    f_ffree: usize,
 | 
			
		||||
    /// Filesystem ID
 | 
			
		||||
    f_fsid: [i32; 2],
 | 
			
		||||
    /// Maximum length of filenames
 | 
			
		||||
    f_namelen: usize,
 | 
			
		||||
    /// Fragment size
 | 
			
		||||
    f_frsize: usize,
 | 
			
		||||
    /// Mount flags of filesystem
 | 
			
		||||
    f_flags: usize,
 | 
			
		||||
    /// Padding bytes reserved for future use
 | 
			
		||||
    f_spare: [usize; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<FsInfo> for Statfs {
 | 
			
		||||
    fn from(info: FsInfo) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            f_type: match info.magic {
 | 
			
		||||
                // The "/dev" and "/dev/shm" are tmpfs on Linux, so we transform the
 | 
			
		||||
                // magic number to TMPFS_MAGIC.
 | 
			
		||||
                rcore_fs_ramfs::RAMFS_MAGIC | rcore_fs_devfs::DEVFS_MAGIC => {
 | 
			
		||||
                    const TMPFS_MAGIC: usize = 0x0102_1994;
 | 
			
		||||
                    TMPFS_MAGIC
 | 
			
		||||
                }
 | 
			
		||||
                val => val,
 | 
			
		||||
            },
 | 
			
		||||
            f_bsize: info.bsize,
 | 
			
		||||
            f_blocks: info.blocks,
 | 
			
		||||
            f_bfree: info.bfree,
 | 
			
		||||
            f_bavail: info.bavail,
 | 
			
		||||
            f_files: info.files,
 | 
			
		||||
            f_ffree: info.ffree,
 | 
			
		||||
            f_fsid: [0i32; 2],
 | 
			
		||||
            f_namelen: info.namemax,
 | 
			
		||||
            f_frsize: info.frsize,
 | 
			
		||||
            f_flags: 0,
 | 
			
		||||
            f_spare: [0usize; 4],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -36,7 +36,8 @@ impl FileSystem for HostFS {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn info(&self) -> FsInfo {
 | 
			
		||||
        unimplemented!()
 | 
			
		||||
        warn!("HostFS: FsInfo is unimplemented");
 | 
			
		||||
        Default::default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -221,6 +221,10 @@ impl File for INodeFile {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fs(&self) -> Result<Arc<dyn FileSystem>> {
 | 
			
		||||
        Ok(self.inode.fs())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_any(&self) -> &dyn Any {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ pub use self::file_ops::{
 | 
			
		||||
    IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum,
 | 
			
		||||
};
 | 
			
		||||
pub use self::file_table::{FileDesc, FileTable, FileTableEvent, FileTableNotifier};
 | 
			
		||||
pub use self::fs_ops::Statfs;
 | 
			
		||||
pub use self::fs_view::FsView;
 | 
			
		||||
pub use self::host_fd::HostFd;
 | 
			
		||||
pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile};
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,9 @@ mod pid_inode;
 | 
			
		||||
mod proc_inode;
 | 
			
		||||
mod self_inode;
 | 
			
		||||
 | 
			
		||||
// Same with the procfs on Linux
 | 
			
		||||
const PROC_SUPER_MAGIC: usize = 0x9fa0;
 | 
			
		||||
 | 
			
		||||
/// Proc file system
 | 
			
		||||
pub struct ProcFS {
 | 
			
		||||
    root: Arc<Dir<LockedProcRootINode>>,
 | 
			
		||||
@ -33,14 +36,15 @@ impl FileSystem for ProcFS {
 | 
			
		||||
 | 
			
		||||
    fn info(&self) -> vfs::FsInfo {
 | 
			
		||||
        vfs::FsInfo {
 | 
			
		||||
            bsize: 0,
 | 
			
		||||
            frsize: 0,
 | 
			
		||||
            magic: PROC_SUPER_MAGIC,
 | 
			
		||||
            bsize: 4096,
 | 
			
		||||
            frsize: 4096,
 | 
			
		||||
            blocks: 0,
 | 
			
		||||
            bfree: 0,
 | 
			
		||||
            bavail: 0,
 | 
			
		||||
            files: 0,
 | 
			
		||||
            ffree: 0,
 | 
			
		||||
            namemax: 0,
 | 
			
		||||
            namemax: 255,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -553,3 +553,24 @@ pub fn do_fallocate(fd: FileDesc, mode: u32, offset: off_t, len: off_t) -> Resul
 | 
			
		||||
    file_ops::do_fallocate(fd, mode, offset as u64, len as u64)?;
 | 
			
		||||
    Ok(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs) -> Result<isize> {
 | 
			
		||||
    from_user::check_mut_ptr(statfs_buf)?;
 | 
			
		||||
 | 
			
		||||
    let statfs = fs_ops::do_fstatfs(fd)?;
 | 
			
		||||
    unsafe {
 | 
			
		||||
        statfs_buf.write(statfs);
 | 
			
		||||
    }
 | 
			
		||||
    Ok(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn do_statfs(path: *const i8, statfs_buf: *mut Statfs) -> Result<isize> {
 | 
			
		||||
    let path = from_user::clone_cstring_safely(path)?
 | 
			
		||||
        .to_string_lossy()
 | 
			
		||||
        .into_owned();
 | 
			
		||||
    let statfs = fs_ops::do_statfs(&path)?;
 | 
			
		||||
    unsafe {
 | 
			
		||||
        statfs_buf.write(statfs);
 | 
			
		||||
    }
 | 
			
		||||
    Ok(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,12 +24,12 @@ use crate::exception::do_handle_exception;
 | 
			
		||||
use crate::fs::{
 | 
			
		||||
    do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd,
 | 
			
		||||
    do_eventfd2, do_faccessat, do_fallocate, do_fchmod, do_fchmodat, do_fchown, do_fchownat,
 | 
			
		||||
    do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getcwd, do_getdents,
 | 
			
		||||
    do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir,
 | 
			
		||||
    do_mkdirat, do_mount_rootfs, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite,
 | 
			
		||||
    do_read, do_readlink, do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir, do_sendfile,
 | 
			
		||||
    do_stat, do_symlink, do_symlinkat, do_sync, do_truncate, do_unlink, do_unlinkat, do_write,
 | 
			
		||||
    do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds, Stat,
 | 
			
		||||
    do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_fstatfs, do_fsync, do_ftruncate, do_getcwd,
 | 
			
		||||
    do_getdents, do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, do_lseek, do_lstat,
 | 
			
		||||
    do_mkdir, do_mkdirat, do_mount_rootfs, do_open, do_openat, do_pipe, do_pipe2, do_pread,
 | 
			
		||||
    do_pwrite, do_read, do_readlink, do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir,
 | 
			
		||||
    do_sendfile, do_stat, do_statfs, do_symlink, do_symlinkat, do_sync, do_truncate, do_unlink,
 | 
			
		||||
    do_unlinkat, do_write, do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds, Stat, Statfs,
 | 
			
		||||
};
 | 
			
		||||
use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t};
 | 
			
		||||
use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t};
 | 
			
		||||
@ -222,8 +222,8 @@ macro_rules! process_syscall_table_with_callback {
 | 
			
		||||
            (Uselib = 134) => handle_unsupported(),
 | 
			
		||||
            (Personality = 135) => handle_unsupported(),
 | 
			
		||||
            (Ustat = 136) => handle_unsupported(),
 | 
			
		||||
            (Statfs = 137) => handle_unsupported(),
 | 
			
		||||
            (Fstatfs = 138) => handle_unsupported(),
 | 
			
		||||
            (Statfs = 137) => do_statfs(path: *const i8, statfs_buf: *mut Statfs),
 | 
			
		||||
            (Fstatfs = 138) => do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs),
 | 
			
		||||
            (SysFs = 139) => handle_unsupported(),
 | 
			
		||||
            (Getpriority = 140) => handle_unsupported(),
 | 
			
		||||
            (Setpriority = 141) => handle_unsupported(),
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,8 @@ TEST_DEPS := client data_sink naughty_child
 | 
			
		||||
TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
 | 
			
		||||
	truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \
 | 
			
		||||
	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
 | 
			
		||||
	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait spawn_attribute
 | 
			
		||||
	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | 
			
		||||
	spawn_attribute statfs
 | 
			
		||||
# Benchmarks: need to be compiled and run by bench-% target
 | 
			
		||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/vfs.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
@ -142,6 +143,22 @@ static int test_read_from_proc_cpuinfo() {
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define PROC_SUPER_MAGIC 0x9fa0
 | 
			
		||||
static int test_statfs() {
 | 
			
		||||
    const char *file_path = "/proc/cpuinfo";
 | 
			
		||||
    struct statfs statfs_buf;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = statfs(file_path, &statfs_buf);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        THROW_ERROR("failed to statfs the file");
 | 
			
		||||
    }
 | 
			
		||||
    if (statfs_buf.f_type != PROC_SUPER_MAGIC) {
 | 
			
		||||
        THROW_ERROR("failed to check the f_type");
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ============================================================================
 | 
			
		||||
// Test suite main
 | 
			
		||||
// ============================================================================
 | 
			
		||||
@ -154,6 +171,7 @@ static test_case_t test_cases[] = {
 | 
			
		||||
    TEST_CASE(test_read_from_proc_self_cmdline),
 | 
			
		||||
    TEST_CASE(test_read_from_proc_meminfo),
 | 
			
		||||
    TEST_CASE(test_read_from_proc_cpuinfo),
 | 
			
		||||
    TEST_CASE(test_statfs),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, const char *argv[]) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								test/statfs/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/statfs/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
include ../test_common.mk
 | 
			
		||||
 | 
			
		||||
EXTRA_C_FLAGS :=
 | 
			
		||||
EXTRA_LINK_FLAGS :=
 | 
			
		||||
BIN_ARGS :=
 | 
			
		||||
							
								
								
									
										117
									
								
								test/statfs/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										117
									
								
								test/statfs/main.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
			
		||||
#include <sys/vfs.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include "test_fs.h"
 | 
			
		||||
 | 
			
		||||
// ============================================================================
 | 
			
		||||
// Helper function
 | 
			
		||||
// ============================================================================
 | 
			
		||||
 | 
			
		||||
static int create_file(const char *file_path) {
 | 
			
		||||
    int fd;
 | 
			
		||||
    int flags = O_RDONLY | O_CREAT | O_TRUNC;
 | 
			
		||||
    int mode = 00666;
 | 
			
		||||
 | 
			
		||||
    fd = open(file_path, flags, mode);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        THROW_ERROR("failed to create a file");
 | 
			
		||||
    }
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int remove_file(const char *file_path) {
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = unlink(file_path);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        THROW_ERROR("failed to unlink the created file");
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ============================================================================
 | 
			
		||||
// Test cases for statfs
 | 
			
		||||
// ============================================================================
 | 
			
		||||
 | 
			
		||||
static int __test_statfs(const char *file_path, unsigned long expected_type) {
 | 
			
		||||
    struct statfs statfs_buf;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = statfs(file_path, &statfs_buf);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        THROW_ERROR("failed to statfs the file");
 | 
			
		||||
    }
 | 
			
		||||
    if (statfs_buf.f_type != expected_type) {
 | 
			
		||||
        THROW_ERROR("failed to check the f_type");
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __test_fstatfs(const char *file_path, unsigned long expected_type) {
 | 
			
		||||
    struct statfs statfs_buf;
 | 
			
		||||
    int fd, ret;
 | 
			
		||||
    int flags = O_RDONLY;
 | 
			
		||||
 | 
			
		||||
    fd = open(file_path, flags);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        THROW_ERROR("failed to open file");
 | 
			
		||||
    }
 | 
			
		||||
    ret = fstatfs(fd, &statfs_buf);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        THROW_ERROR("failed to fstatfs the file");
 | 
			
		||||
    }
 | 
			
		||||
    if (statfs_buf.f_type != expected_type) {
 | 
			
		||||
        THROW_ERROR("failed to check the f_type");
 | 
			
		||||
    }
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef int(*test_statfs_func_t)(const char *, unsigned long);
 | 
			
		||||
 | 
			
		||||
static int test_statfs_framework(test_statfs_func_t fn, const char *file_path,
 | 
			
		||||
                                 unsigned long expected_type) {
 | 
			
		||||
    if (create_file(file_path) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (fn(file_path, expected_type) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (remove_file(file_path) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define UNIONFS_MAGIC  0x2f8dbe2f
 | 
			
		||||
#define TMPFS_MAGIC    0x01021994
 | 
			
		||||
 | 
			
		||||
static int test_statfs_on_root() {
 | 
			
		||||
    const char *file_path = "/root/test_fs_statfs.txt";
 | 
			
		||||
    unsigned long expected_type = UNIONFS_MAGIC;
 | 
			
		||||
    return test_statfs_framework(__test_statfs, file_path, expected_type) +
 | 
			
		||||
           test_statfs_framework(__test_fstatfs, file_path, expected_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int test_statfs_on_dev_shm() {
 | 
			
		||||
    const char *file_path = "/dev/shm/test_fs_statfs.txt";
 | 
			
		||||
    unsigned long expected_type = TMPFS_MAGIC;
 | 
			
		||||
    return test_statfs_framework(__test_statfs, file_path, expected_type) +
 | 
			
		||||
           test_statfs_framework(__test_fstatfs, file_path, expected_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ============================================================================
 | 
			
		||||
// Test suite main
 | 
			
		||||
// ============================================================================
 | 
			
		||||
 | 
			
		||||
static test_case_t test_cases[] = {
 | 
			
		||||
    TEST_CASE(test_statfs_on_root),
 | 
			
		||||
    TEST_CASE(test_statfs_on_dev_shm),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, const char *argv[]) {
 | 
			
		||||
    return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user