ffat: remove deprecated env usage

Issue #2310.
This commit is contained in:
Josef Söntgen 2017-03-07 15:16:15 +01:00 committed by Christian Helmuth
parent 078f28238f
commit a34fb617a7
11 changed files with 233 additions and 137 deletions

View File

@ -0,0 +1,26 @@
/*
* \brief Block session initialize function
* \author Josef Soentgen
* \date 2017-03-07
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__FFAT__BLOCK_H_
#define _INCLUDE__FFAT__BLOCK_H_
namespace Genode {
struct Env;
struct Allocator;
}
namespace Ffat {
void block_init(Genode::Env &, Genode::Allocator &heap);
}
#endif /* _INCLUDE__FFAT__BLOCK_H_ */

View File

@ -5,10 +5,11 @@
#
set use_sd_card_drv [expr [have_spec omap4] || [have_spec arndale] || [have_spec pl180]]
set use_ahci_drv [have_spec x86]
set use_ahci_drv [expr [have_spec x86] && ![have_spec linux]]
set use_ram_blk [have_spec linux]
set mkfs [check_installed mkfs.vfat]
if {[expr [have_spec linux] || [have_spec odroid_xu]]} {
if {[have_spec odroid_xu]} {
puts "Run script does not support this platform"; exit }
#
@ -24,6 +25,7 @@ set build_components {
lappend_if $use_ahci_drv build_components drivers/ahci
lappend_if $use_sd_card_drv build_components drivers/sd_card
lappend_if $use_ram_blk build_components server/ram_blk
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
@ -63,12 +65,11 @@ set config {
<start name="test-libc_vfs">
<resource name="RAM" quantum="2M"/>
<config>
<libc stdout="/dev/log">
<vfs>
<dir name="dev"> <log/> </dir>
<fs/>
</vfs>
</libc>
<libc stdout="/dev/log"/>
<vfs>
<dir name="dev"> <log/> </dir>
<fs/>
</vfs>
</config>
</start>}
@ -76,7 +77,7 @@ append_platform_drv_config
append_if $use_ahci_drv config {
<start name="ahci_drv">
<resource name="RAM" quantum="1M"/>
<resource name="RAM" quantum="2M"/>
<provides> <service name="Block"/> </provides>
<config>
<policy label_prefix="ffat_fs" device="0" />
@ -91,6 +92,13 @@ append_if $use_sd_card_drv config {
</start>
}
append_if $use_ram_blk config {
<start name="ram_blk">
<resource name="RAM" quantum="128M" />
<provides><service name="Block"/></provides>
<config file="test.hda" block_size="512"/>
</start>}
append config {
</config>
}
@ -110,6 +118,8 @@ set boot_modules {
lappend_if $use_ahci_drv boot_modules ahci_drv
lappend_if $use_sd_card_drv boot_modules sd_card_drv
lappend_if $use_ram_blk boot_modules ram_blk
lappend_if $use_ram_blk boot_modules test.hda
append_platform_drv_boot_modules

View File

@ -13,15 +13,15 @@ if {[have_spec odroid_xu] || [have_spec zynq]} {
set use_sd_card_drv [expr [have_spec omap4] || [have_spec arndale] || [have_spec pl180]]
# use AHCI on x86
set use_ahci [have_spec x86]
set use_ahci [expr [have_spec x86] && ![have_spec linux]]
# use ram_blk on Linux
set use_ram_blk [have_spec linux]
if {[catch { exec which $mkfs_cmd } ]} {
puts stderr "Error: $mkfs_cmd not installed, aborting test"; exit }
if {[have_spec linux]} {
puts "Run script does not support this platform"; exit }
if {![have_include "power_on/qemu"]} {
if {[expr ![have_include "power_on/qemu"] && !$use_ram_blk]} {
puts "\nPlease setup your native sd or hard drive. Remove this fail stop";
puts "check when you have prepared your native environment.\n";
exit 0
@ -41,6 +41,7 @@ lappend build_components test/libc_$filesystem
lappend_if $use_ahci build_components drivers/ahci
lappend_if $use_sd_card_drv build_components drivers/sd_card
lappend_if $use_ram_blk build_components server/ram_blk
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
@ -82,15 +83,14 @@ append config {
append_if [have_include "power_on/qemu"] config {
<write-read size="1M" buffer_size="8K"/>}
append config {
<libc stdout="/dev/log" stderr="/dev/log">
<vfs>
<dir name="dev">}
<libc stdout="/dev/log" stderr="/dev/log"/>
<vfs>
<dir name="dev">}
append config $libc_dev_blkdev
append config {
<log/>
</dir>
</vfs>
</libc>
<log/>
</dir>
</vfs>
</config>
</start>}
@ -117,6 +117,13 @@ append_if $use_sd_card_drv config {
</start>
}
append_if $use_ram_blk config {
<start name="ram_blk">
<resource name="RAM" quantum="128M" />
<provides><service name="Block"/></provides>
<config file="test.hda" block_size="512"/>
</start>}
append config {
</config>
}
@ -139,6 +146,8 @@ append boot_modules libc_$filesystem.lib.so
lappend_if $use_ahci boot_modules ahci_drv
lappend_if $use_sd_card_drv boot_modules sd_card_drv
lappend_if $use_ram_blk boot_modules ram_blk
lappend_if $use_ram_blk boot_modules test.hda
append_platform_drv_boot_modules

View File

@ -16,20 +16,35 @@
#include <base/log.h>
#include <block_session/connection.h>
/* Genode block backend */
#include <ffat/block.h>
/* Ffat includes */
extern "C" {
#include <ffat/diskio.h>
}
namespace Ffat {
void block_init(Genode::Env &, Genode::Allocator &alloc);
}
using namespace Genode;
static bool const verbose = false;
static Genode::Allocator_avl _block_alloc(Genode::env()->heap());
static Block::Connection *_block_connection;
static Constructible<Genode::Allocator_avl> _block_alloc;
static Constructible<Block::Connection> _block_connection;
static size_t _blk_size = 0;
static Block::sector_t _blk_cnt = 0;
static Block::Session::Tx::Source *_source;
static Genode::Env *_global_env;
void Ffat::block_init(Genode::Env &env, Genode::Allocator &alloc)
{
_global_env = &env;
_block_alloc.construct(&alloc);
}
extern "C" DSTATUS disk_initialize (BYTE drv)
@ -50,7 +65,7 @@ extern "C" DSTATUS disk_initialize (BYTE drv)
}
try {
_block_connection = new (Genode::env()->heap()) Block::Connection(&_block_alloc);
_block_connection.construct(*_global_env, &*_block_alloc);
} catch(...) {
Genode::error("could not open block connection");
return STA_NOINIT;
@ -64,7 +79,7 @@ extern "C" DSTATUS disk_initialize (BYTE drv)
/* check for read- and write-capability */
if (!ops.supported(Block::Packet_descriptor::READ)) {
Genode::error("block device not readable!");
destroy(env()->heap(), _block_connection);
_block_connection.destruct();
return STA_NOINIT;
}
if (!ops.supported(Block::Packet_descriptor::WRITE)) {

View File

@ -13,6 +13,7 @@
/* Genode includes */
#include <base/env.h>
#include <base/heap.h>
#include <base/log.h>
#include <os/path.h>
@ -28,6 +29,9 @@
#include <libc-plugin/plugin.h>
#include <libc-plugin/fd_alloc.h>
/* Genode block backend */
#include <ffat/block.h>
/* ffat includes */
namespace Ffat { extern "C" {
#include <ffat/ff.h>
@ -138,6 +142,8 @@ class Plugin : public Libc::Plugin
{
private:
Genode::Constructible<Genode::Heap> _heap;
Ffat::FATFS _fatfs;
Ffat::FIL *_get_ffat_file(Libc::File_descriptor *fd)
@ -170,8 +176,20 @@ class Plugin : public Libc::Plugin
/**
* Constructor
*/
Plugin() : Libc::Plugin(PLUGIN_PRIORITY)
Plugin() : Libc::Plugin(PLUGIN_PRIORITY) { }
~Plugin()
{
/* unmount the file system */
Ffat::f_mount(0, NULL);
}
void init(Genode::Env &env) override
{
_heap.construct(env.ram(), env.rm());
Ffat::block_init(env, *_heap);
/* mount the file system */
if (verbose)
Genode::log(__func__, ": mounting device ...");
@ -181,12 +199,6 @@ class Plugin : public Libc::Plugin
}
}
~Plugin()
{
/* unmount the file system */
Ffat::f_mount(0, NULL);
}
/*
* TODO: decide if the file named <path> shall be handled by this plugin
*/
@ -251,14 +263,14 @@ class Plugin : public Libc::Plugin
if (!ffat_file){
/* directory */
Genode::destroy(Genode::env()->heap(), context(fd));
Genode::destroy(&*_heap, context(fd));
Libc::file_descriptor_allocator()->free(fd);
return 0;
}
FRESULT res = f_close(ffat_file);
Genode::destroy(Genode::env()->heap(), context(fd));
Genode::destroy(&*_heap, context(fd));
Libc::file_descriptor_allocator()->free(fd);
switch(res) {
@ -513,7 +525,7 @@ class Plugin : public Libc::Plugin
switch(res) {
case FR_OK: {
Plugin_context *context = new (Genode::env()->heap())
Plugin_context *context = new (&*_heap)
File_plugin_context(pathname, ffat_file);
context->status_flags(flags);
Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->alloc(this, context);
@ -533,7 +545,7 @@ class Plugin : public Libc::Plugin
Genode::log(__func__, ": opendir res=", (int)f_opendir_res);
switch(f_opendir_res) {
case FR_OK: {
Plugin_context *context = new (Genode::env()->heap())
Plugin_context *context = new (&*_heap)
Directory_plugin_context(pathname, ffat_dir);
context->status_flags(flags);
Libc::File_descriptor *f =

View File

@ -12,12 +12,11 @@
*/
/* Genode includes */
#include <base/component.h>
#include <file_system/node_handle_registry.h>
#include <file_system_session/rpc_object.h>
#include <root/component.h>
#include <cap_session/connection.h>
#include <base/attached_rom_dataspace.h>
#include <os/config.h>
#include <os/session_policy.h>
#include <util/xml_node.h>
#include <base/heap.h>
@ -28,6 +27,9 @@
#include <file.h>
#include <util.h>
/* Genode block backend */
#include <ffat/block.h>
/* ffat includes */
namespace Ffat { extern "C" {
#include <ffat/ff.h>
@ -57,12 +59,14 @@ namespace File_system {
{
private:
Genode::Env &_env;
Genode::Allocator &_heap;
Directory &_root;
Node_handle_registry _handle_registry;
bool _writable;
Signal_dispatcher<Session_component> _process_packet_dispatcher;
Signal_handler<Session_component> _process_packet_dispatcher;
/******************************
** Packet-stream processing **
@ -132,7 +136,7 @@ namespace File_system {
* Called by signal dispatcher, executed in the context of the main
* thread (not serialized with the RPC functions)
*/
void _process_packets(unsigned)
void _process_packets()
{
while (tx_sink()->packet_avail()) {
@ -171,15 +175,16 @@ namespace File_system {
/**
* Constructor
*/
Session_component(size_t tx_buf_size, Rpc_entrypoint &ep,
Region_map &rm,
Signal_receiver &sig_rec,
Directory &root, bool writable)
Session_component(Genode::Env &env,
Genode::Allocator &heap,
size_t tx_buf_size,
Directory &root,
bool writable)
:
Session_rpc_object(env()->ram_session()->alloc(tx_buf_size), rm, ep),
_root(root),
_writable(writable),
_process_packet_dispatcher(sig_rec, *this,
Session_rpc_object(env.ram().alloc(tx_buf_size),
env.rm(), env.ep().rpc_ep()),
_env(env), _heap(heap), _root(root), _writable(writable),
_process_packet_dispatcher(env.ep(), *this,
&Session_component::_process_packets)
{
/*
@ -196,10 +201,9 @@ namespace File_system {
~Session_component()
{
Dataspace_capability ds = tx_sink()->dataspace();
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds));
_env.ram().free(static_cap_cast<Ram_dataspace>(ds));
}
/***************************
** File_system interface **
***************************/
@ -244,7 +248,7 @@ namespace File_system {
switch(res) {
case FR_OK: {
File *file_node = new (env()->heap()) File(absolute_path.base());
File *file_node = new (&_heap) File(absolute_path.base());
file_node->ffat_fil(ffat_fil);
return _handle_registry.alloc(file_node);
}
@ -300,7 +304,7 @@ namespace File_system {
* The 'Directory' constructor removes trailing slashes,
* except for "/"
*/
Directory *dir_node = new (env()->heap()) Directory(path.string());
Directory *dir_node = new (&_heap) Directory(path.string());
using namespace Ffat;
@ -356,7 +360,7 @@ namespace File_system {
throw Lookup_failed();
}
} catch (Exception e) {
destroy(env()->heap(), dir_node);
destroy(&_heap, dir_node);
throw e;
}
}
@ -396,7 +400,7 @@ namespace File_system {
throw Lookup_failed();
}
} catch (Exception e) {
destroy(env()->heap(), dir_node);
destroy(&_heap, dir_node);
throw e;
}
}
@ -417,7 +421,7 @@ namespace File_system {
throw Lookup_failed();
}
Node *node = new (env()->heap()) Node(absolute_path.base());
Node *node = new (&_heap) Node(absolute_path.base());
/* f_stat() does not work for "/" */
if (!is_root(node->name())) {
@ -462,7 +466,7 @@ namespace File_system {
throw Lookup_failed();
}
} catch (Exception e) {
destroy(env()->heap(), node);
destroy(&_heap, node);
throw e;
}
}
@ -493,7 +497,7 @@ namespace File_system {
FRESULT res = f_close(file->ffat_fil());
/* free the node */
destroy(env()->heap(), file);
destroy(&_heap, file);
switch(res) {
case FR_OK:
@ -825,10 +829,11 @@ namespace File_system {
{
private:
Rpc_entrypoint &_channel_ep;
Region_map &_rm;
Signal_receiver &_sig_rec;
Directory &_root_dir;
Genode::Env &_env;
Genode::Allocator &_md_alloc;
Genode::Allocator &_heap;
Directory &_root_dir;
protected:
@ -901,7 +906,7 @@ namespace File_system {
throw Root::Unavailable();
}
session_root_dir = new (env()->heap()) Directory(root);
session_root_dir = new (&_md_alloc) Directory(root);
}
} catch (Xml_node::Nonexistent_attribute) {
error("missing \"root\" attribute in policy definition");
@ -942,7 +947,7 @@ namespace File_system {
throw Root::Quota_exceeded();
}
return new (md_alloc())
Session_component(tx_buf_size, _channel_ep, _rm, _sig_rec,
Session_component(_env, _heap, tx_buf_size,
*session_root_dir, writeable);
}
@ -951,52 +956,52 @@ namespace File_system {
/**
* Constructor
*
* \param session_ep session entrypoint
* \param sig_rec signal receiver used for handling the
* data-flow signals of packet streams
* \param md_alloc meta-data allocator
* \param env reference to Genode environment
* \param heap meta-data allocator
* \param root normal root directory if root in policy starts
* at root
*/
Root(Rpc_entrypoint &session_ep, Allocator &md_alloc,
Region_map &rm,
Signal_receiver &sig_rec, Directory &root_dir)
Root(Genode::Env &env, Allocator &md_alloc, Genode::Allocator &heap,
Directory &root)
:
Root_component<Session_component>(&session_ep, &md_alloc),
_channel_ep(session_ep), _rm(rm), _sig_rec(sig_rec),
_root_dir(root_dir)
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _md_alloc(md_alloc), _heap(heap), _root_dir(root)
{ }
};
};
int main(int, char **)
struct Main
{
using namespace File_system;
using namespace Ffat;
Genode::Env &_env;
Genode::Heap _heap { _env.ram(), _env.rm() };
Genode::Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
static Ffat::FATFS _fatfs;
File_system::Directory _root_dir { "/" };
File_system::Root _root { _env, _sliced_heap, _heap, _root_dir };
/* mount the file system */
if (f_mount(0, &_fatfs) != Ffat::FR_OK) {
error("mount failed");
return -1;
Ffat::FATFS _fatfs;
Main(Genode::Env &env) : _env(env)
{
Ffat::block_init(_env, _heap);
using namespace File_system;
using namespace Ffat;
/* mount the file system */
if (f_mount(0, &_fatfs) != Ffat::FR_OK) {
error("mount failed");
struct Mount_failed : Genode::Exception { };
throw Mount_failed();
}
Genode::log("--- Starting Ffat_fs ---");
_env.parent().announce(_env.ep().manage(_root));
}
};
enum { STACK_SIZE = 3*sizeof(addr_t)*1024 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "ffat_fs_ep");
static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
static Signal_receiver sig_rec;
static Directory root_dir("/");
static File_system::Root root(ep, sliced_heap, *env()->rm_session(), sig_rec, root_dir);
env()->parent()->announce(ep.manage(&root));
for (;;) {
Signal s = sig_rec.wait_for_signal();
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
}
return 0;
}
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@ -1,4 +1,4 @@
TARGET = ffat_fs
SRC_CC = main.cc
LIBS = base ffat_block config
LIBS = base ffat_block
INC_DIR += $(PRG_DIR)

View File

@ -13,7 +13,8 @@
*/
/* Genode includes */
#include <os/config.h>
#include <base/attached_rom_dataspace.h>
#include <libc/component.h>
/* libc includes */
#include <dirent.h>
@ -27,14 +28,27 @@
#include <errno.h>
static void test_write_read()
struct Test_failed : Genode::Exception { };
#define CALL_AND_CHECK(ret, operation, condition, info_string, ...) \
printf("calling " #operation " " info_string "\n", ##__VA_ARGS__); \
ret = operation; \
if (condition) { \
printf(#operation " succeeded\n"); \
} else { \
printf(#operation " failed, " #ret "=%ld, errno=%d\n", (long)ret, errno); \
throw Test_failed(); \
}
static void test_write_read(Genode::Xml_node node)
{
size_t rounds = 4;
size_t size = 4*1024*1024;
size_t buffer_size = 32*1024;
try {
Genode::Xml_node config = Genode::config()->xml_node().sub_node("write-read");
Genode::Xml_node config = node.sub_node("write-read");
try { config.attribute("rounds").value(&rounds); } catch (...) { }
@ -78,18 +92,7 @@ static void test_write_read()
}
#define CALL_AND_CHECK(ret, operation, condition, info_string, ...) \
printf("calling " #operation " " info_string "\n", ##__VA_ARGS__); \
ret = operation; \
if (condition) { \
printf(#operation " succeeded\n"); \
} else { \
printf(#operation " failed, " #ret "=%ld, errno=%d\n", (long)ret, errno); \
return -1; \
}
int main(int argc, char *argv[])
static void test(Genode::Xml_node node)
{
int ret, fd;
ssize_t count;
@ -108,7 +111,7 @@ int main(int argc, char *argv[])
unsigned int iterations = 1;
try {
Genode::config()->xml_node().sub_node("iterations").attribute("value").value(&iterations);
node.sub_node("iterations").attribute("value").value(&iterations);
} catch(...) { }
for (unsigned int i = 0; i < iterations; i++) {
@ -148,7 +151,7 @@ int main(int argc, char *argv[])
printf("content of file: \"%s\"\n", buf);
if (strcmp(buf, pattern) != 0) {
printf("unexpected content of file\n");
return -1;
throw Test_failed();
} else {
printf("file content is correct\n");
}
@ -176,7 +179,7 @@ int main(int argc, char *argv[])
printf("content of file: \"%s\"\n", buf);
if (strcmp(buf, &pattern[2]) != 0) {
printf("unexpected content of file\n");
return -1;
throw Test_failed();
} else {
printf("file content is correct\n");
}
@ -205,7 +208,7 @@ int main(int argc, char *argv[])
printf("content of buffer: \"%s\"\n", buf);
if (strcmp(buf, pattern) != 0) {
printf("unexpected content of file\n");
return -1;
throw Test_failed();
} else {
printf("file content is correct\n");
}
@ -234,21 +237,21 @@ int main(int argc, char *argv[])
CALL_AND_CHECK(ret, ftruncate(fd, 100), ret == 0, ""); /* increase size */
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(ret, stat(file_name4, &stat_buf),
(ret == 0) && (stat_buf.st_size == 100),
"file_name=%s", file_name4);
(ret == 0) && (stat_buf.st_size == 100),
"file_name=%s", file_name4);
CALL_AND_CHECK(fd, open(file_name4, O_WRONLY), fd >= 0, "file_name=%s", file_name4);
CALL_AND_CHECK(ret, ftruncate(fd, 10), ret == 0, ""); /* decrease size */
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(ret, stat(file_name4, &stat_buf),
(ret == 0) && (stat_buf.st_size == 10),
"file_name=%s", file_name4);
(ret == 0) && (stat_buf.st_size == 10),
"file_name=%s", file_name4);
/* test 'O_TRUNC' flag */
CALL_AND_CHECK(fd, open(file_name4, O_WRONLY | O_TRUNC), fd >= 0, "file_name=%s", file_name4);
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(ret, stat(file_name4, &stat_buf),
(ret == 0) && (stat_buf.st_size == 0),
"file_name=%s", file_name4);
(ret == 0) && (stat_buf.st_size == 0),
"file_name=%s", file_name4);
/* test 'fchdir()' */
CALL_AND_CHECK(fd, open(dir_name, O_RDONLY), fd >= 0, "dir_name=%s", dir_name);
@ -268,7 +271,7 @@ int main(int argc, char *argv[])
CALL_AND_CHECK(ret, mkdir("a", 0777), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "a");
CALL_AND_CHECK(ret, mkdir("c", 0777), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "c");
CALL_AND_CHECK(ret, symlink("../a", "c/d"),
((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/c/d");
((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/c/d");
CALL_AND_CHECK(ret, symlink("c", "e"), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "e");
CALL_AND_CHECK(fd, open("a/b", O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", "a/b");
@ -281,7 +284,7 @@ int main(int argc, char *argv[])
printf("content of file: \"%s\"\n", buf);
if (strcmp(buf, pattern) != 0) {
printf("unexpected content of file\n");
return -1;
throw Test_failed();
} else {
printf("file content is correct\n");
}
@ -294,10 +297,26 @@ int main(int argc, char *argv[])
if (i < (iterations - 1))
sleep(2);
}
test_write_read();
printf("test finished\n");
return 0;
}
struct Main
{
Main(Genode::Env &env)
{
Genode::Attached_rom_dataspace config_rom { env, "config" };
Libc::with_libc([&] () {
test(config_rom.xml());
test_write_read(config_rom.xml());
printf("test finished\n");
});
env.parent().exit(0);
}
};
void Libc::Component::construct(Libc::Env &env) { static Main main(env); }

View File

@ -1,3 +1,3 @@
TARGET = test-libc_ffat
LIBS = posix libc_ffat
LIBS = libc libc_ffat
SRC_CC = main.cc

View File

@ -1,5 +1,5 @@
TARGET = test-libc_vfs
LIBS = posix
LIBS = libc
SRC_CC = main.cc
# we re-use the libc_ffat test

View File

@ -1,5 +1,5 @@
TARGET = test-libc_noux
LIBS = posix libc_noux
LIBS = libc libc_noux
SRC_CC = main.cc
# we re-use the libc_ffat test