fs-mutations.dhover×292all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_fs_mutations"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — asynchronous filesystem mutation ops (Linux 5.11 / 5.15).
 *
 * Before 5.11, path-based metadata operations (create, rename, unlink, …) had
 * no `io_uring` opcode and had to run on a worker thread or out-of-band. Linux
 * 5.11 added `RENAMEAT` and `UNLINKAT`; 5.15 rounded out the set with `MKDIRAT`,
 * `SYMLINKAT`, and `LINKAT`, so a whole directory-mutation workflow can be driven
 * through the ring.
 *
 * This example builds a small workflow inside a fresh `mkdtemp` scratch directory,
 * issuing every step as an `io_uring` SQE rather than a blocking libc syscall:
 *   1. `MKDIRAT`   — create a `sub/` subdirectory.
 *   2. (libc)      — create a regular file `sub/file` (so there is something to act on).
 *   3. `SYMLINKAT` — make `link` point at `sub/file`.
 *   4. `RENAMEAT`  — rename `sub/file` to `sub/renamed`.
 *   5. `UNLINKAT`  — remove `sub/renamed`, then remove `link`.
 *   6. `UNLINKAT`  — remove the now-empty `sub/` (with `AT_REMOVEDIR`).
 * Each op's CQE `res` must be `>= 0`, and we verify the on-disk state with `stat`
 * between the relevant steps. The scratch directory is always cleaned up.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *   § "5.11 — Filesystem mutation ops, SQPOLL without root".
 *
 * Run with: `dub run --single fs-mutations.d`
 *
 * Portability: if the running kernel has no `io_uring`, or lacks any of these
 * filesystem opcodes (probe / `-EINVAL`), the program prints a `SKIP:` line and
 * exits 0 so it stays green in CI regardless of the host kernel.
 */
module 
(module) io_uring_fs_mutations

io_uring — asynchronous filesystem mutation ops (Linux 5.11 / 5.15).

Before 5.11, path-based metadata operations (create, rename, unlink, …) had no io_uring opcode and had to run on a worker thread or out-of-band. Linux 5.11 added RENAMEAT and UNLINKAT; 5.15 rounded out the set with MKDIRAT, SYMLINKAT, and LINKAT, so a whole directory-mutation workflow can be driven through the ring.

This example builds a small workflow inside a fresh mkdtemp scratch directory, issuing every step as an io_uring SQE rather than a blocking libc syscall:

  1. MKDIRAT — create a sub/ subdirectory.

  2. (libc) — create a regular file sub/file (so there is something to act on).

  3. SYMLINKAT — make link point at sub/file.

  4. RENAMEAT — rename sub/file to sub/renamed.

  5. UNLINKAT — remove sub/renamed, then remove link.

  6. UNLINKAT — remove the now-empty sub/ (with AT_REMOVEDIR).

Each op's CQE res must be >= 0, and we verify the on-disk state with stat between the relevant steps. The scratch directory is always cleaned up.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "5.11 — Filesystem mutation ops, SQPOLL without root".

Run with: dub run --single fs-mutations.d

Portability

if the running kernel has no io_uring, or lacks any of these filesystem opcodes (probe / -EINVAL), the program prints a SKIP: line and exits 0 so it stays green in CI regardless of the host kernel.

io_uring_fs_mutations
;
import
(module) during

