#!/usr/bin/env dub
/+ dub.sdl:
name "openat2_resolve_flags"
platforms "linux"
targetPath "build"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* `openat2(2)` — what each `RESOLVE_*` flag accepts and refuses (Linux 5.6+).
*
* Builds a scratch tree, then opens nine paths relative to its root with
* different `resolve` masks and prints the outcome of each. The point is the
* table this produces: a relative symlink that stays inside the root is fine
* under `RESOLVE_BENEATH` but refused under `RESOLVE_NO_SYMLINKS`; a `..` that
* stays beneath is fine in the kernel even though a portable component check
* must refuse it; an absolute symlink, an absolute path and a `..` climb are
* all `EXDEV`; a procfs magic link is `ELOOP` under `RESOLVE_NO_MAGICLINKS`.
*
* Companion to docs/research/safe-path-traversal/linux-openat2.md
* § "Dimension 3 — symlink and `..` policy".
*
* Run with: `dub run --single openat2-resolve-flags.d`
*
* Portability: on a kernel without `openat2` (pre-5.6, or a seccomp policy
* that returns `ENOSYS`), prints a `SKIP:` line and exits 0.
*/
module (module) openat2_resolve_flagsopenat2(2) — what each RESOLVE_* flag accepts and refuses (Linux 5.6+).
Builds a scratch tree, then opens nine paths relative to its root with
different resolve masks and prints the outcome of each. The point is the
table this produces: a relative symlink that stays inside the root is fine
under RESOLVE_BENEATH but refused under RESOLVE_NO_SYMLINKS; a .. that
stays beneath is fine in the kernel even though a portable component check
must refuse it; an absolute symlink, an absolute path and a .. climb are
all EXDEV; a procfs magic link is ELOOP under RESOLVE_NO_MAGICLINKS.
Companion to docs/research/safe-path-traversal/linux-openat2.md
§ "Dimension 3 — symlink and .. policy".
Run with: dub run --single openat2-resolve-flags.d
Portability
on a kernel without openat2 (pre-5.6, or a seccomp policy
that returns ENOSYS), prints a SKIP: line and exits 0.
openat2_resolve_flags;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.errnoD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h
Source
core/stdc/errno.d
errno : (alias) openat2_resolve_flags.errno = int core.stdc.errno.__errno_location() nothrow @nogc ref @trustederrno, (alias constant) openat2_resolve_flags.EXDEV = int core.stdc.errno.EXDEV = 18EXDEV, (alias constant) openat2_resolve_flags.ELOOP = int core.stdc.errno.ELOOP = 40ELOOP, (alias constant) openat2_resolve_flags.ENOSYS = int core.stdc.errno.ENOSYS = 38ENOSYS, (alias constant) openat2_resolve_flags.ENOENT = int core.stdc.errno.ENOENT = 2ENOENT;
import (package) corecore.(package) core.syssys.(package) core.sys.linuxlinux.(module) core.sys.linux.unistdunistd : (alias) openat2_resolve_flags.syscall = long core.sys.linux.unistd.syscall(long sysno, ...) nothrow @nogcInvoke system call number sysno, passing it the remaining arguments.
syscall;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.fcntlD header file for POSIX.
fcntl : (alias constant) openat2_resolve_flags.O_RDONLY = int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY, (alias constant) openat2_resolve_flags.O_DIRECTORY = int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY, (alias constant) openat2_resolve_flags.O_CLOEXEC = int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC, open;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) openat2_resolve_flags.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose;
import (package) stdstd.(module) std.convA one-stop shop for converting values from one type to another.
Category Functions Generic asOriginalType castFrom parse to toChars bitCast Strings text wtext dtext writeText writeWText writeDText hexString Numeric octal roundTo signed unsigned Exceptions ConvException ConvOverflowException
Source
std/conv.d
conv : (alias template) openat2_resolve_flags.text = std.conv.text(T...)(T args) if (T.length > 0)Convenience functions for converting one or more arguments
of any type into text (the three character widths).
text;
import (package) stdstd.(module) std.fileUtilities for manipulating files and scanning directories. Functions
in this module handle files as a unit, e.g., read or write one file
at a time. For opening files and manipulating them via handles refer
to module std.stdio.
Category Functions General exists isDir isFile isSymlink rename thisExePath Directories chdir dirEntries getcwd mkdir mkdirRecurse rmdir rmdirRecurse tempDir Files append copy read readText remove slurp write Symlinks symlink readLink Attributes attrIsDir attrIsFile attrIsSymlink getAttributes getLinkAttributes getSize setAttributes Timestamp getTimes getTimesWin setTimes timeLastModified timeLastAccessed timeStatusChanged Other DirEntry FileException PreserveAttributes SpanMode getAvailableDiskSpace
Source
std/file.d
file : (alias template) openat2_resolve_flags.exists = std.file.exists(R)(R name) if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)Determine whether the given file (or directory) exists.
exists, (alias) openat2_resolve_flags.mkdirRecurse = void std.file.mkdirRecurse(scope const(char)[] pathname) @safeMake directory and all parent directories as needed.
Does nothing if the directory specified by
pathname already exists.
mkdirRecurse, (alias) openat2_resolve_flags.rmdirRecurse = void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse, (alias template) openat2_resolve_flags.symlink = std.file.symlink(RO, RL)(RO original, RL link) if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL))This function is POSIX-Only.
Creates a symbolic link (symlink).
symlink, (alias) openat2_resolve_flags.tempDir = string std.file.tempDir() @trustedReturns the path to a directory for temporary files.
On POSIX platforms, it searches through the following list of directories
and returns the first one which is found to exist:
The directory given by the TMPDIR environment variable.
The directory given by the TEMP environment variable.
The directory given by the TMP environment variable.
/tmp/
/var/tmp/
/usr/tmp/
On all platforms, tempDir returns the current working directory on failure.
The return value of the function is cached, so the procedures described
below will only be performed the first time the function is called. All
subsequent runs will return the same string, regardless of whether
environment variables and directory structures have changed in the
meantime.
The POSIX tempDir algorithm is inspired by Python's
tempfile.tempdir.
tempDir, (alias template) openat2_resolve_flags.write = std.file.write(R)(R name, const void[] buffer) if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R)Write buffer to file name.
Creates the file if it does not already exist.
write;
import (package) stdstd.(module) std.pathThis module is used to manipulate path strings.
All functions, with the exception of expandTilde (and in some
cases absolutePath and relativePath), are pure
string manipulation functions; they don't depend on any state outside
the program, nor do they perform any actual file system actions.
This has the consequence that the module does not make any distinction
between a path that points to a directory and a path that points to a
file, and it does not know whether or not the object pointed to by the
path actually exists in the file system.
To differentiate between these cases, use isDir and
exists.
Note that on Windows, both the backslash (\) and the slash (/)
are in principle valid directory separators. This module treats them
both on equal footing, but in cases where a new separator is
added, a backslash will be used. Furthermore, the buildNormalizedPath
function will replace all slashes with backslashes on that platform.
In general, the functions in this module assume that the input paths
are well-formed. (That is, they should not contain invalid characters,
they should follow the file system's path format, etc.) The result
of calling a function on an ill-formed path is undefined. When there
is a chance that a path or a file name is invalid (for instance, when it
has been input by the user), it may sometimes be desirable to use the
isValidFilename and isValidPath functions to check
this.
Most functions do not perform any memory allocations, and if a string is
returned, it is usually a slice of an input string. If a function
allocates, this is explicitly mentioned in the documentation.
Category Functions Normalization absolutePath asAbsolutePath asNormalizedPath asRelativePath buildNormalizedPath buildPath chainPath expandTilde Partitioning baseName dirName dirSeparator driveName pathSeparator pathSplitter relativePath rootName stripDrive Validation isAbsolute isDirSeparator isRooted isValidFilename isValidPath Extension defaultExtension extension setExtension stripExtension withDefaultExtension withExtension Other filenameCharCmp filenameCmp globMatch CaseSensitive
Source
std/path.d
path : (alias template) openat2_resolve_flags.buildPath = std.path.buildPath(Range)(scope Range segments) if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range))Combines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
buildPath;
import (package) stdstd.(module) std.stdioCategory 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:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) openat2_resolve_flags.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) openat2_resolve_flags.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) stdstd.(module) std.stringString 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
string : (alias) openat2_resolve_flags.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedtoStringz;
// From <linux/openat2.h>. Not in druntime as of LDC 1.42.
enum (constant) int openat2_resolve_flags.RESOLVE_NO_XDEV = 1RESOLVE_NO_XDEV = 0x01;
enum (constant) int openat2_resolve_flags.RESOLVE_NO_MAGICLINKS = 2RESOLVE_NO_MAGICLINKS = 0x02;
enum (constant) int openat2_resolve_flags.RESOLVE_NO_SYMLINKS = 4RESOLVE_NO_SYMLINKS = 0x04;
enum (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH = 0x08;
enum (constant) int openat2_resolve_flags.RESOLVE_IN_ROOT = 16RESOLVE_IN_ROOT = 0x10;
// From <sys/syscall.h>: 437 on every architecture that has openat2 (it was
// added after the syscall tables were unified).
enum (constant) int openat2_resolve_flags.SYS_openat2 = 437SYS_openat2 = 437;
// `struct open_how` — extensible: the size argument lets the kernel tell an
// old struct from a new one (`E2BIG` if we pass trailing non-zero bytes it
// does not know about).
struct (struct) openat2_resolve_flags.open_howopen_how
{
ulong (field) ulong openat2_resolve_flags.open_how.flagsflags;
ulong (field) ulong openat2_resolve_flags.open_how.modemode;
ulong (field) ulong openat2_resolve_flags.open_how.resolveresolve;
}
/// Returns the fd, or `-errno`.
int int openat2_resolve_flags.openat2(int dirfd, string path, ulong flags, ulong resolve)Returns the fd, or -errno.
openat2(int (parameter) int dirfddirfd, (alias) object.string = stringstring (parameter) string pathpath, ulong (parameter) ulong flagsflags, ulong (parameter) ulong resolveresolve)
{
auto (local variable) openat2_resolve_flags.open_how howhow = (struct) openat2_resolve_flags.open_howopen_how((parameter) ulong flagsflags, 0, (parameter) ulong resolveresolve);
const (local variable) const(long) rr = long core.sys.linux.unistd.syscall(long sysno, ...) nothrow @nogcInvoke system call number sysno, passing it the remaining arguments.
syscall((constant) int openat2_resolve_flags.SYS_openat2 = 437SYS_openat2, (parameter) int dirfddirfd, (parameter) string pathpath.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
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(""));
toStringz, &(local variable) openat2_resolve_flags.open_how howhow, (local variable) openat2_resolve_flags.open_how howhow.(constant) ulong openat2_resolve_flags.open_how.sizeof = 24LUsizeof);
return (local variable) const(long) rr < 0 ? -int core.stdc.errno.__errno_location() nothrow @nogc ref @trustederrno : cast(int) (local variable) const(long) rr;
}
(alias) object.string = stringstring string openat2_resolve_flags.errName(int e)errName(int (parameter) int ee)
{
switch ((parameter) int ee)
{
case (constant) int core.stdc.errno.EXDEV = 18EXDEV: return "EXDEV";
case (constant) int core.stdc.errno.ELOOP = 40ELOOP: return "ELOOP";
case (constant) int core.stdc.errno.ENOENT = 2ENOENT: return "ENOENT";
case (constant) int core.stdc.errno.ENOSYS = 38ENOSYS: return "ENOSYS";
default: return string std.conv.text!(string, int)(string __param_0, int __param_1) pure nothrow @safeConvenience functions for converting one or more arguments
of any type into text (the three character widths).
text("errno ", (parameter) int ee);
}
}
void void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report((alias) object.string = stringstring (parameter) string labellabel, int (parameter) int dirfddirfd, (alias) object.string = stringstring (parameter) string pathpath, ulong (parameter) ulong resolveresolve)
{
const (local variable) const(int) fdfd = int openat2_resolve_flags.openat2(int dirfd, string path, ulong flags, ulong resolve)Returns the fd, or -errno.
openat2((parameter) int dirfddirfd, (parameter) string pathpath, (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC, (parameter) ulong resolveresolve);
if ((local variable) const(int) fdfd >= 0)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
void std.stdio.writefln!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-42s %-30s ok", (parameter) string pathpath, (parameter) string labellabel);
}
else
void std.stdio.writefln!(char, string, string, string)(in char[] fmt, string __param_1, string __param_2, string __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-42s %-30s %s", (parameter) string pathpath, (parameter) string labellabel, string openat2_resolve_flags.errName(int e)errName(-(local variable) const(int) fdfd));
}
int int D main()main()
{
// Probe first: an `ENOSYS` here means no openat2 at all.
{
const (local variable) const(int) fdfd = int openat2_resolve_flags.openat2(int dirfd, string path, ulong flags, ulong resolve)Returns the fd, or -errno.
openat2(-100 /* AT_FDCWD */, ".", (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY, 0);
if ((local variable) const(int) fdfd == -(constant) int core.stdc.errno.ENOSYS = 38ENOSYS)
{
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("SKIP: openat2 unavailable on this kernel (ENOSYS)");
return 0;
}
if ((local variable) const(int) fdfd >= 0)
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
}
const (local variable) const(string) rootroot = string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath(string std.file.tempDir() @trustedReturns the path to a directory for temporary files.
On POSIX platforms, it searches through the following list of directories
and returns the first one which is found to exist:
The directory given by the TMPDIR environment variable.
The directory given by the TEMP environment variable.
The directory given by the TMP environment variable.
/tmp/
/var/tmp/
/usr/tmp/
On all platforms, tempDir returns the current working directory on failure.
The return value of the function is cached, so the procedures described
below will only be performed the first time the function is called. All
subsequent runs will return the same string, regardless of whether
environment variables and directory structures have changed in the
meantime.
The POSIX tempDir algorithm is inspired by Python's
tempfile.tempdir.
Examples
import std.ascii : letters;
import std.conv : to;
import std.path : buildPath;
import std.random : randomSample;
import std.utf : byCodeUnit;
// random id with 20 letters
auto id = letters.byCodeUnit.randomSample(20).to!string;
auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
scope(exit) myFile.remove;
myFile.write("hello");
assert(myFile.readText == "hello");
tempDir, "openat2-resolve-flags-demo");
if ((local variable) const(string) rootroot.bool std.file.exists!string(string name) nothrow @nogc @safeDetermine whether the given file (or directory) exists.
exists)
void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse((local variable) const(string) rootroot);
scope (exit) void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse((local variable) const(string) rootroot);
void std.file.mkdirRecurse(scope const(char)[] pathname) @safeMake directory and all parent directories as needed.
Does nothing if the directory specified by
pathname already exists.
Examples
import std.path : buildPath;
auto dir = deleteme ~ "dir";
scope(exit) dir.rmdirRecurse;
dir.mkdir;
assert(dir.exists);
dir.mkdirRecurse; // does nothing
// creates all parent directories as needed
auto nested = dir.buildPath("a", "b", "c");
nested.mkdirRecurse;
assert(nested.exists);
import std.exception : assertThrown;
scope(exit) deleteme.remove;
deleteme.write("a");
// cannot make directory as it's already a file
assertThrown!FileException(deleteme.mkdirRecurse);
mkdirRecurse(string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "inside"));
void std.file.write!string(string name, const(void[]) buffer) @safeWrite buffer to file name.
Creates the file if it does not already exist.
Examples
scope(exit)
{
assert(exists(deleteme));
remove(deleteme);
}
int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
write(deleteme, a); // deleteme is the name of a temporary file
const bytes = read(deleteme);
const fileInts = () @trusted { return cast(int[]) bytes; }();
assert(fileInts == a);
write(string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "inside", "file.txt"), "x");
void std.file.symlink!(string, string)(string original, string link) @safeThis function is POSIX-Only.
Creates a symbolic link (symlink).
symlink("inside", string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "link-in")); // relative, stays beneath
void std.file.symlink!(string, string)(string original, string link) @safeThis function is POSIX-Only.
Creates a symbolic link (symlink).
symlink("/", string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "link-out")); // absolute: leaves the root
const (local variable) const(int) dirfddirfd = open(int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogcroot.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
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(""));
toStringz, (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY | (constant) int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC);
assert((local variable) const(int) dirfddirfd >= 0);
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) dirfddirfd);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("relative to the scratch root:");
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "inside/file.txt", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "inside/../inside/file.txt", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "../", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "/etc", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "link-out/etc", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) dirfddirfd, "link-in/file.txt", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH|NO_SYMLINKS", (local variable) const(int) dirfddirfd, "link-in/file.txt",
(constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH | (constant) int openat2_resolve_flags.RESOLVE_NO_SYMLINKS = 4RESOLVE_NO_SYMLINKS);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("IN_ROOT", (local variable) const(int) dirfddirfd, "link-out/inside/file.txt", (constant) int openat2_resolve_flags.RESOLVE_IN_ROOT = 16RESOLVE_IN_ROOT);
// A magic link: `/proc/self/cwd` is not a symlink to a path but a jump to
// a kernel object, which is what `RESOLVE_NO_MAGICLINKS` refuses.
const (local variable) const(int) procfdprocfd = open("/proc/self", (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY | (constant) int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC);
if ((local variable) const(int) procfdprocfd >= 0)
{
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) procfdprocfd);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("relative to /proc/self:");
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("(none)", (local variable) const(int) procfdprocfd, "cwd", 0);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("NO_MAGICLINKS", (local variable) const(int) procfdprocfd, "cwd", (constant) int openat2_resolve_flags.RESOLVE_NO_MAGICLINKS = 2RESOLVE_NO_MAGICLINKS);
void openat2_resolve_flags.report(string label, int dirfd, string path, ulong resolve)report("BENEATH", (local variable) const(int) procfdprocfd, "cwd", (constant) int openat2_resolve_flags.RESOLVE_BENEATH = 8RESOLVE_BENEATH);
}
return 0;
}