Peios Learn
Products
PePeios pkpekit PvProvium UDUniversal Directory TrTrail Ckclackatoo PrProject WiWispist
Using Peios Security Basics Technical Documentation Source
Using Peios Security Basics Technical Documentation Source
Peios

Files and directories

Single-page view · as markdown

Files and directories

Peios / Using Peios / Peiosutils / Files and directories

This topic covers commands for creating, copying, moving, removing, and resizing files and directories, as well as reporting disk usage. Commands in other topics can also write output files.

This page names every command, and then explains the one theme that runs through the whole topic on Peios: what happens to a file's security descriptor when the file is created or copied.

The commands #

CommandPurpose
cpCopy files and directories.
mvMove or rename files and directories.
rmRemove files, and with -r, directories.
rmdirRemove directories, but only empty ones.
mkdirCreate directories.
mkfifoCreate named pipes (FIFOs).
mknodCreate special files — device nodes and FIFOs.
touchCreate empty files, or update a file's timestamps.
lnCreate hard links and symbolic links.
link and unlinkThe single-purpose, low-level versions of "make a hard link" and "remove a file".
shredDestroy a file's contents by overwriting them, so they cannot be recovered.
ddCopy data block by block, converting it on the way.
dfReport how much space each file system has, and how much is free.
duReport how much space files and directories use.
truncateShrink or extend a file to an exact size.
mktempCreate a temporary file or directory with a safely unique name.
sdInspect selected file security components and change file security descriptors.
mkexecMark a regular file as a program, so the kernel is willing to run it.

Every file has a security descriptor #

On Peios, every file and directory carries a security descriptor — the record of who owns it and who may do what to it. Access to a file is decided from its descriptor; see Security descriptors for the full picture.

That fact shapes two groups of commands in this topic.

Creating a file gives it a descriptor #

When mkdir, mkfifo, mknod, or touch creates a new object, that object needs a security descriptor. By default it gets one inherited from the directory it is created in — the new object picks up the access rules its parent directory hands down.

All four of these commands share one set of options — the creation flags — that let you set the new object's descriptor explicitly instead of taking the inherited default: name an owner, set a different group, lock the object so it does not inherit, give it an integrity label, or supply a complete descriptor. The flags are documented in full on the mkdir page; mkfifo, mknod, and touch each link back to it.

Copying a file makes a fresh descriptor — unless you preserve #

cp creates new files, and a cross-file-system mv does too. A brand-new copy gets a brand-new descriptor inherited from its destination directory — it does not carry the source file's security across by default.

When you do want the copy to keep the source's owner, access rules, timestamps, or other attributes, that is the job of the --preserve family of options. cp and mv share an identical --preserve surface; it is documented on the cp page.

Removing a file checks whether you may write it #

rm and shred both make a decision based on whether you can write a file. rm prompts before removing a file you cannot write to; shred's --force overrides a file's protection so it can be destroyed. Both judge "can you write this?" by a live access check against the file's security descriptor — not by any decorative metadata on the inode. Each command's page explains exactly where that check is made.

Where to start #

For copying — and for the whole --preserve model — read cp.

For creating files and directories, and the shared creation flags, read mkdir.

For removing things safely, read rm.

cp

Peios / Using Peios / Peiosutils / Files and directories

cp copies files and directories.

cp [options] source dest
cp [options] source... directory
cp [options] -t directory source...

In the first form, cp copies one source to one dest. In the other two, it copies any number of sources into a directory, keeping their names.

$ cp report.txt report-backup.txt
$ cp report.txt notes.txt /home/jack/archive/

Copying directories #

cp refuses to copy a directory unless you ask it to descend into one:

OptionEffect
-r, -R, --recursiveCopy directories, and everything inside them, recursively.
-a, --archiveCopy recursively, follow no symbolic links, and request all supported preservation attributes listed below. Equivalent to -d -R --preserve-all.

-a requests the tree structure and the supported preservation set, including owner, DACL, SACL, timestamps, hard links, executable-ness, and extended attributes. It does not guarantee a complete security-descriptor duplicate; see the component limits below. A plain -r copies the contents but gives newly created files fresh security of their own.

A recursive copy never follows a symbolic link inside the destination. If a directory being copied corresponds to an entry in the destination tree that is a symbolic link to a directory, cp refuses with "cannot overwrite non-directory with directory" rather than writing the source tree through the link. Naming a symbolic link to a directory as the destination operand itself is still allowed, as it is with GNU cp.

What a copy's security descriptor is #

This is the part of cp that is specific to Peios, so it is worth being precise.

Every file carries a security descriptor — the record of who owns it and who may do what to it. When cp creates a new destination file, that new file needs a descriptor. The defaults here describe newly created files, not a promise to replace the descriptor of an existing destination.

By default, a copy gets a fresh descriptor — it does not inherit the source's. Specifically, a plain cp:

  • gives the copy a descriptor inherited from the destination directory, exactly as if you had created a new file there;
  • makes you the owner of the copy;
  • gives the copy fresh timestamps — the copy's modification time is "now".

This is usually what you want. A file copied into your home directory should be governed by your home directory's rules and owned by you, regardless of where it came from. But when you need the copy to keep something from the source, you ask for it explicitly — with the --preserve family.

The --preserve family #

--preserve carries chosen attributes from the source onto the copy. The attributes you can preserve:

AttributeWhat it carries from the source
ownerThe owner SID.
daclThe whole DACL — every access rule on the file, inherited rules included.
saclThe whole SACL — the audit rules and the integrity label.
daclniThe DACL with inherited rules stripped out — only the rules set explicitly on the source.
saclniThe SACL with inherited rules stripped out.
timestampsThe access and modification times, not creation or change time.
linksHard-link relationships among source entries selected in this command. Links to names outside that set are not recreated.
execExecutable-ness: if the source has any POSIX execute bit, set all three execute bits on the destination; otherwise clear them. Symbolic links are skipped. This does not preserve each Unix mode bit separately.
securityExtended attributes whose names begin with security., except security.peios.sd, which is handled through the descriptor components above.
xattrsExtended attributes outside the security.* namespace. xattr is an alias.

You rarely name those individually. These options select sensible sets:

OptionPreserves
-ptimestamps and exec, both required.
--preservetimestamps only. This differs from -p.
--preserve=LISTThe comma-separated attributes you name, with no implicit attributes. Other preservation options can add or remove attributes.
--preserve-allEvery supported attribute in the table above, including exec. The full DACL/SACL selections take precedence over their inherited-rule-stripping variants.
--sdowner, dacl, sacl, including inherited ACL entries. The descriptor's group component is not selected.
--sd-explicitowner, daclni, saclni — the owner and the source's explicitly set ACL rules. The group component is not selected.
--no-preserve=LISTTurns the named attributes back off — useful to subtract from a broader option.

With no explicit preservation list, a plain copy preserves exec best-effort: failure to carry executable-ness does not fail the copy. An explicit --preserve=LIST starts with no implicit attributes, so include exec if you need it. Bare --preserve uses the list timestamps and likewise does not select exec. In contrast, -p, -a, and an explicit --preserve=exec require it. These distinctions come from the option defaults and cp's bare-option parsing; the exec handler implements the execute-bit conversion.