Simple idiomatic dlang wrapper around linux io_uring (see: https://kernel.dk/io_uring.pdf) asynchronous API.

during
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.string

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h

Source

core/stdc/string.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly@standardsISO/IEC 9899:1999 (E)
string
:
(alias) io_uring_fs_mutations.strlen = ulong core.stdc.string.strlen(scope const(char*) s) pure nothrow @nogc
strlen
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.stdlib

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
stdlib
:
(alias) io_uring_fs_mutations.mkdtemp = char* core.sys.posix.stdlib.mkdtemp(char*) nothrow @nogc
mkdtemp
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.fcntl

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly, Alex Rønne Petersen@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
fcntl
:
(alias constant) io_uring_fs_mutations.AT_FDCWD = int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
, open,
(alias constant) io_uring_fs_mutations.O_CREAT = int core.sys.posix.fcntl.O_CREAT = 64
O_CREAT
,
(alias constant) io_uring_fs_mutations.O_WRONLY = int core.sys.posix.fcntl.O_WRONLY = 1
O_WRONLY
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.sys
sys
.
(module) core.sys.posix.sys.stat

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly, Alex Rønne Petersen@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
stat
:
(alias) io_uring_fs_mutations.stat = int core.sys.posix.sys.stat.stat64(scope const(char*), core.sys.posix.sys.stat.stat_t*) nothrow @nogc
stat
,
(struct) core.sys.posix.sys.stat.stat_t
stat_t
, lstat,
(alias template) io_uring_fs_mutations.S_ISDIR = core.sys.posix.sys.stat.S_ISDIR()(mode_t mode)
S_ISDIR
,
(alias template) io_uring_fs_mutations.S_ISLNK = core.sys.posix.sys.stat.S_ISLNK()(mode_t mode)
S_ISLNK
,
(alias template) io_uring_fs_mutations.S_ISREG = core.sys.posix.sys.stat.S_ISREG()(mode_t mode)
S_ISREG
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.unistd

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly@standardsThe Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
unistd
:
(alias) io_uring_fs_mutations.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
,
(alias) io_uring_fs_mutations.rmdir = int core.sys.posix.unistd.rmdir(scope const(char*)) nothrow @nogc
rmdir
,
(alias) io_uring_fs_mutations.unlink = int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogc
unlink
;
import
(package) std
std
.
(module) std.stdio
Category Symbols
File handles _popen File isFileHandle openNetwork stderr stdin stdout
Reading chunks lines readf readfln readln
Writing toFile write writef writefln writeln
Misc KeepTerminator LockType StdioException

Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio is publically imported when importing std.stdio.

There are three layers of I/O:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
: stderr,
(alias template) io_uring_fs_mutations.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

Equivalent to writef(fmt, args, '\n').

writefln
;
import
(package) std
std
.
(module) std.string

String handling functions.

Category Functions
Searching
column
indexOf
indexOfAny
indexOfNeither
lastIndexOf
lastIndexOfAny
lastIndexOfNeither
Comparison
isNumeric
Mutation
capitalize
Pruning and Filling
center
chomp
chompPrefix
chop
detabber
detab
entab
entabber
leftJustify
outdent
rightJustify
strip
stripLeft
stripRight
wrap
Substitution
abbrev
soundex
soundexer
succ
tr
translate
Miscellaneous
assumeUTF
fromStringz
lineSplitter
representation
splitLines
toStringz
Objects of types string, wstring, and dstring are value types
and cannot be mutated element-by-element. For using mutation during building
strings, use char[], wchar[], or dchar[]. The xxxstring
types are preferable because they don't exhibit undesired aliasing, thus
making code more robust.

The following functions are publicly imported:

Module Functions
Publicly imported functions
std.algorithm
cmp, std,algorithm,comparison
count, std,algorithm,searching
endsWith, std,algorithm,searching
startsWith, std,algorithm,searching
std.array
join, std,array
replace, std,array
replaceInPlace, std,array
split, std,array
empty, std,array
std.format
format, std,format
sformat, std,format
std.uni
icmp, std,uni
toLower, std,uni
toLowerInPlace, std,uni
toUpper, std,uni
toUpperInPlace, std,uni
There is a rich set of functions for string handling defined in other modules.
Functions related to Unicode and ASCII are found in std.uni
and std.ascii, respectively. Other functions that have a
wider generality than just strings can be found in std.algorithm
and std.range.

Source

std/string.d

@seestd.algorithm and std.range for generic range algorithms , std.ascii for functions that work with ASCII strings , std.uni for functions that work with unicode strings@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Jonathan M Davis, and David L. 'SpottedTiger' Davis
string
:
(alias) io_uring_fs_mutations.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
// `AT_REMOVEDIR` (== 0x200) tells unlinkat(2) to remove a directory instead of a // file. core.sys.posix.fcntl doesn't expose it portably, so define it locally. enum int
(constant) int io_uring_fs_mutations.AT_REMOVEDIR = 512
AT_REMOVEDIR
= 0x200;
/// Submit one prepared SQE, wait for its single CQE, and return its `res` field. /// Returns the (possibly negative) kernel result; the caller decides how to react. int
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(Op)(ref
(struct) during.Uring

Main entry point to work with io_uring.

It hides SubmissionQueue and CompletionQueue behind standard range interface. We put in SubmissionEntry entries and take out CompletionEntry entries.

Use predefined prepXX methods to fill required fields of SubmissionEntry before put or during putWith.

Note

prepXX functions doesn't touch previous entry state, just fills in operation properties. This is because for less error prone interface it is cleared automatically when prepared using putWith. So when using on own SubmissionEntry (outside submission queue), that would be added to the submission queue using put, be sure its cleared if it's reused for multiple operations.

Uring
(parameter) during.Uring io
io
, scope
(alias) Op = during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system
Op
(parameter) during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep
prep
, ulong
(parameter) ulong cookie
cookie
)
{
(parameter) during.Uring io
io
.putWith!((ref SubmissionEntry e, scope Op p, ulong c) {
p(e); e.user_data = c; })(
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system p, ulong c) nothrow @nogc @system { p(e); e.user_data = c; } , during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system, ulong)(ref during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system __param_0, ref ulong __param_1) nothrow @nogc return ref @safe

Adds new entry to the SubmissionQueue.

Note that this just adds entry to the queue and doesn't advance the tail marker kernel sees. For that finishSq() is needed to be called next.

Also note that to actually enter new entries to kernel, it's needed to call submit().

@paramFN Function to fill next entry in queue by ref (should be faster). It is expected to be in a form of void function(ARGS)(ref SubmissionEntry, auto ref ARGS). Note that in this case queue entry is cleaned first before function is called.@paramentry Custom built SubmissionEntry to be posted as is. Note that in this case it is copied whole over one in the SubmissionQueue.@paramargs Optional arguments passed to the function@returnsreference to Uring structure so it's possible to chain multiple commands.
prep
,
(parameter) ulong cookie
cookie
);
const
(local variable) const(int) submitted
submitted
=
(parameter) during.Uring io
io
.
int during.Uring.submit(uint want) nothrow @nogc @safe

