<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_6_-_Users._Permissions</id>
	<title>OS Lab 6 - Users. Permissions - Revizia istoricului</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_6_-_Users._Permissions"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Users._Permissions&amp;action=history"/>
	<updated>2026-11-13T20:54:58Z</updated>
	<subtitle>Istoricul versiunilor pentru această pagină din wiki</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Users._Permissions&amp;diff=8448&amp;oldid=prev</id>
		<title>Vserbu: Pagină nouă: In the last lab, we gave one process several threads. In this lab, we will look at how the kernel decides what a task is allowed to do. It compares...</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Users._Permissions&amp;diff=8448&amp;oldid=prev"/>
		<updated>2026-11-13T14:14:22Z</updated>

		<summary type="html">&lt;p&gt;Pagină nouă: In &lt;a href=&quot;/index.php/OS_Lab_5_-_Threads._Mutexes&quot; title=&quot;OS Lab 5 - Threads. Mutexes&quot;&gt;the last lab&lt;/a&gt;, we gave one process several threads. In this lab, we will look at how the kernel decides what a task is allowed to do. It compares...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;In [[OS_Lab_5_-_Threads._Mutexes|the last lab]], we gave one process several threads. In this lab, we will look at how the kernel decides what a task is allowed to do. It compares the numbers of the task, its &amp;#039;&amp;#039;&amp;#039;credentials&amp;#039;&amp;#039;&amp;#039;, with the owner, group and mode of the file. We will read these numbers, turn them into names, and change the permissions of our own files. As an application, we will write our own &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;, and a tool which finds and repairs dangerous permissions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Everything in this lab works as a normal user, and you will not need &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;. Some of the commands below fail with a permission error on purpose.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Learning objectives ==&lt;br /&gt;