Preservation options combine in command-line order: requests add attributes, --no-preserve removes them, and -a or --preserve-all selects the full supported set again. For example, -a --no-preserve=exec removes executable-ness from the archive request. See the resolver.

The security selection is broader than security.peios.*: it can include compatibility attributes such as security.capability if present and readable. xattrs does not include that namespace. The attribute filters exclude security.peios.sd from both classes, so copying extended attributes cannot fill in descriptor components omitted by the SD copy. The xattr copy loop sets source attributes but does not remove destination-only attributes. Copying over an existing destination is not an exact attribute synchronization.

--sd versus --sd-explicit #

Both select the source's owner, DACL, and SACL; they differ in how they treat inherited rules.

  • --sd passes the owner and the whole source DACL and SACL, including inherited entries, to the destination's descriptor-setting operation.
  • --sd-explicit carries the owner but strips inherited entries from the source ACLs before applying them, letting the destination directory supply inheritance. Use it when copying a file into a different directory and you want the file's own tailored rules to come along while still picking up the new location's inherited rules.

Neither option selects the descriptor's GROUP component. The copy implementation selects only OWNER, DACL, and SACL; it does not request replacement of the destination's group from the source. There is no group preservation attribute. The same limit applies to -a and --preserve-all. These options are not a promise of a byte-identical descriptor or preservation of every descriptor control bit, and do not establish a complete security backup/restore procedure. When the destination already has a valid descriptor, the kernel merges selected components and their selected control bits; it retains unselected group and resource-manager state. Missing or invalid destination descriptors use a separate privileged replacement path. See the set-security contract.

For what "inherited" versus "explicit" means, see Inheritance.

When a preserve cannot be honoured #

Required preservation failures are errors in the copy operation, including required exec preservation. This differs from the best-effort executable-ness of a plain copy. Descriptor writes must pass the component's access and validation checks; a full SACL write also needs enabled SeRelabelPrivilege if its label exceeds the caller's integrity. SeSecurityPrivilege or SeRestorePrivilege alone does not guarantee that every label can be copied. See integrity-label constraints.

A recursive link-preserving copy can suppress a returned error for a source symbolic link. This includes -a: the recursive caller does not propagate that error when hard-link preservation is selected. An underlying operation may still print a diagnostic, so this is not a claim that every such failure is silent. A successful exit status does not certify that every link and its required metadata were copied.

Failure is not a rollback. Destination files or a partially copied tree may already exist when a preserve fails; the regular-file error path even attempts to truncate the destination. Treat that output as incomplete; do not delete the source or replace a known-good backup on the strength of a partial copy. Check the exit status and verify the content and each attribute you require before relying on the result.

Source-side unsupported xattr enumeration is a separate accepted case. security and xattrs name extended attributes, and some filesystems have no extended attributes at all — FAT, 9p, and a squashfs image built without an attribute table are examples. If source enumeration reports that it is unsupported, the xattr handler returns success for that step without copying attributes. This does not establish that every other copy step succeeded.

That exception applies only to source enumeration. If an attribute is read from the source but cannot be set on the destination, the attribute handler returns an error. The recursive source-symlink exception above still applies. A diagnostic can look like:

# cp -a /home/jack/notes /mnt/usb-fat/
cp: failed to set extended attributes on '/mnt/usb-fat/notes': Operation not supported

The distinction matters for -a, which requests both attribute classes: unsupported source enumeration is accepted, while a destination set failure remains a preservation error.

Overwriting an existing destination #

By default, if the destination already exists, cp overwrites it. These options change that.

OptionEffect
-i, --interactiveAsk before overwriting an existing file.
-n, --no-clobberNever overwrite an existing file; skip it silently.
-u, --update[=WHICH]Overwrite only when the source is newer than the destination. WHICH can be all, none, or older.
-f, --forceIf an existing destination cannot be opened for writing, remove it and try the copy again.
--remove-destinationRemove the destination before opening it, always — contrast --force, which only removes on failure.
-b, --backup[=CONTROL]Before overwriting, make a backup of the destination.

A backup is refused when making it would destroy the source: if the source is the file the destination's backup would be written over (for example cp --backup=simple a~ a), cp stops with an error instead of overwriting it. The check compares file identity, not spelling.

Symbolic links in the source #

When a source is a symbolic link, cp can copy the link itself or the file it points to.

OptionEffect
-L, --dereferenceAlways follow symbolic links — copy the file they point to.
-P, --no-dereferenceNever follow symbolic links — copy the link itself.
-HFollow only the symbolic links named directly on the command line, not links found while recursing.
-dCopy symbolic links as links, and preserve hard-link structure.

Other ways to copy #

cp can also produce something other than a plain duplicate.

OptionEffect
-l, --linkCreate a hard link to the source instead of copying its data.
-s, --symbolic-linkCreate a symbolic link to the source instead of copying.
--reflink[=WHEN]Make a lightweight copy-on-write clone where the file system supports it. WHEN is always, auto, or never.
--sparse[=WHEN]Control whether runs of zero bytes are stored as holes rather than written out. WHEN is always, auto, or never.
--attributes-onlyCreate the destination and copy its attributes, but not its data.

Other options #

OptionEffect
-t, --target-directory=DIRCopy every source into DIR. Useful when the directory is not the last argument.
-T, --no-target-directoryTreat dest as a plain file even if it is a directory.
--parentsRecreate the source's leading directories under the destination.
-x, --one-file-systemDo not cross into a different file system while recursing.
--strip-trailing-slashesStrip any trailing slashes from each source argument before using it.
-v, --verbosePrint each file as it is copied.
--debugExplain in detail how each file was copied. Implies -v.
--progressShow a progress bar.

Exit status #

CodeMeaning
0No failure reached the command's exit status. Skipped entries and the recursive source-symlink error case above mean this is not proof that every file or attribute was copied.
1A copy failure was reported through the command's error path, including propagated required-preservation failures.

Source and version scope #

The preservation corrections on this page are checked against peiosutils 0.8.18, commit 3344d469. The cited implementation establishes the option selections and copy behavior at that revision. It does not establish when each behavior first shipped, prove the version installed on a particular machine, or substitute for runtime validation of a copy or restore.

mv

Peios / Using Peios / Peiosutils / Files and directories

mv moves files and directories from one place to another. Moving a file within the same directory, under a new name, is how you rename it.

mv [options] source dest
mv [options] source... directory
mv [options] -t directory source...
$ mv draft.txt report.txt                 # rename
$ mv report.txt notes.txt /home/jack/done/ # move into a directory

A move is either a rename or a copy-and-delete #

mv does its job in one of two ways, and which one decides what happens to the file's security.

Within one file system, a move is normally a rename. Nothing is copied: the file stays exactly where it physically is, and only its name and directory entry change. The file keeps its inode, and so it keeps its security descriptor unchanged — its owner, access rules, and audit rules stay the same. A rename does not re-evaluate inheritance; a file does not pick up new rules just because it landed in a new directory.

Across file systems, a move is a copy followed by a delete. When the destination is on a different file system, mv cannot just rename — it copies the data to a new file on the destination, then removes the original. That new file is a genuinely new object, so, exactly as with cp, it gets a fresh security descriptor inherited from the destination directory unless you ask otherwise.