Submits qued SubmissionEntry to be processed by kernel.

@paramwant number of CompletionEntries to wait for. If 0, this just submits queued entries and returns. If > 0, it blocks until at least wanted number of entries were completed.@paramsig See io_uring_enter(2) man page@returnsNumber of submitted entries on success, -errno on error
submit
(1);
if (
(local variable) const(int) submitted
submitted
< 0)
return
(local variable) const(int) submitted
submitted
; // surface submit failure as a negative result
(parameter) during.Uring io
io
.
int during.Uring.wait(uint want = 1u) nothrow @nogc

Simmilar to submit but with this method we just wait for required number of CompletionEntries.

@returns0 on success, -errno on error
wait
(1);
const
(local variable) const(int) res
res
=
(parameter) during.Uring io
io
.
during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safe

Get first CompletionEntry from cq ring

front
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

res
;
assert(
(parameter) during.Uring io
io
.
during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safe

Get first CompletionEntry from cq ring

front
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
==
(parameter) ulong cookie
cookie
, "CQE cookie mismatch");
(parameter) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
return
(local variable) const(int) res
res
;
} /// True if `path` exists and matches the predicate over its `st_mode`. bool
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(scope const(char)*
(parameter) const(char)* path
path
, bool delegate(uint)
(parameter) bool delegate(uint) pred
pred
, bool
(parameter) bool useLstat
useLstat
= false)
{
(struct) core.sys.posix.sys.stat.stat_t
stat_t
(local variable) core.sys.posix.sys.stat.stat_t st
st
;
const
(local variable) const(int) rc
rc
=
(parameter) bool useLstat
useLstat
? lstat(
int core.sys.posix.sys.stat.lstat64(scope const(char*), core.sys.posix.sys.stat.stat_t*) nothrow @nogc
path
, &
(local variable) core.sys.posix.sys.stat.stat_t st
st
) :
int core.sys.posix.sys.stat.stat64(scope const(char*), core.sys.posix.sys.stat.stat_t*) nothrow @nogc
stat
(
int core.sys.posix.sys.stat.stat64(scope const(char*), core.sys.posix.sys.stat.stat_t*) nothrow @nogc
path
, &
(local variable) core.sys.posix.sys.stat.stat_t st
st
);
if (
(local variable) const(int) rc
rc
!= 0) return false;
return
(parameter) bool delegate(uint) pred
pred
(
(local variable) core.sys.posix.sys.stat.stat_t st
st
.
(field) uint core.sys.posix.sys.stat.stat_t.st_mode
st_mode
);
} int
int D main()
main
()
{
(struct) during.Uring

Main entry point to work with io_uring.

It hides SubmissionQueue and CompletionQueue behind standard range interface. We put in SubmissionEntry entries and take out CompletionEntry entries.

Use predefined prepXX methods to fill required fields of SubmissionEntry before put or during putWith.

Note

prepXX functions doesn't touch previous entry state, just fills in operation properties. This is because for less error prone interface it is cleared automatically when prepared using putWith. So when using on own SubmissionEntry (outside submission queue), that would be added to the submission queue using put, be sure its cleared if it's reused for multiple operations.

Uring
(local variable) during.Uring io
io
;
const
(local variable) const(int) setupRet
setupRet
=
(local variable) during.Uring io
io
.
int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safe

Setup new instance of io_uring into provided Uring structure.

@paramuring Uring structure to be initialized (must not be already initialized)@paramentries Number of entries to initialize uring with@paramflags SetupFlags to use to initialize uring.@returnsOn succes it returns 0, -errno otherwise.
setup
(8);
if (
(local variable) const(int) setupRet
setupRet
< 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -
(local variable) const(int) setupRet
setupRet
);
return 0; } // Capability probe: every op we use must be advertised by the kernel. If any // is missing this kernel predates 5.11/5.15 for that opcode — skip cleanly. auto
(local variable) during.Probe probe
probe
=
(local variable) during.Uring io
io
.
during.Probe during.Uring.probe() nothrow @nogc @safe