&lt;br /&gt;
By the end of this lab you should be able to:&lt;br /&gt;
&lt;br /&gt;
# read the user and group IDs of a task, and turn them into names;&lt;br /&gt;
# read the owner, group and mode of a file with [https://man7.org/linux/man-pages/man2/statx.2.html &amp;lt;code&amp;gt;statx(2)&amp;lt;/code&amp;gt;], and change the mode with [https://man7.org/linux/man-pages/man2/chmod.2.html &amp;lt;code&amp;gt;chmod(2)&amp;lt;/code&amp;gt;];&lt;br /&gt;
# say which class of permission bits applies when a task uses a file;&lt;br /&gt;
# explain what read, write and execute mean for files and for directories;&lt;br /&gt;
# recognize the set-user-ID, set-group-ID and sticky bits.&lt;br /&gt;
&lt;br /&gt;
== The credentials of a task ==&lt;br /&gt;
&lt;br /&gt;
Every task has a set of numbers, which the kernel uses for every permission check. First, it has three user IDs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ID !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Real UID || Who started the task&lt;br /&gt;
|-&lt;br /&gt;
| Effective UID || Whose rights the task uses&lt;br /&gt;
|-&lt;br /&gt;
| Saved set-user-ID || One more value the task can go back to&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It also has a real GID, an effective GID and a saved set-group-ID, which work in the same way, and a list of &amp;#039;&amp;#039;&amp;#039;supplementary groups&amp;#039;&amp;#039;&amp;#039;. All of them are copied to the child by [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], and kept across &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, so everything started from a shell normally has the same credentials as the shell.&lt;br /&gt;
&lt;br /&gt;
=== getresuid(2) and getresgid(2) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);&lt;br /&gt;
int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);&lt;br /&gt;
&lt;br /&gt;
uid_t getuid(void);  // The real UID&lt;br /&gt;
uid_t geteuid(void); // The effective UID&lt;br /&gt;
gid_t getgid(void);  // The real GID&lt;br /&gt;
gid_t getegid(void); // The effective GID&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/getresuid.2.html &amp;lt;code&amp;gt;getresuid(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/getresgid.2.html &amp;lt;code&amp;gt;getresgid(2)&amp;lt;/code&amp;gt;] store the three IDs at the given addresses. The four shorter functions return a single ID, and cannot fail.&lt;br /&gt;
&lt;br /&gt;
=== getgroups(2) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
int getgroups(int size, gid_t list[]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt; || How many elements &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt; can hold&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt; || Where to store the supplementary groups&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It returns the number of groups, or -1 on error. If &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt; is 0, &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt; is not touched, and the call only returns the number of groups. This lets us first ask how many groups there are, and then declare an array of exactly that size: a &amp;#039;&amp;#039;&amp;#039;variable-length array&amp;#039;&amp;#039;&amp;#039; (VLA), whose size is only known at run time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; A VLA must have at least one element. A task may have no supplementary groups at all, so the programs below make room for one more group than needed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following program prints all the credentials of the task running it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uid_t ruid, euid, suid;&lt;br /&gt;
    gid_t rgid, egid, sgid;&lt;br /&gt;
&lt;br /&gt;
    getresuid(&amp;amp;ruid, &amp;amp;euid, &amp;amp;suid);&lt;br /&gt;
    getresgid(&amp;amp;rgid, &amp;amp;egid, &amp;amp;sgid);&lt;br /&gt;
&lt;br /&gt;
    int n = getgroups(0, nullptr);&lt;br /&gt;
    if (n == -1) {&lt;br /&gt;
        print_error(&amp;quot;getgroups&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
    gid_t groups[n + 1]; // + 1: a VLA cannot be empty&lt;br /&gt;
    n = getgroups(n, groups);&lt;br /&gt;
    if (n == -1) {&lt;br /&gt;
        print_error(&amp;quot;getgroups&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;UID: real %u, effective %u, saved %u\n&amp;quot;, ruid, euid, suid);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;GID: real %u, effective %u, saved %u\n&amp;quot;, rgid, egid, sgid);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
&lt;br /&gt;
    write(STDOUT_FILENO, &amp;quot;Groups:&amp;quot;, 7);&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot; %u&amp;quot;, groups[i]);&lt;br /&gt;
        write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    }&lt;br /&gt;
    write(STDOUT_FILENO, &amp;quot;\n&amp;quot;, 1);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
UID: real 1000, effective 1000, saved 1000&lt;br /&gt;
GID: real 1000, effective 1000, saved 1000&lt;br /&gt;
Groups: 1000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your numbers may be different. The three IDs are the same for almost every task, and only differ while special programs run, as we will see below.&lt;br /&gt;
&lt;br /&gt;
== From numbers to names ==&lt;br /&gt;
&lt;br /&gt;
The kernel does not know what users are called. Names are looked up in user space, by the C library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;pwd.h&amp;gt;&lt;br /&gt;
struct passwd *getpwuid(uid_t uid);&lt;br /&gt;
&lt;br /&gt;
// In: &amp;lt;grp.h&amp;gt;&lt;br /&gt;
struct group *getgrgid(gid_t gid);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They return a structure describing the user or group, or &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt; if nothing is known about that number. For example, a file can still belong to a user who has been deleted. A program must be ready to print the number instead.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;pwd.h&amp;gt; (simplified)&lt;br /&gt;
struct passwd {&lt;br /&gt;
    char *pw_name;  // The name of the user&lt;br /&gt;
    uid_t pw_uid;   // Its UID&lt;br /&gt;
    gid_t pw_gid;   // Its primary group&lt;br /&gt;
    char *pw_dir;   // Its home directory&lt;br /&gt;
    char *pw_shell; // Its shell&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// In: &amp;lt;grp.h&amp;gt; (simplified)&lt;br /&gt;
struct group {&lt;br /&gt;
    char *gr_name; // The name of the group&lt;br /&gt;
    gid_t gr_gid;  // Its GID&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The returned structure is kept in a static buffer, which the next call to the same function overwrites. If you need a name after another lookup, copy it first.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following program prints the same line as the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;grp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pwd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static void print_group(gid_t gid)&lt;br /&gt;
{&lt;br /&gt;
    const struct group *gr = getgrgid(gid);&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%u(%s)&amp;quot;, gid, gr != nullptr ? gr-&amp;gt;gr_name : &amp;quot;?&amp;quot;);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int n = getgroups(0, nullptr);&lt;br /&gt;
    if (n == -1) {&lt;br /&gt;
        print_error(&amp;quot;getgroups&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
    gid_t groups[n + 1]; // + 1: a VLA cannot be empty&lt;br /&gt;
    n = getgroups(n, groups);&lt;br /&gt;
    if (n == -1) {&lt;br /&gt;
        print_error(&amp;quot;getgroups&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    uid_t uid = getuid();&lt;br /&gt;
    const struct passwd *pw = getpwuid(uid);&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;uid=%u(%s) gid=&amp;quot;, uid, pw != nullptr ? pw-&amp;gt;pw_name : &amp;quot;?&amp;quot;);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    print_group(getgid());&lt;br /&gt;
&lt;br /&gt;
    write(STDOUT_FILENO, &amp;quot; groups=&amp;quot;, 8);&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
        if (i &amp;gt; 0) {&lt;br /&gt;
            write(STDOUT_FILENO, &amp;quot;,&amp;quot;, 1);&lt;br /&gt;
        }&lt;br /&gt;
        print_group(groups[i]);&lt;br /&gt;
    }&lt;br /&gt;
    write(STDOUT_FILENO, &amp;quot;\n&amp;quot;, 1);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
uid=1000(john) gid=1000(john) groups=1000(john)&lt;br /&gt;
$ id&lt;br /&gt;
uid=1000(john) gid=1000(john) groups=1000(john)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The permissions of a file ==&lt;br /&gt;
&lt;br /&gt;
Every file has an &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; (a UID), a &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; (a GID) and a &amp;#039;&amp;#039;&amp;#039;mode&amp;#039;&amp;#039;&amp;#039; of 16 bits. The mode holds the type of the file, 3 special bits, and 9 permission bits: read, write and execute, for each of the three classes.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_6_-_mode_bits.png|center|thumb|500px|The owner, group and mode of a file. The mode is usually written in octal: each digit is one group of 3 bits. The file here is &amp;lt;code&amp;gt;rwxr-x---&amp;lt;/code&amp;gt;, or 0750.]]&lt;br /&gt;
&lt;br /&gt;
When a task uses a file, the kernel picks &amp;#039;&amp;#039;&amp;#039;exactly one&amp;#039;&amp;#039;&amp;#039; class, and only checks that one:&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_6_-_open_check.png|center|thumb|500px|The first match decides. An owner with &amp;lt;code&amp;gt;---&amp;lt;/code&amp;gt; cannot read the file, even if the others can.]]&lt;br /&gt;
&lt;br /&gt;
The checks use the effective IDs. A task with effective UID 0, the superuser, skips them. In addition, every directory on the path needs the execute permission.&lt;br /&gt;
&lt;br /&gt;
The mode bits have names, defined in &amp;lt;code&amp;gt;&amp;amp;lt;sys/stat.h&amp;amp;gt;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Read !! Write !! Execute&lt;br /&gt;
|-&lt;br /&gt;
| Owner || &amp;lt;code&amp;gt;S_IRUSR&amp;lt;/code&amp;gt; (0400) || &amp;lt;code&amp;gt;S_IWUSR&amp;lt;/code&amp;gt; (0200) || &amp;lt;code&amp;gt;S_IXUSR&amp;lt;/code&amp;gt; (0100)&lt;br /&gt;
|-&lt;br /&gt;
| Group || &amp;lt;code&amp;gt;S_IRGRP&amp;lt;/code&amp;gt; (0040) || &amp;lt;code&amp;gt;S_IWGRP&amp;lt;/code&amp;gt; (0020) || &amp;lt;code&amp;gt;S_IXGRP&amp;lt;/code&amp;gt; (0010)&lt;br /&gt;
|-&lt;br /&gt;
| Others || &amp;lt;code&amp;gt;S_IROTH&amp;lt;/code&amp;gt; (0004) || &amp;lt;code&amp;gt;S_IWOTH&amp;lt;/code&amp;gt; (0002) || &amp;lt;code&amp;gt;S_IXOTH&amp;lt;/code&amp;gt; (0001)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The three special bits are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Value !! Shown as !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Set-user-ID, &amp;lt;code&amp;gt;S_ISUID&amp;lt;/code&amp;gt; || 04000 || &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of the owner&amp;#039;s &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; || The program runs with the effective UID of the owner of the file&lt;br /&gt;
|-&lt;br /&gt;
| Set-group-ID, &amp;lt;code&amp;gt;S_ISGID&amp;lt;/code&amp;gt; || 02000 || &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of the group&amp;#039;s &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; || The program runs with the effective GID of the group of the file. On a directory: new files get the group of the directory&lt;br /&gt;
|-&lt;br /&gt;
| Sticky, &amp;lt;code&amp;gt;S_ISVTX&amp;lt;/code&amp;gt; || 01000 || &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; instead of the others&amp;#039; &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; || On a directory: an entry can only be deleted or renamed by its owner, the owner of the directory, or root&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; underneath is not set, the letter is uppercase: &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== statx(2) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;fcntl.h&amp;gt; and &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dirfd&amp;lt;/code&amp;gt; || A directory to which a relative &amp;lt;code&amp;gt;pathname&amp;lt;/code&amp;gt; is relative, or &amp;lt;code&amp;gt;AT_FDCWD&amp;lt;/code&amp;gt; for the current directory&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pathname&amp;lt;/code&amp;gt; || The file&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; || 0 to follow a symbolic link and describe its target, or &amp;lt;code&amp;gt;AT_SYMLINK_NOFOLLOW&amp;lt;/code&amp;gt; to describe the link itself&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mask&amp;lt;/code&amp;gt; || Which fields we want. &amp;lt;code&amp;gt;STATX_BASIC_STATS&amp;lt;/code&amp;gt; asks for the usual ones&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;statxbuf&amp;lt;/code&amp;gt; || Where to store the result&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;sys/stat.h&amp;gt; (simplified)&lt;br /&gt;
struct statx {&lt;br /&gt;
    uint16_t stx_mode;  // The type, special bits and permission bits&lt;br /&gt;
    uint32_t stx_uid;   // The owner&lt;br /&gt;
    uint32_t stx_gid;   // The group&lt;br /&gt;
    uint32_t stx_nlink; // The number of hard links&lt;br /&gt;
    uint64_t stx_size;  // The size in bytes&lt;br /&gt;
    struct statx_timestamp stx_atime; // Last access&lt;br /&gt;
    struct statx_timestamp stx_mtime; // Last modification&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct statx_timestamp {&lt;br /&gt;
   int64_t tv_sec;    /* Seconds since the Epoch (00:00 1st Jan 1970 UTC) */&lt;br /&gt;
   uint32_t tv_nsec;  /* Nanoseconds since tv_sec */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The type is tested with macros: &amp;lt;code&amp;gt;S_ISREG(mode)&amp;lt;/code&amp;gt; (regular file), &amp;lt;code&amp;gt;S_ISDIR&amp;lt;/code&amp;gt; (directory), &amp;lt;code&amp;gt;S_ISLNK&amp;lt;/code&amp;gt; (symbolic link), &amp;lt;code&amp;gt;S_ISCHR&amp;lt;/code&amp;gt; (character device), &amp;lt;code&amp;gt;S_ISBLK&amp;lt;/code&amp;gt; (block device), &amp;lt;code&amp;gt;S_ISFIFO&amp;lt;/code&amp;gt; (pipe) and &amp;lt;code&amp;gt;S_ISSOCK&amp;lt;/code&amp;gt; (socket).&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/statx.2.html &amp;lt;code&amp;gt;statx(2)&amp;lt;/code&amp;gt;] only needs the execute permission on the directories of the path, and no permission on the file itself. The following program describes any file we can reach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;grp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pwd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        const char *usage = &amp;quot;usage: app &amp;lt;path&amp;gt;\n&amp;quot;;&lt;br /&gt;
        write(STDERR_FILENO, usage, strlen(usage));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    struct statx st;&lt;br /&gt;
    if (statx(AT_FDCWD, argv[1], 0, STATX_BASIC_STATS, &amp;amp;st) == -1) {&lt;br /&gt;
        print_error(argv[1]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const char *type = &amp;quot;other&amp;quot;;&lt;br /&gt;
    if (S_ISREG(st.stx_mode)) {&lt;br /&gt;
        type = &amp;quot;regular file&amp;quot;;&lt;br /&gt;
    } else if (S_ISDIR(st.stx_mode)) {&lt;br /&gt;
        type = &amp;quot;directory&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const struct passwd *pw = getpwuid(st.stx_uid);&lt;br /&gt;
    const struct group *gr = getgrgid(st.stx_gid);&lt;br /&gt;
&lt;br /&gt;
    char msg[512];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s, owner %u (%s), group %u (%s), mode %04o\n&amp;quot;, type,&lt;br /&gt;
             st.stx_uid, pw != nullptr ? pw-&amp;gt;pw_name : &amp;quot;?&amp;quot;,&lt;br /&gt;
             st.stx_gid, gr != nullptr ? gr-&amp;gt;gr_name : &amp;quot;?&amp;quot;,&lt;br /&gt;
             st.stx_mode &amp;amp; 07777);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app /etc/shadow&lt;br /&gt;
regular file, owner 0 (root), group 42 (shadow), mode 0640&lt;br /&gt;
$ ./build/debug/app /tmp&lt;br /&gt;
directory, owner 0 (root), group 0 (root), mode 1777&lt;br /&gt;
$ ./build/debug/app /usr/bin/passwd&lt;br /&gt;
regular file, owner 0 (root), group 0 (root), mode 4755&lt;br /&gt;
$ ./build/debug/app /nope&lt;br /&gt;
/nope: No such file or directory&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;stx_mode&amp;lt;/code&amp;gt; also holds the type of the file, and &amp;lt;code&amp;gt;&amp;amp; 07777&amp;lt;/code&amp;gt; keeps only the special and permission bits. In the examples above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; holds the password hashes, and the others class has no permissions at all.&lt;br /&gt;
* &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; can be written by anyone, but the sticky bit (the leading 1) stops users from deleting each other&amp;#039;s files.&lt;br /&gt;
* &amp;lt;code&amp;gt;/usr/bin/passwd&amp;lt;/code&amp;gt; is set-user-ID (the leading 4) and belongs to root. This is how a normal user can change their own password: the program runs with effective UID 0, and it knows who started it from the real UID.&lt;br /&gt;
&lt;br /&gt;
=== chmod(2) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
int chmod(const char *pathname, mode_t mode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/chmod.2.html &amp;lt;code&amp;gt;chmod(2)&amp;lt;/code&amp;gt;] replaces the permission and special bits of a file. Only the owner of the file, or root, may call it. The permission bits themselves do not matter: an owner with &amp;lt;code&amp;gt;---&amp;lt;/code&amp;gt; can still give itself &amp;lt;code&amp;gt;rw-&amp;lt;/code&amp;gt;. Since it replaces all the bits, changing just some of them means starting from the old mode, which [https://man7.org/linux/man-pages/man2/statx.2.html &amp;lt;code&amp;gt;statx(2)&amp;lt;/code&amp;gt;] gives us. For example, to remove the write permission of the group and of the others:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
mode_t new_mode = (st.stx_mode &amp;amp; 07777) &amp;amp; ~(S_IWGRP | S_IWOTH);&lt;br /&gt;
chmod(path, new_mode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; [https://man7.org/linux/man-pages/man2/chmod.2.html &amp;lt;code&amp;gt;chmod(2)&amp;lt;/code&amp;gt;] follows symbolic links: called on a link, it changes the file the link points to.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Trying it out ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt; command takes the same octal numbers. Try the following on your own files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo hello &amp;gt; notes.txt&lt;br /&gt;
$ chmod 044 notes.txt&lt;br /&gt;
$ cat notes.txt&lt;br /&gt;
cat: notes.txt: Permission denied&lt;br /&gt;
$ chmod 644 notes.txt&lt;br /&gt;
$ cat notes.txt&lt;br /&gt;
hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With mode 044, everybody else can read the file, but its owner cannot: only the owner class is checked. On a directory, the three permissions mean something else:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Permission !! For a directory&lt;br /&gt;
|-&lt;br /&gt;
| Read || List the names&lt;br /&gt;
|-&lt;br /&gt;
| Write || Create, delete and rename entries&lt;br /&gt;
|-&lt;br /&gt;
| Execute || Go through it, to use the names inside&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ mkdir box&lt;br /&gt;
$ echo hi &amp;gt; box/a.txt&lt;br /&gt;
$ chmod 600 box&lt;br /&gt;
$ ls box&lt;br /&gt;
a.txt&lt;br /&gt;
$ cat box/a.txt&lt;br /&gt;
cat: box/a.txt: Permission denied&lt;br /&gt;
$ chmod 300 box&lt;br /&gt;
$ ls box&lt;br /&gt;
ls: cannot open directory &amp;#039;box&amp;#039;: Permission denied&lt;br /&gt;
$ cat box/a.txt&lt;br /&gt;
hi&lt;br /&gt;
$ chmod 500 box&lt;br /&gt;
$ touch box/b.txt&lt;br /&gt;
touch: cannot touch &amp;#039;box/b.txt&amp;#039;: Permission denied&lt;br /&gt;
$ rm box/a.txt&lt;br /&gt;
rm: cannot remove &amp;#039;box/a.txt&amp;#039;: Permission denied&lt;br /&gt;
$ chmod 700 box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Without execute, we can see the names, but we cannot use them. Without read, we can use a name, if we already know it. Deleting a file needs write permission on its &amp;#039;&amp;#039;&amp;#039;directory&amp;#039;&amp;#039;&amp;#039;. The permissions of the file itself are not checked. Depending on its version, &amp;lt;code&amp;gt;ls box&amp;lt;/code&amp;gt; may also complain about each name in the first case.&lt;br /&gt;
&lt;br /&gt;
== Exercises ==&lt;br /&gt;
&lt;br /&gt;
Write each program in its own copy of the project template. As a reminder from [[OS_Lab_1_-_Linux_setup._Bash_primer._C_primer#getting-the-template|the first lab]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/oslab/lab06 &amp;amp;&amp;amp; cd ~/oslab/lab06&lt;br /&gt;
curl -OL https://wiki.dcae.pub.ro/images/9/93/Os_lab_skeleton.zip&lt;br /&gt;
unzip -d ls Os_lab_skeleton.zip&lt;br /&gt;
unzip -d doctor Os_lab_skeleton.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this lab:&lt;br /&gt;
&lt;br /&gt;
* As in the last labs, do not use the standard I/O library for output: build the text with [https://man7.org/linux/man-pages/man3/snprintf.3.html &amp;lt;code&amp;gt;snprintf(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] it.&lt;br /&gt;
* Do not run other programs: make the system calls yourself.&lt;br /&gt;
* If a file cannot be reached, print the error on standard error, and continue with the next one.&lt;br /&gt;
&lt;br /&gt;
=== A. Your own ls -l ===&lt;br /&gt;
&lt;br /&gt;
Write a command which lists a directory like &amp;lt;code&amp;gt;ls -la&amp;lt;/code&amp;gt;. It takes the directory as an optional parameter, and uses the current directory if there is none. Build it in these steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Names and sizes.&amp;#039;&amp;#039;&amp;#039; Read the directory with [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;], as in [[OS_Lab_3_-_Files._Memory_mapping#readdir()|Lab 3]], and call [https://man7.org/linux/man-pages/man2/statx.2.html &amp;lt;code&amp;gt;statx(2)&amp;lt;/code&amp;gt;] for each entry. Print the size and the name.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;The mode.&amp;#039;&amp;#039;&amp;#039; Print the mode as 10 characters: the type (&amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt;), then the 9 permission bits. Write a function which fills in a string.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;The other columns.&amp;#039;&amp;#039;&amp;#039; Add the number of hard links, the owner, the group and the date. If a number has no name, print the number. For a symbolic link, also print &amp;lt;code&amp;gt;-&amp;gt; target&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;The special bits.&amp;#039;&amp;#039;&amp;#039; Show &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; for the set-user-ID and set-group-ID bits, as described [[#The permissions of a file|above]]. You can ignore the sticky bit.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;What can I do?&amp;#039;&amp;#039;&amp;#039; Add a first column with your own rights over the file, such as &amp;lt;code&amp;gt;r-x&amp;lt;/code&amp;gt;, by picking the class as the kernel does: compare with your effective UID, then with your effective GID and supplementary groups, or else use the others class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
rwx drwxr-xr-x   1 john     john           44 2026-11-09 15:54 .&lt;br /&gt;
rwx drwxr-xr-x   1 john     john           50 2026-11-09 15:54 ..&lt;br /&gt;
rw- -rw-r-S---   1 john     john            6 2026-11-09 15:54 notes.txt&lt;br /&gt;
rwx lrwxrwxrwx   1 john     john            9 2026-11-09 15:54 link -&amp;gt; notes.txt&lt;br /&gt;
rwx drwxr-x--x   1 john     john            0 2026-11-09 15:54 sub&lt;br /&gt;
rwx -rwsr-xr-x   1 john     john        68064 2026-11-09 15:54 tool&lt;br /&gt;
$ ./build/debug/app /etc | grep shadow&lt;br /&gt;
--- -rw-r-----   1 root     shadow        840 2026-11-09 15:54 shadow&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The names do not have to be sorted. The widths of the columns are up to you.&lt;br /&gt;
&lt;br /&gt;
* You do not need to build a path for each entry: give [https://man7.org/linux/man-pages/man2/statx.2.html &amp;lt;code&amp;gt;statx(2)&amp;lt;/code&amp;gt;] the directory through &amp;lt;code&amp;gt;dirfd(dir)&amp;lt;/code&amp;gt;, which returns the file descriptor of an open &amp;lt;code&amp;gt;DIR&amp;lt;/code&amp;gt;, and the name of the entry as &amp;lt;code&amp;gt;pathname&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;AT_SYMLINK_NOFOLLOW&amp;lt;/code&amp;gt;, so that links are listed as links.&lt;br /&gt;
* &amp;lt;code&amp;gt;readlinkat(dirfd(dir), name, buffer, size)&amp;lt;/code&amp;gt; reads the target of a link. It returns the length, and does not add a &amp;lt;code&amp;gt;&amp;#039;\0&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* For the date: &amp;lt;code&amp;gt;time_t when = st.stx_mtime.tv_sec;&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;localtime_r(&amp;amp;when, &amp;amp;tm)&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;strftime(date, sizeof date, &amp;quot;%Y-%m-%d %H:%M&amp;quot;, &amp;amp;tm)&amp;lt;/code&amp;gt;, from &amp;lt;code&amp;gt;&amp;amp;lt;time.h&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* [https://man7.org/linux/man-pages/man3/getpwuid.3.html &amp;lt;code&amp;gt;getpwuid(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man3/getgrgid.3.html &amp;lt;code&amp;gt;getgrgid(3)&amp;lt;/code&amp;gt;] use different buffers, but copy the names anyway if you want to look up anything else before printing them.&lt;br /&gt;
&lt;br /&gt;
After step 4, compare your output with the real &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== B. Permission doctor ===&lt;br /&gt;
&lt;br /&gt;
Write a command which walks a directory and everything below it, and reports dangerous permissions. With &amp;lt;code&amp;gt;--fix&amp;lt;/code&amp;gt;, it also repairs them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
./build/debug/app [--fix] &amp;lt;directory&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It checks only regular files and directories, including the starting directory, against three rules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Applies to !! Problem !! Repair&lt;br /&gt;
|-&lt;br /&gt;
| Regular files and directories || Writable by others, except for directories with the sticky bit || Remove the write permission of the others&lt;br /&gt;
|-&lt;br /&gt;
| Regular files whose name ends in &amp;lt;code&amp;gt;.key&amp;lt;/code&amp;gt; || Any permission for the group or the others || Remove all of them&lt;br /&gt;
|-&lt;br /&gt;
| Regular files || Set-user-ID or set-group-ID || Remove those bits&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Create a test directory inside the project:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p demo/sub demo/locked&lt;br /&gt;
echo hello &amp;gt; demo/notes.txt&lt;br /&gt;
echo hello &amp;gt; demo/shared.txt &amp;amp;&amp;amp; chmod 666 demo/shared.txt&lt;br /&gt;
echo secret &amp;gt; demo/sub/server.key &amp;amp;&amp;amp; chmod 644 demo/sub/server.key&lt;br /&gt;
cp /bin/true demo/sub/tool &amp;amp;&amp;amp; chmod 4755 demo/sub/tool&lt;br /&gt;
chmod 777 demo/sub&lt;br /&gt;
ln -s /etc/shadow demo/link&lt;br /&gt;
chmod 000 demo/locked&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app demo&lt;br /&gt;
demo/sub: writable by others (0777)&lt;br /&gt;
demo/sub/server.key: private key open to group or others (0644)&lt;br /&gt;
demo/sub/tool: runs with the rights of its owner or group (4755)&lt;br /&gt;
demo/locked: Permission denied&lt;br /&gt;
demo/shared.txt: writable by others (0666)&lt;br /&gt;
problems found: 4, fixed: 0&lt;br /&gt;
$ ./build/debug/app --fix demo&lt;br /&gt;
demo/sub: writable by others (0777)&lt;br /&gt;
  fixed: 0777 -&amp;gt; 0775&lt;br /&gt;
demo/sub/server.key: private key open to group or others (0644)&lt;br /&gt;
  fixed: 0644 -&amp;gt; 0600&lt;br /&gt;
demo/sub/tool: runs with the rights of its owner or group (4755)&lt;br /&gt;
  fixed: 4755 -&amp;gt; 0755&lt;br /&gt;
demo/locked: Permission denied&lt;br /&gt;
demo/shared.txt: writable by others (0666)&lt;br /&gt;
  fixed: 0666 -&amp;gt; 0664&lt;br /&gt;
problems found: 4, fixed: 4&lt;br /&gt;
$ ./build/debug/app demo&lt;br /&gt;
demo/locked: Permission denied&lt;br /&gt;
problems found: 0, fixed: 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The lines may come in a different order. The exit status is 0 if every problem found was fixed, and 1 otherwise.&lt;br /&gt;
&lt;br /&gt;
* Write a recursive function which takes the path of a directory. Build the path of each entry with [https://man7.org/linux/man-pages/man3/snprintf.3.html &amp;lt;code&amp;gt;snprintf(3)&amp;lt;/code&amp;gt;], into a buffer of &amp;lt;code&amp;gt;PATH_MAX&amp;lt;/code&amp;gt; bytes (from &amp;lt;code&amp;gt;&amp;amp;lt;limits.h&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Skip &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt;, or the walk will never end.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;AT_SYMLINK_NOFOLLOW&amp;lt;/code&amp;gt;, and leave symbolic links alone.&lt;br /&gt;
* Set &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt; to 0 before &amp;#039;&amp;#039;&amp;#039;each&amp;#039;&amp;#039;&amp;#039; call to [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;]. The recursive calls change it.&lt;br /&gt;
* A file can break several rules. Work out the new mode from all of them, and call [https://man7.org/linux/man-pages/man2/chmod.2.html &amp;lt;code&amp;gt;chmod(2)&amp;lt;/code&amp;gt;] once.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The set-user-ID bit on &amp;lt;code&amp;gt;demo/sub/tool&amp;lt;/code&amp;gt; is harmless here, because that program is a copy of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;. Never set it on a shell or on anything which runs commands: whoever starts the program would get your rights.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some things to look at:&lt;br /&gt;
&lt;br /&gt;
* Run &amp;lt;code&amp;gt;./build/debug/app --fix /usr/bin&amp;lt;/code&amp;gt;. Why does it fail for every file?&lt;br /&gt;
* Why is &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; not reported, even though everybody can write to it?&lt;br /&gt;
* &amp;lt;code&amp;gt;demo/link&amp;lt;/code&amp;gt; has mode 0777. What would &amp;lt;code&amp;gt;--fix&amp;lt;/code&amp;gt; try to do if the program treated it like a regular file? Why would that fail here, and what would happen if the link pointed to a file of yours?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>