So: rename a file and its security is untouched; move it to another file system and the copy starts fresh.

The same applies to POSIX ownership: the copy is a new file owned by whoever ran mv, and the source's uid and gid are not carried. Because of that, a cross-file-system move also drops the setuid and setgid bits from the copy, as GNU mv does: a set-id file that changed hands would grant the mover's identity, not the original owner's. Data, timestamps and ordinary extended attributes do travel with the file.

When a directory is moved across file systems onto an existing destination, mv removes the destination and recreates it. If something else appears under that name in between, mv refuses with "destination was replaced while moving" rather than copying into it.

Selecting attributes forces a copy-and-delete #

mv accepts the same attribute-selection options as cp: --preserve, --no-preserve, --preserve-all, --sd, and --sd-explicit. Specifying any of these options forces a copy followed by deletion of the source, even within one file system, rather than a rename. The selected options control which attributes are carried onto the new file.

See cp attribute selection and security preservation for the attribute list and the meaning of these options.

Overwriting an existing destination #

By default mv overwrites an existing destination. These options change that. When more than one of -i, -f, -n is given, the last one wins.

OptionEffect
-i, --interactiveAsk before overwriting an existing file.
-f, --forceDo not prompt before overwriting.
-n, --no-clobberNever overwrite an existing file.
-u, --update[=WHICH]Select the update policy below. -u and bare --update select older.
-b, --backup[=CONTROL]Make a backup of the destination before overwriting it.

The update modes are:

  • all: no age restriction; this is the default when no update option is given. Other overwrite options still apply.
  • older: move when the destination is missing or the source is newer; otherwise skip successfully.
  • none: skip existing destinations successfully.
  • none-fail: refuse to replace an existing destination and report failure.

A backup is refused when making it would destroy the source: if the source is the file the destination's backup would be written over (for example mv --backup=simple a~ a), mv stops with an error. The check compares file identity, not how the paths are spelled.

Other options #

OptionEffect
-t, --target-directory=DIRMove every source into DIR.
-T, --no-target-directoryTreat dest as a plain file even if it is a directory.
--strip-trailing-slashesStrip any trailing slashes from each source argument.
-v, --verbosePrint each file as it is moved.
--debugExplain in detail how each file was moved. Implies -v.
--progressShow a progress bar.

Exit status #

CodeMeaning
0No error was reported. This includes successful skips; it does not mean every source was moved.
1A move failed, including refusal to replace an existing destination with --update=none-fail.

rm

Peios / Using Peios / Peiosutils / Files and directories

rm removes files. With -r, it removes directories and everything inside them.

rm [options] file...
$ rm draft.txt old-notes.txt
$ rm -r build/

Removal is not reversible — once rm removes a file, its directory entry is gone. rm has a few safety behaviours, below, but the basic rule stands: think before you confirm.

Removing directories #

rm will not remove a directory unless you say so:

OptionEffect
-r, -R, --recursiveRemove directories and all of their contents, recursively.
-d, --dirRemove a directory, but only if it is already empty.

The write-protected prompt #

By default — when you have not passed -f and rm is running interactively — rm prompts before removing a file you cannot write to:

$ rm policy.db
rm: remove write-protected regular file 'policy.db'?

The point of that prompt is to catch mistakes: a file you cannot write is one you most likely did not mean to delete.

"Write-protected" here means a real access check. rm decides it by asking the kernel whether your token may write the file — a live check against the file's security descriptor. It is not read off any decorative metadata on the inode; it is the same authority question that any write to the file would face. So the prompt is honest: if rm says a file is write-protected, it is a file the access model would genuinely stop you writing.