Probes supported operations

probe
();
if (!cast(bool)
(local variable) during.Probe probe
probe
)
{
void std.stdio.writefln!char(in char[] fmt) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: io_uring probe failed — cannot determine supported ops");
return 0; } foreach (
(parameter) during.io_uring.Operation op
op
; [
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.MKDIRAT = cast(ubyte)37u

IORING_OP_MKDIRAT - see mkdirat(2)

MKDIRAT
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.SYMLINKAT = cast(ubyte)38u

IORING_OP_SYMLINKAT - see symlinkat(2)

SYMLINKAT
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.RENAMEAT = cast(ubyte)35u

IORING_OP_RENAMEAT - see renameat2()

RENAMEAT
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.UNLINKAT = cast(ubyte)36u

IORING_OP_UNLINKAT - see unlinkat(2)

UNLINKAT
])
{ if (!
(local variable) during.Probe probe
probe
.
bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safe

Is operation supported?

isSupported
(
(local variable) during.io_uring.Operation op
op
))
{
void std.stdio.writefln!(char, during.io_uring.Operation)(in char[] fmt, during.io_uring.Operation __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: io_uring op %s unsupported on this kernel (needs 5.11/5.15)",
(local variable) during.io_uring.Operation op
op
);
return 0; } } // Fresh scratch directory under the system temp dir. mkdtemp mutates the // template in place and returns null on failure. char[]
(local variable) char[] tmpl
tmpl
= "/tmp/iouring_fs_XXXXXX\0".
char[] object.dup!char(const(char)[] a) pure nothrow @property @safe
dup
;
if (
char* core.sys.posix.stdlib.mkdtemp(char*) nothrow @nogc
mkdtemp
(
(local variable) char[] tmpl
tmpl
.
(field) char* char[].ptr
ptr
) is null)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("mkdtemp failed");
return 1; } const
(local variable) const(string) base
base
= cast(
(alias) object.string = string
string
)
(local variable) char[] tmpl
tmpl
[0 ..
ulong core.stdc.string.strlen(scope const(char*) s) pure nothrow @nogc
strlen
(
(local variable) char[] tmpl
tmpl
.
(field) char* char[].ptr
ptr
)];
scope (exit)
int core.sys.posix.unistd.rmdir(scope const(char*)) nothrow @nogc
rmdir
(
(local variable) const(string) base
base
.
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

import core.stdc.string : strlen;
import std.conv : to;

auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);

string test = "";
p = toStringz(test);
assert(*p == 0);

test = "\0";
p = toStringz(test);
assert(*p == 0);

test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);

const string test2 = "";
p = toStringz(test2);
assert(*p == 0);

assert(toStringz([]) is toStringz(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
); // remove the top-level scratch dir last
// Build absolute paths once; toStringz gives us GC'd, NUL-terminated copies // that stay alive for the whole submit/complete cycle of each op. const
(local variable) const(immutable(char)*) subDir
subDir
= (
(local variable) const(string) base
base
~ "/sub").
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

import core.stdc.string : strlen;
import std.conv : to;

auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);

string test = "";
p = toStringz(test);
assert(*p == 0);

test = "\0";
p = toStringz(test);
assert(*p == 0);

test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);

const string test2 = "";
p = toStringz(test2);
assert(*p == 0);

assert(toStringz([]) is toStringz(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
const
(local variable) const(immutable(char)*) filePath
filePath
= (
(local variable) const(string) base
base
~ "/sub/file").
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

import core.stdc.string : strlen;
import std.conv : to;

auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);

string test = "";
p = toStringz(test);
assert(*p == 0);

test = "\0";
p = toStringz(test);
assert(*p == 0);

test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);

const string test2 = "";
p = toStringz(test2);
assert(*p == 0);

assert(toStringz([]) is toStringz(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
const
(local variable) const(immutable(char)*) renamed
renamed
= (
(local variable) const(string) base
base
~ "/sub/renamed").
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

import core.stdc.string : strlen;
import std.conv : to;

auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);

string test = "";
p = toStringz(test);
assert(*p == 0);

test = "\0";
p = toStringz(test);
assert(*p == 0);

test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);

const string test2 = "";
p = toStringz(test2);
assert(*p == 0);

assert(toStringz([]) is toStringz(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
const
(local variable) const(immutable(char)*) linkPath
linkPath
= (
(local variable) const(string) base
base
~ "/link").
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

import core.stdc.string : strlen;
import std.conv : to;

auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);

string test = "";
p = toStringz(test);
assert(*p == 0);

test = "\0";
p = toStringz(test);
assert(*p == 0);

test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);

const string test2 = "";
p = toStringz(test2);
assert(*p == 0);