(The check is about write access to the file's contents. Whether you may actually remove the file is a separate question, decided by its own access right — so you can be prompted about a write-protected file and still be allowed to delete it. The prompt is a courtesy check, not the authorisation.)

Controlling the prompts #

OptionEffect
-f, --forceNever prompt. Also ignore files that do not exist instead of reporting them.
-iPrompt before every removal.
-IPrompt just once before removing more than three files or before a recursive removal. Less intrusive than -i, but still a check against a slip.
--interactive[=WHEN]Set the prompting explicitly: never, once, or always.

The root failsafe #

rm refuses to recursively remove / — the root of the file system — because doing so by accident would be catastrophic.

OptionEffect
--preserve-rootRefuse to recurse on /. This is the default; you do not need to ask for it.
--no-preserve-rootDisable the failsafe. Required, in full and unabbreviated, if you genuinely intend a recursive operation on /.

The failsafe recognises the root by identity, not by spelling: a symbolic link to /, a path such as /.. that resolves to it, and a bind mount of the root file system are all refused.

A recursive removal also refuses to be redirected while it runs. rm descends through directory handles rather than paths, never follows a symbolic link it meets on the way down, and if the operand it started from is replaced by something else mid-way it stops with "File changed while removing".

Other options #

OptionEffect
-v, --verbosePrint each file as it is removed.
--progressShow a progress bar.

rm and shred #

rm unlinks a file — it removes the name, and the space becomes free. The file's data may still be physically present on the device until something else overwrites it. When you need the contents to be genuinely unrecoverable, use shred, which overwrites the data before removing the file.

Exit status #

CodeMeaning
0Every requested removal succeeded (or, with -f, the file did not exist).
1A file could not be removed.

rmdir

Peios / Using Peios / Peiosutils / Files and directories

rmdir removes directories — but only empty ones. If a directory still has anything in it, rmdir leaves it alone and reports the failure.

rmdir [options] directory...
$ rmdir old-cache/

That refusal to touch a non-empty directory is the whole point of rmdir: it is the safe way to remove a directory you believe is empty. If it is not empty, you find out instead of losing the contents. To remove a directory together with everything inside it, use rm -r.

Options #

OptionEffect
-p, --parentsRemove the directory and then its ancestors, as long as each becomes empty in turn. rmdir -p a/b/c removes a/b/c, then a/b, then a.
--ignore-fail-on-non-emptyDo not treat "directory not empty" as a failure. Other failures are still reported. Useful when clearing out whatever directories happen to be empty and leaving the rest.
-v, --verbosePrint a line for each directory as it is removed.

Exit status #

CodeMeaning
0Every directory was removed.
1A directory could not be removed — it was not empty, did not exist, or removal was refused.

mkdir

Peios / Using Peios / Peiosutils / Files and directories

mkdir creates directories.

mkdir [options] directory...
$ mkdir reports
$ mkdir -p projects/2026/q2

Creating parent directories #

By default mkdir creates exactly the directories you name, and fails if a parent is missing. -p removes that restriction:

OptionEffect
-p, --parentsCreate any missing parent directories along the way. Also makes mkdir succeed quietly if the directory already exists.
-v, --verbosePrint a line for each directory as it is created.

The creation flags #

A new directory needs a security descriptor — the record of who owns it and who may do what to it. By default, a new directory inherits its descriptor from the directory it is created in: it picks up the access rules its parent hands down. For most directories that is exactly right, and you pass no extra options at all.

When you need the new directory to have a different descriptor, the creation flags set it explicitly. These five flags are shared, unchanged, by mkdir, mkfifo, mknod, and touch — whenever one of those commands creates a new object, it accepts this same set. This page is the full reference for them.

FlagArgumentEffect
--ownerSIDMake this principal the owner, instead of you.
--groupSIDSet the object's group.
--labellevelGive the object a mandatory integrity label.
--no-inherit—Do not inherit rules from the parent directory; lock the object to its owner.
--sddlSDDL stringSupply the entire security descriptor directly.

A SID argument is either a literal S-1-… value or a well-known alias — BA for the built-in administrators, for example.

--label takes one of these levels, lowest to highest: untrusted, low, medium, medium-plus, high, system, protected. The label applies the standard rule for files and directories — a principal below the object's level cannot write to it.

Inherited, or explicit #

The default and --no-inherit are the two ends of a choice.

  • Inherited (the default) — the new directory tracks its parent. Rules the parent hands down apply to it, and as the parent's rules change the directory follows along. This keeps a tree of directories consistent without per-directory effort.
  • --no-inherit — the new directory takes nothing from its parent. Its access is locked to its owner, and it does not follow the parent's rules. Use it to carve out a directory whose security stands apart from the tree around it.

See Inheritance for the full model.

--sddl for a complete descriptor #

The four flags above are shortcuts for common adjustments. When you need to specify the whole descriptor — a particular set of access rules, audit rules, owner, and group all at once — --sddl takes it as a single string in the security-descriptor definition language:

$ mkdir --sddl 'O:BAG:BAD:(A;OICI;FA;;;BA)' secure-area

--sddl is mutually exclusive with --owner, --group, --label, and --no-inherit — it already says everything those flags would say, so combining them is rejected.

How the descriptor is applied #

mkdir creates the directory first and then applies the descriptor to it. The two steps are not separately visible — mkdir either finishes with the directory created and secured as asked, or fails as a whole. If applying the descriptor fails, the half-created directory is not left behind.

Exit status #

CodeMeaning
0Every directory was created.
1A directory could not be created, or a creation flag could not be applied.

mkfifo

Peios / Using Peios / Peiosutils / Files and directories

mkfifo creates named pipes, also called FIFOs.

mkfifo [options] name...
$ mkfifo events

A named pipe is a file that two programs use to pass a stream of data: one writes into it, the other reads from it, and the data flows through in order — first in, first out, which is what "FIFO" stands for. Unlike an ordinary pipe between two commands, a named pipe has a name on the file system, so the two programs do not have to be started together or be related to each other.

Setting the new pipe's security #

A FIFO is a file, and a new file needs a security descriptor. By default a new FIFO inherits its descriptor from the directory it is created in.

mkfifo accepts the shared creation flags — --owner, --group, --label, --no-inherit, and --sddl — to set that descriptor explicitly instead. They behave exactly as they do for mkdir; the full reference is on the mkdir page.

Exit status #

CodeMeaning
0Every FIFO was created.
1A FIFO could not be created — for example, the name already exists.

mknod

Peios / Using Peios / Peiosutils / Files and directories

mknod creates special files — files that are not data but an interface to something else: a device, or a named pipe.

mknod [options] name type [major minor]
$ mknod /dev/mydisk b 8 0
$ mknod backlog p

The type argument #

The type says what kind of special file to create:

TypeCreates
bA block device — a device addressed in fixed-size blocks, such as a disk.
c or uA character device — a device addressed as a stream of bytes, such as a terminal.
pA named pipe (FIFO).

For a block or character device (b, c, u) you must also give a major and a minor number. The major number selects which driver handles the device; the minor number tells that driver which specific device is meant. For a pipe (p), the major and minor numbers are omitted — a pipe has no driver behind it.

A major or minor number is read as hexadecimal if it begins with 0x, as octal if it begins with 0, and as decimal otherwise.

mknod NAME p and mkfifo NAME do the same thing; mkfifo exists as the clearer way to ask for just a pipe.

Setting the new file's security #

A special file needs a security descriptor like any other file, and by default a new one inherits its descriptor from the directory it is created in.

mknod accepts the shared creation flags — --owner, --group, --label, --no-inherit, and --sddl — to set that descriptor explicitly. They behave exactly as they do for mkdir; the full reference is on the mkdir page.

Exit status #

CodeMeaning
0The special file was created.
1It could not be created — a missing or invalid type, missing device numbers, or a name that already exists.

touch

Peios / Using Peios / Peiosutils / Files and directories

touch does one of two things, depending on whether the file already exists:

  • if the file does not exist, touch creates it, empty;
  • if the file does exist, touch updates its timestamps to the current time.
touch [options] [file...]
$ touch notes.txt        # create notes.txt if absent, else bump its times

The "create an empty file" behaviour is the everyday use. The "update timestamps" behaviour matters to tools that decide what work to do by comparing file times — touch is how you tell such a tool that a file should be considered freshly changed.

Which timestamps, and to what #

By default touch sets both the access time and the modification time to the current moment. These options narrow or redirect that.

OptionEffect
-aChange only the access time.
-mChange only the modification time.
--time=WORDChoose which time to change by name: access, atime, or use for the access time; modify or mtime for the modification time.
-d, --date=STRINGUse the time described by STRING instead of the current time.
-t STAMPUse the time given as [[CC]YY]MMDDhhmm[.ss] instead of the current time.
-r, --reference=FILEUse the timestamps of FILE instead of the current time.
-h, --no-dereferenceIf the named file is a symbolic link, change the link's own timestamps rather than those of the file it points to.

Not creating a file #

To use touch purely to update timestamps, and never to create anything:

OptionEffect
-c, --no-createDo not create a file that does not exist. A named file that is absent is silently skipped.

Setting a created file's security #

When touch creates a file, that new file needs a security descriptor, and by default it inherits one from the directory it is created in.

touch accepts the shared creation flags — --owner, --group, --label, --no-inherit, and --sddl — to set that descriptor explicitly. They behave exactly as they do for mkdir; the full reference is on the mkdir page.

These flags apply only on the create path. If touch is updating the timestamps of a file that already exists, there is no new descriptor to set, and the creation flags have nothing to do — they do not alter an existing file's security. Pair them with -c and they never take effect at all, since -c means nothing is ever created.

Exit status #

CodeMeaning
0Every file was created or updated as requested.
1A file could not be created or updated.

ln

Peios / Using Peios / Peiosutils / Files and directories

ln creates links — extra ways to reach a file.

ln [options] target link_name
ln [options] target
ln [options] target... directory
ln [options] -t directory target...
$ ln -s /opt/app/bin/app /lcl/bin/app   # a symbolic link
$ ln data.csv data-archive.csv                # a hard link

Hard links and symbolic links #

There are two kinds of link, and they are genuinely different things.

A hard link is another name for the same file. The file's data exists once; a hard link is an additional directory entry pointing at it. Every hard link to a file is equal — none is "the original" — and the file's data stays as long as at least one hard link remains. Hard links cannot span file systems, and cannot be made to directories.

A symbolic link is a small file that simply contains a path. When something opens a symbolic link, it is redirected to whatever that path names. The symbolic link and its target are separate files: if the target is moved or removed, the link still exists but now points at nothing — it "dangles". Symbolic links can point anywhere, across file systems and at directories.

ln creates hard links by default, and symbolic links with -s. When in doubt, -s — a symbolic link's behaviour is the easier one to reason about.

The two also differ in how they are secured. A hard link is not a new file, so creating one involves no new security descriptor — every name for the file shares the file's existing one. A symbolic link is a new file, and like any new file it inherits its own descriptor from the directory it is created in.

The forms #

  • ln target link_name — create one link named link_name.
  • ln target — create a link to target in the current directory, with the same final name.
  • ln target... directory — create a link to each target inside directory.
  • ln -t directory target... — the same, with the directory named first.

Options #

OptionEffect
-s, --symbolicMake symbolic links instead of hard links.
-f, --forceRemove an existing destination file so the link can be created.
-i, --interactiveAsk before removing an existing destination.
-r, --relativeFor a symbolic link, write the target as a path relative to the link's own location, rather than an absolute path.
-n, --no-dereferenceIf link_name is itself a symbolic link to a directory, treat it as an ordinary file — replace the link rather than create something inside the directory it points to.
-L, --logicalWhen the target is a symbolic link, link to what it points to.
-P, --physicalMake a hard link to a symbolic link itself, rather than to its target.
-t, --target-directory=DIRCreate all links inside DIR.
-T, --no-target-directoryTreat link_name as a plain file, never as a directory to create links inside.
-b, --backup[=CONTROL]Back up an existing destination before replacing it.
-S, --suffix=SUFFIXUse SUFFIX for backup file names.
-v, --verbosePrint the name of each link as it is created.

Inspecting links afterwards #

readlink prints where a symbolic link points. The low-level link command makes a single hard link with no options, for scripts that want exactly that and nothing else.

Exit status #

CodeMeaning
0Every link was created.
1A link could not be created.

link and unlink

Peios / Using Peios / Peiosutils / Files and directories

link and unlink are the bare, single-purpose versions of two operations that ln and rm also perform. Each does exactly one thing, takes exactly its operands, and has no options. They exist for scripts that want one precise operation with no surrounding behaviour — no prompting, no link kinds to choose, no recursion.

link #

link file1 file2

link creates one hard link: a new name, file2, for the existing file file1. file1 must exist; file2 must not.

$ link data.csv data-archive.csv

That is the whole command. It performs the link operation directly and reports whether it succeeded. For anything more — symbolic links, replacing an existing destination, linking into a directory — use ln.

unlink #

unlink file

unlink removes one file: it deletes the directory entry file. It takes a single name and removes it directly.

$ unlink stale.lock

unlink does not prompt, does not recurse, and does not remove directories. For removing several files, removing directories, or any of the safety prompts, use rm.

Why they exist #

ln and rm are the commands to use day to day — they are flexible and have the safety behaviours. link and unlink are deliberately rigid: a script that calls unlink will only ever remove a single file, and a reviewer can see that at a glance. The narrowness is the feature.

Exit status #

Both commands:

CodeMeaning
0The operation succeeded.
1The operation failed — a missing or already-existing operand, or a refused request.

shred

Peios / Using Peios / Peiosutils / Files and directories

shred destroys a file's contents. It does not just unlink the file — it overwrites the data, repeatedly, so that what was there cannot be read back even with effort and specialised equipment.

shred [options] file...
$ shred -u -v secret-keys.txt

rm removes a file's name; the data may sit on the device, recoverable, until something else happens to overwrite it. shred overwrites the data on purpose. Use it when a file held something that must not be recoverable.

By default shred overwrites a file three times and then leaves the file in place — empty of its old contents but still present. That default exists because shred is often pointed at device files, which usually should not be removed. To remove the file after shredding it, pass -u.

When shredding actually works #

shred relies on one assumption: that writing to the file overwrites the same physical storage the old data occupied. On a traditional file system that holds. Several common file system designs break it — anything that writes new data to a fresh location instead of in place, keeps snapshots, journals file data, caches copies elsewhere, or compresses. On those, shred cannot guarantee the old data is gone.

Backups and remote mirrors are a separate matter entirely: shred cannot reach a copy of the file that lives somewhere else.

Treat shred as effective on a plain local file system and uncertain otherwise.

Options #

OptionEffect
-n, --iterations=NOverwrite N times instead of the default of 3.
-u, --remove[=HOW]Remove the file after overwriting it. HOW controls how thoroughly the name itself is obscured before the file is unlinked.
-z, --zeroAdd a final pass that writes zeros, so the file does not visibly look shredded afterwards.
-s, --size=NShred N bytes, rather than the whole file. Accepts size suffixes such as K, M, G.
-x, --exactDo not round the shredded size up to the next full block. Without it, shred rounds up so the block's slack space is overwritten too.
--random-source=FILETake the random bytes for the overwrite passes from FILE.
-v, --verboseShow progress as each pass runs.
-f, --forceOverride a file's protection: when a live access check shows you cannot write the file, rewrite its security descriptor so the overwrite can proceed (see below).

--force and a file's protection #

To overwrite a file, shred has to be able to write it. If the file's security descriptor does not grant you write access, an ordinary shred cannot proceed.

-f (--force) handles that case. When --force is given and a live access check shows you cannot currently write the file, shred rewrites the file's security descriptor to one that grants full access, and then overwrites the file. The reasoning is deliberate: the file is about to be destroyed anyway, and --force is you saying "override this file's protection."

--force can do this only when you are allowed to change the file's security in the first place — that is, when you own the file or hold the right to rewrite its access rules. If you cannot change the descriptor, --force cannot grant itself write access, and the shred still fails. --force overrides a file's protection; it does not manufacture authority you do not have.

Exit status #

CodeMeaning
0Every file was shredded successfully.
1A file could not be shredded.

dd

Peios / Using Peios / Peiosutils / Files and directories

dd copies data from one place to another, a block at a time, and can transform the data as it goes. It is the tool for low-level copying — writing a disk image to a device, extracting an exact byte range out of a file, copying from or to a raw device.

dd [operand]...
$ dd if=disk.img of=/dev/mydisk bs=4M status=progress

How dd is told what to do #

dd does not take options in the usual -x form. It takes operands, each written name=value, in any order. With no if= operand it reads from standard input; with no of= operand it writes to standard output.

The operands #

OperandMeaning
if=FILERead input from FILE.
of=FILEWrite output to FILE.
bs=BYTESRead and write BYTES at a time. Sets both the input and output block size at once.
ibs=BYTESInput block size (default 512).
obs=BYTESOutput block size (default 512).
cbs=BYTESConversion block size — used by the block and unblock conversions.
count=NStop after N input blocks, rather than reading to the end.
skip=NSkip N input blocks before copying. Also spelled iseek=N.
seek=NSkip N output blocks before writing. Also spelled oseek=N.
conv=LISTApply the comma-separated conversions below.
iflag=LISTComma-separated input flags — how the input is opened and read.
oflag=LISTComma-separated output flags — how the output is opened and written.
status=LEVELHow much to report: progress (periodic stats while copying), noxfer (final counts only), none (silent).

A size value accepts a unit suffix (K, M, G, …). So bs=4M is four mebibytes per block.

Conversions (conv=) #

ValueEffect
ucase / lcaseConvert the data to upper-case / lower-case.
swabSwap every adjacent pair of bytes.
syncPad each input block out to its full size — with zeros, or with spaces alongside block/unblock.
block / unblockConvert between newline-terminated lines and fixed-size cbs records.
sparseWhere an output block is all zeros, seek past it instead of writing it.
exclFail if the output file already exists.
nocreatFail if the output file does not already exist.
notruncDo not truncate the output file when opening it.
noerrorContinue past read errors instead of stopping.
fdatasync / fsyncFlush the data — or the data and metadata — to storage before finishing.

When dd creates the output file — an of= file that does not already exist — the new file needs a security descriptor, and it inherits one from the directory it is created in.

Input and output flags (iflag=, oflag=) #

FlagApplies toEffect
count_bytesinputInterpret count=N as a number of bytes, not blocks.
skip_bytesinputInterpret skip=N as a number of bytes.
fullblockinputWait for a full ibs of data on each read.
seek_bytesoutputInterpret seek=N as a number of bytes.
appendoutputOpen the output in append mode.
directbothUse direct I/O, bypassing the cache.
dsync / syncbothUse synchronised I/O — for data, or for data and metadata.
nonblockbothUse non-blocking I/O.
noatimebothDo not update the file's access time.
nocachebothAsk the system to drop the file's cached pages.
directorybothFail unless the file is a directory.

What dd prints #

Unless status=none is set, dd prints a summary when it finishes:

16+0 records in
16+0 records out
67108864 bytes (67 MB, 64 MiB) copied, 1.234 s, 54.4 MB/s

The records in / records out counts are written complete+partial — full-sized blocks plus any short final block. status=progress prints the last line periodically during a long copy.

Exit status #

CodeMeaning
0The copy completed.
1The copy failed — a bad operand, an I/O error, a file that could not be opened, or a regular output file that could not be truncated to its final length.

df

Peios / Using Peios / Peiosutils / Files and directories

df — "disk free" — reports, for each file system, how much space it has and how much is still available.

df [options] [file...]

With no arguments, df reports on every mounted file system. Given a file, it reports on the one file system that file lives on.

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       456G  198G  235G  46% /
/dev/sda1       511M   12M  499M   3% /boot

The default columns #

Each line is one file system:

  • Filesystem — the device or source backing it.
  • Size — its total capacity.
  • Used — space in use.
  • Avail — space still available.
  • Use% — used space as a percentage.
  • Mounted on — where it is attached in the directory tree.

Choosing the units #

By default sizes are shown in blocks. These options change that.

OptionEffect
-h, --human-readableShow sizes with unit suffixes — 456G, 12M — using powers of 1024.
-H, --siLikewise, but using powers of 1000.
-B, --block-size=SIZEShow sizes scaled to SIZE — -BM reports in megabytes.
-kUse 1024-byte blocks.

Choosing what to report #

OptionEffect
-a, --allInclude dummy and otherwise-hidden file systems that df would normally skip.
-l, --localReport only local file systems, skipping network-mounted ones.
-t, --type=TYPEReport only file systems of type TYPE.
-x, --exclude-type=TYPEReport everything except file systems of type TYPE.
--totalAdd a final line giving the grand total across everything reported.

Changing the output #

OptionEffect
-i, --inodesReport inode counts — total, used, and free — instead of block space. A file system can run out of inodes while it still has space.
-T, --print-typeAdd a column showing each file system's type.
--output[=LIST]Replace the default columns with exactly the fields named in LIST. With no list, print every available field.
-P, --portabilityUse a fixed, simple column layout that does not wrap a long device name onto its own line.

Sync behaviour #

OptionEffect
--syncFlush pending writes before reading the usage figures, for the most up-to-date numbers.
--no-syncDo not flush first. This is the default and is faster.

Exit status #

CodeMeaning
0The report was produced.
1A failure — for example, a named file does not exist, or the table of file systems could not be read.

du

Peios / Using Peios / Peiosutils / Files and directories

du — "disk usage" — reports how much space files and directories occupy. Where df tells you about a whole file system, du tells you where the space has actually gone.

du [options] [file...]
$ du -sh /home/jack/projects
4.2G    /home/jack/projects

With no arguments, du works on the current directory. By default it walks the directory tree, prints the cumulative size of each directory (a directory's size includes everything under it), and ends with the total for the argument itself. Individual files are not listed unless you ask for them.

What to count and how deep #

OptionEffect
-a, --allList every file, not only directories.
-s, --summarizePrint only a single total for each argument — no per-subdirectory breakdown.
-d, --max-depth=NShow totals only down to N levels below each argument. -d 0 is the same as -s.
-S, --separate-dirsGive each directory's own size, excluding its subdirectories.
-c, --totalAdd a final grand-total line.
--inodesCount inodes used rather than space used.

What "size" means #

OptionEffect
-h, --human-readableSizes with unit suffixes — 4.2G — using powers of 1024.
--siLikewise, using powers of 1000.
-B, --block-size=SIZEScale sizes to SIZE.
-k / -mUse 1024-byte / 1024×1024-byte units.
--apparent-sizeReport the apparent size of files — how much data they contain — rather than the disk space they occupy. The two differ for sparse files and because of block rounding.
-b, --bytesReport plain byte counts. Equivalent to --apparent-size --block-size=1.

Following symbolic links #

By default du does not follow symbolic links.

OptionEffect
-L, --dereferenceFollow all symbolic links and count what they point to.
-D, -H, --dereference-argsFollow only the symbolic links named directly on the command line.
-P, --no-dereferenceFollow no symbolic links. This is the default.
-x, --one-file-systemDo not cross into a different file system while walking.
-l, --count-linksCount a hard-linked file every time it is encountered, instead of once.

Limiting the output #

OptionEffect
--exclude=PATTERNSkip files and directories whose name matches PATTERN.
--exclude-from=FILESkip everything matching any pattern listed in FILE.
-t, --threshold=SIZEShow only entries at least SIZE (or, with a negative SIZE, at most that big).
--files0-from=FTake the list of files to measure from F, NUL-separated. - means standard input.
--time[=WORD]Also show a timestamp — by default the latest modification time anywhere in the directory.
-0, --nullEnd each output line with a NUL character instead of a newline.
-v, --verboseReport extra detail about what is being processed.

Exit status #

CodeMeaning
0The report was produced.
1A file or directory could not be accessed.

truncate

Peios / Using Peios / Peiosutils / Files and directories

truncate sets a file's size to an exact figure — shrinking it, or extending it.

truncate [options] file...
$ truncate -s 0 logfile        # empty the file
$ truncate -s 1G disk.img      # make a 1 GiB file

Shrinking a file discards everything past the new size. Extending it adds space that reads back as zero bytes; that added space is a hole — it costs no actual storage until something writes real data into it, which is how truncate -s 1G can create a "1 GiB file" instantly.

By default, truncate creates a file that does not yet exist (as an empty file, then sized as asked). A file created this way needs a security descriptor, and it inherits one from the directory it is created in.

Specifying the size #

-s (--size) takes the size. A plain number sets the size outright. A unit suffix scales it: K, M, G, T, … where the bare letter is a power of 1024 (K = 1024) and the letter with B is a power of 1000 (KB = 1000).

A size may also begin with a prefix, which makes it relative to the file's current size:

PrefixMeaning
+Extend by this much.
-Reduce by this much.
<Shrink to this size only if the file is currently larger ("at most").
>Extend to this size only if the file is currently smaller ("at least").
/Round the size down to a multiple of this number.
%Round the size up to a multiple of this number.
$ truncate -s +4K notes.txt    # 4 KiB larger than it is now
$ truncate -s '<1M' notes.txt  # cap it at 1 MiB, leave smaller files alone

Options #

OptionEffect
-s, --size=SIZESet or adjust the size to SIZE, as described above.
-r, --reference=RFILEBase the size on the current size of RFILE instead of giving a number. Combine with a relative -s to mean "the size of RFILE, adjusted".
-c, --no-createDo not create a file that does not exist; skip it instead.

Exit status #

CodeMeaning
0Every file was resized.
1A file could not be resized — a bad size, or a file that could not be opened.

mktemp

Peios / Using Peios / Peiosutils / Files and directories

mktemp creates a temporary file — or, with -d, a temporary directory — gives it a name that is not already taken, and prints that name.

mktemp [options] [template]
$ mktemp
/tmp/tmp.QV8x3kZ1aR
$ mktemp -d
/tmp/tmp.7bNc0wPe2L

The reason to use mktemp rather than inventing a name yourself is safety. A script that picks a fixed name like /tmp/work has two problems: two copies of the script collide, and a name an attacker can predict is a name an attacker can interfere with before the script gets to it. mktemp chooses an unpredictable name and creates the file in the same step, so nothing can slip in between. It prints the name it used, for the script to capture:

work=$(mktemp)
# ...use "$work"...
rm "$work"

The temporary file or directory mktemp creates is a new object, and a new object needs a security descriptor; it inherits one from the directory it is created in.

Templates #

The name comes from a template — a name with a run of trailing X characters, each of which mktemp replaces with a random character. More Xs means a wider range of possible names.

$ mktemp report-XXXXXX.txt
report-a8Kp2Q.txt

With no template, mktemp uses a built-in default that places the file in the temporary directory. A template needs at least three Xs.

Options #

OptionEffect
-d, --directoryCreate a directory instead of a file.
-p, --tmpdir[=DIR]Create the temporary object inside DIR. With no DIR, use the directory named by the TMPDIR environment variable, or the system temporary directory. The template is then just the final name component.
--suffix=SUFFIXAppend SUFFIX after the random part of the name — for giving the file an extension.
-u, --dry-runDo not create anything; just print a name that would be free. This reintroduces the race the command exists to avoid — use it only when you genuinely cannot create the object yet.
-q, --quietPrint no error message if creation fails; rely on the exit status.

Exit status #

CodeMeaning
0The temporary file or directory was created; its name was printed.
1Creation failed — a bad template, or a directory that does not exist.

sd

Peios / Using Peios / Peiosutils / Files and directories

sd is the command-line tool for working with security descriptors on files. Its subcommands edit owners, DACLs, SACLs, integrity labels and inheritance. The show subcommand queries only owner, group, DACL and the integrity-label view; it does not read the full SACL.

sd subcommand path [arguments]
$ sd show ./report.txt
$ sd allow ./report.txt alice:read
$ sd owner ./report.txt BA

Where ls -l shows a file's owner and a summary, and cp --sd carries selected descriptor components (owner, DACL, and SACL), sd is the tool that edits the descriptor directly.

The subcommands #

GroupSubcommandDoes
InspectshowShow owner, group, DACL and the integrity-label view on a path.
checkSimulate an access check against the path.
DACLallowAdd an allow rule for one or more principals.
denyAdd a deny rule for one or more principals.
removeDrop every DACL rule for the named principals.
AuditingauditAdd an audit rule to the SACL.
unauditDrop every SACL rule for the named principals.
OwnershipownerSet the descriptor's owner.
groupSet the descriptor's group.
IntegrityintegritySet the mandatory integrity label.
InheritanceinheritTurn inheritance protection on or off.
resetDrop the file's own rules and re-inherit from the parent.
propagatePush inheritance down to descendants.
WholesalesetWrite the supplied descriptor components; kernel merge and validation rules apply.

Naming a principal #

Wherever a subcommand takes a PRINCIPAL, it accepts any of:

FormExampleMeaning
@self@selfThe user SID of the token running sd.
@owner@ownerA placeholder that the access check substitutes with the file's own owner.
A well-known labelEveryone, Administrators, LocalSystemA named built-in principal.
A two-letter aliasWD, BA, SYThe short alias for a well-known principal.
A raw SIDS-1-5-32-544Any SID, written out in full.

See SIDs for what these are.

Naming permissions #

Wherever a subcommand takes PERMS, several notations are accepted, and may be mixed:

FormExampleMeaning
Single lettersrwx, r, mr read, w write, x execute, d delete, m modify, f full, c change-permissions, o take-ownership.
Wordsread,write, modifyThe same set, spelled out.
Fine-grained namesread-data,append,traverseIndividual low-level rights, for precise rules.
Raw hex0x1F01FFAn access mask written directly.

Run letters together (rwx) or separate names with commas (read,write,execute).

Inspecting #

sd show #

Queries the owner, group, DACL and mandatory integrity-label subset on a path. It selects OWNER | GROUP | DACL | LABEL, not the full SACL. Audit ACEs and other non-label SACL entries are outside this view, even when the caller has authority to read them.

$ sd show ./report.txt
FlagEffect
--sddlRender the queried components as SDDL.
--rawRender SIDs in raw S-1-… form only; this is not raw descriptor export.
--labelRender SIDs as their labels where known.
--allAdd the queried byte length and, in human output, the raw control value.
--jsonEmit JSON for the selected view.

These flags change formatting, not the component selection. A SACL heading or JSON key contains the selected label view, not the audit SACL. --all does not request additional components, and there is no full-SACL/export selector for show.

Human and ordinary JSON output are summaries: callback condition payloads are not serialized, and other ACE bodies are represented by their length. Matching output does not prove complete descriptor equality. --sddl still formats only the queried subset; do not use it as a full backup to replay with sd set. If audit entries or complete metadata verification are required, this command does not provide that evidence.

sd check #

Simulates an access decision: "would this access be allowed?" — without performing it.

$ sd check ./report.txt write
$ sd check ./report.txt read --pid 4821 --explain
Argument / flagEffect
PERMSThe access to test for.
--pid PIDCheck against process PID's token instead of your own.
--explainShow why the decision came out as it did — the rule-by-rule walk.

sd check is the first thing to reach for when an access is denied and you do not know why. --explain walks the descriptor the same way the kernel does.

Changing the DACL #

The DACL is the list of allow and deny rules. These three subcommands edit it.

sd allow and sd deny #

Add an allow (or deny) rule for one or more PRINCIPAL:PERMS pairs.

$ sd allow ./report.txt alice:read bob:rw
$ sd deny  ./report.txt Everyone:write
FlagEffect
--flags LISTACE inheritance flags — CI container-inherit, OI object-inherit, NP no-propagate, IO inherit-only; none clears them.
--if EXPRMake it a conditional rule, applied only when EXPR is true.
--replaceDrop any existing rules for this principal and kind first, instead of appending.
--recursive, -rApply to every descendant of the path.

The DACL is evaluated in order: the first matching ACE that decides a right wins for that right. A later deny does not undo an earlier allow, and a later allow does not undo an earlier deny. See DACL evaluation.

sd remove #

Drops every DACL rule — allow and deny — for the named principals.

$ sd remove ./report.txt bob carol
FlagEffect
--allow-emptyPermit the result to be a present-but-empty DACL, which denies everyone. Without this, sd refuses to produce one.
--recursive, -rApply to every descendant.

Auditing #

sd audit and sd unaudit #

The SACL holds audit rules — see The SACL. sd audit adds one; sd unaudit drops every SACL rule for the named principals.

$ sd audit ./secrets.db Everyone:write:failure

An audit spec is PRINCIPAL:PERMS:WHEN, where WHEN is success, failure, or both — which outcomes to log. sd audit takes the same --flags, --if, --replace, and -r options as sd allow.

Ownership #

sd owner and sd group #

Set the descriptor's owner or group SID.

$ sd owner ./report.txt alice
$ sd group ./report.txt Administrators

Both take a single PRINCIPAL and accept -r. Changing an owner is itself an access-controlled act — see Ownership.

Integrity #

sd integrity #

Sets the file's mandatory integrity label.

$ sd integrity ./report.txt high

The level is one of untrusted, low, medium, medium-plus, high, system, protected. The standard catalog is five levels (untrusted, low, medium, high, system); medium-plus (RID 8448) and protected (RID 20480) are non-standard Windows-compatibility levels — the kernel compares any S-1-16-<rid> numerically.

FlagEffect
--policy BITSThe label's policy bits, comma-separated: NW no write-up, NR no read-up, NX no execute-up.
--recursive, -rApply to every descendant.

For what the label does, see Mandatory integrity control.

Inheritance #

sd inherit #

Turns inheritance protection on or off — the + mark ls -l shows.

$ sd inherit off ./report.txt    # lock the file; stop inheriting
$ sd inherit on  ./report.txt    # let it inherit from its parent again

inherit on permits inheritance from the parent directory; inherit off protects the file while keeping its current rules. Neither creates a live link to the parent: the child stores its own descriptor, and later parent changes require explicit propagation to existing children. See Inheritance.

FlagEffect
--strip-inheritedWhen turning protection off, also drop the inherited rules already on the file.
--recursive, -rApply to every descendant.

sd reset #

Drops the file's own explicit rules and its protection, and rebuilds its DACL purely from what the parent directory hands down — returning the file to "inherits everything".

sd propagate #

Pushes this directory's inheritable rules down into its descendants, refreshing what they inherit. Use it after changing a directory's rules so the children pick up the change. Each descendant gets what it would get if it were created now: a CREATOR OWNER rule gives that descendant's own owner, and generic rights are translated into file rights.

A descendant that protects its rules (sd inherit off) is left as it is and counted in the summary, though the files below it are still refreshed from it. A descendant that can't be changed is reported, and the walk carries on.

FlagEffect
--saclPush the audit rules (SACL) down as well. Needs SeSecurityPrivilege.
--jsonReport what was pushed, protected and refused as JSON.

See Inheritance for the full model.

Replacing the whole descriptor #

sd set #

Writes the supplied descriptor components. On a file with a valid descriptor, the kernel merges selected components and selected control bits rather than replacing every descriptor byte. Missing or invalid file security descriptors use a separate privileged replacement path; see the kernel contract. Do not treat the selected output of sd show --sddl as a complete input backup.

$ sd set ./report.txt 'O:BAG:BAD:P(A;;FA;;;BA)(A;;0x1200a9;;;BU)'
Argument / flagEffect
SDDLThe new descriptor as an SDDL string. - reads it from standard input.
--binary FILEInstead of SDDL, read the raw descriptor bytes from FILE (- for standard input).
--components LISTOverride which parts of the descriptor (owner, group, DACL, SACL) the operation writes.

Common flags #

These apply across the subcommands:

FlagEffect
--recursive, -rApply the change to every descendant of the path. Implies --no-follow-symlinks.
--no-follow-symlinks, -POperate on a symbolic link itself, not the file it points to.
--jsonEmit JSON instead of human-readable output.

Recursion and symbolic links #

A recursive run stamps a tree, and a symbolic link is a node in that tree rather than a route out of it. So -r operates on link inodes and does not descend through them: the links themselves get the descriptor you asked for, and nothing outside the tree is touched. This matters on Peios more than it might elsewhere, because /usr/bin is around 170 links into a single multi-call binary — a recursive grant that followed them would stamp that one binary repeatedly and leave every command unreachable.

Name a path without -r and sd follows it, because naming a symbolic link is how you say you mean its target. If you want the link itself, pass -P.

seed-sd stamps trees the same way, so the two tools agree about what a symbolic link is.

Exit status #

CodeMeaning
0The operation succeeded — or, for sd check, the access would be allowed.
non-zeroThe operation failed, the path was unreachable, or — for sd check — the access would be denied.

Source and version scope #

The show selection and rendering described here are checked against peiosutils 0.8.18, commit 3344d469. This source review does not identify the version installed on a machine or establish a runtime-tested metadata export and restore procedure.

mkexec

Peios / Using Peios / Peiosutils / Files and directories

mkexec marks a regular file as a program, so that the kernel is willing to run it.

mkexec file...
$ mkexec ./report-tool

Why the mark exists #

On Peios, who may do what to a file is decided by the file's security descriptor. POSIX mode bits carry none of that authority, which is why Peios ships no chmod.

One part of the mode survives, with a different meaning. Before any access decision is made, the kernel refuses outright to execute a file that carries no execute bit anywhere. That refusal happens below the access-control layer, and it is not waived for administrators: an unmarked file is unrunnable for everybody, including Local System.

So on Peios the execute bit is not a permission. It is an intrinsic property of the file — a statement that the file is a program rather than data — and mkexec is how you set it.

What it does not do #

Marking a file executable grants nobody the right to execute it. That right comes from the file's security descriptor, and sd is the tool that grants it.

The two are separate steps, and a program needs both: the mark says the file is a program, the descriptor says who may run it. A file that is marked but not permitted fails the access check; a file that is permitted but not marked fails before the check is reached.

When a file arrives unmarked #

A file gets the mark when something deliberately sets it. A file written by a program that simply created it, or moved onto the system by a transport that did not carry the mark across, arrives without one — and refuses to run, usually reporting only "permission denied". mkexec is the fix.

$ ./deploy
sh: ./deploy: Permission denied
$ mkexec ./deploy
$ ./deploy

Details #

mkexec writes the mark to all three execute slots. Which slot carries it has no meaning under the Peios security model — the mark is a single property, and any slot satisfies it — so no arbitrary choice is made between them. Every other mode bit is left alone.

Marking a file that is already marked succeeds and changes nothing, so a script may mark the same file twice.

Symbolic links are followed. Marking a link marks the file it points at, which is the file that would actually be executed.

Only regular files can be marked. Directories, device nodes, and sockets are rejected: the mark means "this is a program", and nothing else can be one.

You can see the mark in the second column of ls -l, which shows x for a marked file and - for an unmarked one.

Exit status #

CodeMeaning
0Every file was marked.
1A file could not be marked — it does not exist, it is not a regular file, or its security descriptor does not permit the change.

Peios Learn — documentation for the Peios project.

Built with Trail.