assert(toStringz([]) is toStringz(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
// Symlink target is relative-as-stored; we point the link at the absolute file. const
(local variable) const(immutable(char)*) linkTarget
linkTarget
=
(local variable) const(immutable(char)*) filePath
filePath
;
// 1. MKDIRAT: create base/sub with mode 0755. { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepMkdirat(return ref during.io_uring.SubmissionEntry entry, int dirfd, const(char)* path, uint mode) nothrow @nogc ref

Note

Available from Linux 5.15

prepMkdirat
(
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) subDir
subDir
,
(template instance) io_uring_fs_mutations.octal!755
octal
!755),
1); if (
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
)
{
void std.stdio.writefln!char(in char[] fmt) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: MKDIRAT rejected with -EINVAL — unsupported on this kernel");
return 0; } if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("MKDIRAT failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } if (!
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) subDir
subDir
,
(alias) __T1 = uint
m
=>
bool core.sys.posix.sys.stat.S_ISDIR!()(uint mode) pure nothrow @nogc @safe
S_ISDIR
(
(parameter) uint m
m
)))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-MKDIRAT: sub/ is not a directory");
return 1; } // 2. Create the regular file via libc (there's no IORING_OP_CREATE; openat // direct/fixed exists but a plain file is all we need to mutate below). { const
(local variable) const(int) fd
fd
= open(
int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogc
filePath
,
(constant) int core.sys.posix.fcntl.O_CREAT = 64
O_CREAT
|
(constant) int core.sys.posix.fcntl.O_WRONLY = 1
O_WRONLY
,
(template instance) io_uring_fs_mutations.octal!644
octal
!644);
if (
(local variable) const(int) fd
fd
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("open(sub/file) failed");
return 1; }
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) const(int) fd
fd
);
} // 3. SYMLINKAT: base/link -> base/sub/file. { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepSymlinkat(return ref during.io_uring.SubmissionEntry entry, const(char)* target, int newdirfd, const(char)* linkpath) nothrow @nogc ref

Note

Available from Linux 5.15

prepSymlinkat
(
(local variable) const(immutable(char)*) linkTarget
linkTarget
,
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) linkPath
linkPath
),
3); if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("SYMLINKAT failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } // lstat the link itself (don't follow) to confirm it's a symlink, and stat it // (follow) to confirm it resolves to the regular file. if (!
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) linkPath
linkPath
,
(alias) __T2 = uint
m
=>
bool core.sys.posix.sys.stat.S_ISLNK!()(uint mode) pure nothrow @nogc @safe
S_ISLNK
(
(parameter) uint m
m
), /*useLstat*/ true))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-SYMLINKAT: link is not a symlink");
return 1; } if (!
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) linkPath
linkPath
,
(alias) __T3 = uint
m
=>
bool core.sys.posix.sys.stat.S_ISREG!()(uint mode) pure nothrow @nogc @safe
S_ISREG
(
(parameter) uint m
m
)))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-SYMLINKAT: link does not resolve to a regular file");
return 1; } // 4. RENAMEAT: sub/file -> sub/renamed (flags 0 == plain renameat semantics). { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepRenameat(return ref during.io_uring.SubmissionEntry entry, int olddfd, const(char)* oldpath, int newfd, const(char)* newpath, int flags) nothrow @nogc ref

Note

Available from Linux 5.11

prepRenameat
(
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) filePath
filePath
,
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) renamed
renamed
, 0),
4); if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("RENAMEAT failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } if (
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) filePath
filePath
,
(alias) __T4 = uint
m
=> true) || !
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) renamed
renamed
,
(alias) __T5 = uint
m
=>
bool core.sys.posix.sys.stat.S_ISREG!()(uint mode) pure nothrow @nogc @safe
S_ISREG
(
(parameter) uint m
m
)))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-RENAMEAT: rename did not take effect");
return 1; } // 5a. UNLINKAT: remove sub/renamed (the regular file). { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepUnlinkat(return ref during.io_uring.SubmissionEntry entry, int dirfd, const(char)* path, int flags) nothrow @nogc ref

Note

Available from Linux 5.11

prepUnlinkat
(
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) renamed
renamed
, 0),
5); if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("UNLINKAT(file) failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } // 5b. UNLINKAT: remove the dangling symlink (flags 0 unlinks the link itself). { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepUnlinkat(return ref during.io_uring.SubmissionEntry entry, int dirfd, const(char)* path, int flags) nothrow @nogc ref

Note

Available from Linux 5.11

prepUnlinkat
(
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) linkPath
linkPath
, 0),
6); if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("UNLINKAT(symlink) failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } if (
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) renamed
renamed
,
(alias) __T6 = uint
m
=> true, true) ||
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) linkPath
linkPath
,
(alias) __T7 = uint
m
=> true, true))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-UNLINKAT: file or symlink still present");
return 1; } // 6. UNLINKAT with AT_REMOVEDIR: remove the now-empty sub/ directory. { const
(local variable) const(int) res
res
=
int io_uring_fs_mutations.runOne!(during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system)(ref during.Uring io, scope during.io_uring.SubmissionEntry delegate(ref during.io_uring.SubmissionEntry) nothrow @nogc @system prep, ulong cookie) nothrow @nogc @system

Submit one prepared SQE, wait for its single CQE, and return its res field. Returns the (possibly negative) kernel result; the caller decides how to react.

runOne
(
(local variable) during.Uring io
io
,
(ref
(struct) during.io_uring.SubmissionEntry

IO operation submission data structure (Submission queue entry).

C API: struct io_uring_sqe

SubmissionEntry
(parameter) during.io_uring.SubmissionEntry e
e
) =>
(parameter) during.io_uring.SubmissionEntry e
e
.
during.io_uring.SubmissionEntry during.prepUnlinkat(return ref during.io_uring.SubmissionEntry entry, int dirfd, const(char)* path, int flags) nothrow @nogc ref

Note

Available from Linux 5.11

prepUnlinkat
(
(constant) int core.sys.posix.fcntl.AT_FDCWD = -100
AT_FDCWD
,
(local variable) const(immutable(char)*) subDir
subDir
,
(constant) int io_uring_fs_mutations.AT_REMOVEDIR = 512
AT_REMOVEDIR
),
7); if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("UNLINKAT(dir) failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } if (
bool io_uring_fs_mutations.checkMode(scope const(char)* path, bool delegate(uint) pred, bool useLstat = false)

True if path exists and matches the predicate over its st_mode.

checkMode
(
(local variable) const(immutable(char)*) subDir
subDir
,
(alias) __T8 = uint
m
=> true, true))
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("post-UNLINKAT: sub/ directory still present");
return 1; }
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("ok: drove MKDIRAT/SYMLINKAT/RENAMEAT/UNLINKAT through the ring in %s — final state verified",
(local variable) const(string) base
base
);
return 0; } // errno + octal helpers (kept local to avoid extra imports cluttering the header). import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.linux
linux
.
(module) core.sys.linux.errno

D header file for GNU/Linux

glibc stdlib/errno.h

errno
:
(alias constant) io_uring_fs_mutations.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
;
/// Compile-time octal literal (D dropped the `0NNN` syntax; std.conv.octal works too). template
(template) io_uring_fs_mutations.octal(int n)

Compile-time octal literal (D dropped the 0NNN syntax; std.conv.octal works too).

octal
(int n) { enum
(constant) int io_uring_fs_mutations.octal!644 = 420

Compile-time octal literal (D dropped the 0NNN syntax; std.conv.octal works too).

octal
=
int io_uring_fs_mutations.octalImpl(int decimalDigits) pure nothrow @nogc @safe
octalImpl
(
(constant) int io_uring_fs_mutations.n = 755
n
); }
private int
int io_uring_fs_mutations.octalImpl(int decimalDigits) pure nothrow @nogc @safe
octalImpl
(int
(parameter) int decimalDigits
decimalDigits
) pure nothrow @nogc @safe
{ int
(local variable) int result
result
= 0,
(local variable) int mult
mult
= 1;
while (
(parameter) int decimalDigits
decimalDigits
> 0)
{
(local variable) int result
result
+= (
(parameter) int decimalDigits
decimalDigits
% 10) *
(local variable) int mult
mult
;
(local variable) int mult
mult
*= 8;
(parameter) int decimalDigits
decimalDigits
/= 10;
} return
(local variable) int result
result
;
}