# Ćwiczenia 6, grupa cz. 10-12, 24 listopada 2022
###### tags: `SO22` `ćwiczenia` `pwit`
## Deklaracje
Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie (co najwyżej jedno!) w trakcie dyskusji oznacz je znakiem ==X== na żółtym tle.
**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**
:::danger
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| ----------------------:| ----- | --- | --- | --- | --- | --- | --- |
Miriam Bouhajeb | X | X | X | X | | | X |
Kacper Chmielewski | X | X | X | X | | | X |
Jan Jankowicz | X | X | X | | | | X |
Jakub Kaczmarek | | | | | | | |
Jarosław Kadecki | X | X | | X | X | | X |
Zuzanna Kania | X | | | | | | |
Julia Konefał | X | X | | X | | | X |
Daniel Sarniak | X | X | | X | X | X | X |
Paweł Tkocz | | | | | | | |
Miłosz Urbanik | | | | | | | |
Tomasz Wołczański | X | X | X | | X | | ==X== |
Radosław Śliwiński | X | X | | X | | | |
:::
:::info
**Uwaga:** Po rozwiązaniu zadania należy zmienić kolor nagłówka na zielony.
:::
## Zadanie 1
:::success
Autor: Kacper Chmielewski

| | start | setuid(2000) | setreuid(-1, 2000) | seteuid(2000) | (setresuid(-1, 2000, 3000) |
| ---- |:-----:|:------------:|:------------------:|:-------------:|:--------------------------:|
| ruid | 1000 | 2000 | 1000 | 1000 | 1000 |
| euid | 0 | 2000 | 2000 | 2000 | 2000 |
| suid | 0 | 2000 | 2000 | 0 | 3000
- The setuid() system call sets the real and effective user IDs and the
saved set-user-ID of the current process to the specified value. The
setuid() system call is permitted if the specified ID is equal to the
real user ID or the effective user ID of the process, or if the effective
user ID is that of the super user.
- The real and effective user IDs of the current process are set according
to the arguments.
If the real user ID is changed (i.e. ruid is not -1) or the effective
user ID is changed to something other than the real user ID, then the
saved user ID will be set to the effective user ID.
- The seteuid() system call (setegid()) sets the effective user ID (group
ID) of the current process.
- The setresuid() system call sets the real, effective and saved user ID of the current process.
Privileged processes may set these IDs to arbitrary values.
Passing -1 as an argument causes the corresponding value to remain unchanged.
Proces z tożsamością ruid = 0, euid = 1000, suid = 1000, nie jest uprzywilejowany poniewąz jego euid nie jest super userem, czyli nie jest równy 0.
:::
## Zadanie 2
::: success
Autor: Radosław Śliwiński
Bity `rwx` dla katalogów pełnią następującą rolę:
* `r`(read) pozwala na pozyskanie listy wszystkich plików w katalogu,
* `w`(write) pozwala na tworzenie, zmienianie nazw oraz kasowanie plików w katalogu, jak i modyfikowanie atrybutów katalogu,
* `x`(execute) pozwala na przechodzenie po katalogu, gdy jest on w ścieżce, do której chcemy się dostać, tzn. można dzięki temu dostać się do wszystkich plików i katalogów w środku. Czasami ten bit nazywany jest search bit ze względu na swoje działanie.
Dzięki bitowi `set-gid` w katalogu, wszystkie nowo tworzone pliki i katalogi stają się własnością grupy będącej właścicielem katalogu, zwykle atrybut ten jest dziedziczony przez nowo tworzone podkatalogi.
Bit `sticky` w katalogu pozwala na usuwanie/zmienianie uprawnień tylko właścicielowi owego katalogu. Bit ten jest stosowany często w /tmp, do którego mogą mieć dostęp wszyscy użytkownicy systemu, przez co użytkownicy nie mogą usuwać plików nienależących do nich.
> The `mode` specifies the accessibility check(s) to be performed, and is either the value `F_OK`, or a mask consisting of the bitwise OR of one or more of `R_OK`, `W_OK`, and `X_OK`. `F_OK` tests for the existence of the file. `R_OK`, `W_OK`, and `X_OK` test whether the file exists and grants read, write, and execute permissions, respectively.
```c=
#define MAX_GROUPS_NUMBER 1024
bool my_access(struct stat *sb, int mode) {
// Jeżeli effective user ID to 0, mamy do czynienia
// z superuserem i przyznajemy od razu dostęp.
if (geteuid() == 0)
return true;
// Jeżeli effective user ID jest takie samo jak ID
// właściciela procesu, to dostęp jest przyznany jeśli
// odpowiedni bit dostępu użytkownika jest ustawiony,
// w przeciwnym wypadku dostęp nie jest przyznawany.
// Odpowiedni bit oznacza, że jeśli proces otwiera plik
// do czytania, to user-read musi być ustawiony, podobnie
// dla write (user-write) i uruchamiania (user-execute).
if (sb->st_uid == geteuid()) {
// Porównaj wszystkie flagi, jeśli (mode >= sb) to przyznaj
// dostęp, w przeciwnym wypadku sprawdzaj dalej warunki.
int sb_mode = sb->st_mode;
uint8_t sb_u_read = (sb_mode & S_IRUSR);
uint8_t sb_u_write = (sb_mode & S_IWUSR);
uint8_t sb_u_exec = (sb_mode & S_IXUSR);
uint8_t md_u_read = (mode & R_OK);
uint8_t md_u_write = (mode & W_OK);
uint8_t md_u_exec = (mode & X_OK);
bool access_flags[3] = {false, false, false};
// Oznaczenie x <= y w tym wypadku oznacza tyle, że y ma
// takie same lub większe uprawnienia (np. x nie wymaga
// uprawnienia czytania, ale y je posiada, więc dostęp
// w takim przypadku też zostanie przyznany).
if (sb_u_read >= md_u_read) access_flags[0] = true;
if (sb_u_write >= md_u_write) access_flags[1] = true;
if (sb_u_exec >= md_u_exec) access_flags[2] = true;
if (access_flags[0] && access_flags[1] && access_flags[2])
return true;
else
return false;
}
// Sprawdzenie, czy jedno z effective group ID lub supplementary
// group ID jest równe group ID pliku, jeżeli tak, to dostęp
// jest przyznawany, w przeciwnym przypadku odrzucany.
gid_t sb_gid = sb->st_gid;
gid_t *group;
group = (gid_t *)malloc(MAX_GROUPS_NUMBER * sizeof(gid_t));
int groups = getgroups(MAX_GROUPS_NUMBER, group);
for (int i = 0; i < groups; i++)
if (sb_gid == group[i])
return true;
// Jeśli odpowiedni bit dostępu jest ustawiony dla other,
// dostęp jest przyznany, w przeciwnym wypadku nie. Proces
// sprawdzania bardzo podobny do tego, co w przypadku drugiego if'a.
return false; // jak nie przejdą żadne testy, to nie ma dostępu
}
```
:::
## Zadanie 3
:::success
Autor: Jan Jankowicz
:::

Ponieważ plik programu 'su' ma ustawioną flagę 'set-uid', proces wykonujący ten program otrzyma na starcie efektywny identyfikator równy identyfikatorowi właściciela tego pliku. W omawianym przypadku jest to root, czyli euid tego procesu będzie wynosić 0. Rzeczywisty identyfikator pozostanie bez zmian, natomiast saved set-user ID przyjmie wartość aktualnego efektywnego identyfikatora, czyli 1000.
```c=
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/types.h>
3
4 #include <errno.h>
5 #include <grp.h>
6 #include <pwd.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include "config.h"
13 #include "passwd.h"
14 #include "util.h"
15
16 extern char **environ;
17
18 static int lflag = 0;
19 static int pflag = 0;
20
21 static int
22 dologin(struct passwd *pw)
23 {
24 char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell;
25 char *term = getenv("TERM");
26 clearenv();
27 setenv("HOME", pw->pw_dir, 1);
28 setenv("SHELL", shell, 1);
29 setenv("USER", pw->pw_name, 1);
30 setenv("LOGNAME", pw->pw_name, 1);
31 setenv("TERM", term ? term : "linux", 1);
32 if (strcmp(pw->pw_name, "root") == 0)
33 setenv("PATH", ENV_SUPATH, 1);
34 else
35 setenv("PATH", ENV_PATH, 1);
36 if (chdir(pw->pw_dir) < 0)
37 eprintf("chdir %s:", pw->pw_dir);
38 execlp(shell, shell, "-l", NULL);
39 weprintf("execlp %s:", shell);
40 return (errno == ENOENT) ? 127 : 126;
41 }
42
43 static void
44 usage(void)
45 {
46 eprintf("usage: %s [-lp] [username]\n", argv0);
47 }
48
49 int
50 main(int argc, char *argv[])
51 {
52 char *usr = "root", *pass;
53 char *shell;
54 struct passwd *pw;
55 char *newargv[2];
56 uid_t uid;
57
58 ARGBEGIN { // https://www.freebsd.org/cgi/man.cgi?query=arg&sektion=
59 case 'l':
60 lflag = 1;
61 break;
62 case 'p':
63 pflag = 1;
64 break;
65 default:
66 usage();
67 } ARGEND;
68
69 if (argc < 1)
70 ;
71 else if (argc == 1)
72 usr = argv[0]; // pobranie nazwy użytkownika, którego tożsamość chcemy przyjąć
73 else
74 usage();
75
76 errno = 0;
77 pw = getpwnam(usr); // sprawdzenie w bazie danych hasła użytkownika
78 if (!pw) {
79 if (errno)
80 eprintf("getpwnam: %s:", usr);
81 else
82 eprintf("who are you?\n");
83 }
84
85 uid = getuid();
86 if (uid) {
87 pass = getpass("Password: "); // getpass turns off echoing - bezpieczne wprowadzenie hasła
88 if (!pass)
89 eprintf("getpass:");
90 if (pw_check(pw, pass) <= 0) // sprawdzenie poprawności hasła
91 exit(1);
92 }
93
94 if (initgroups(usr, pw->pw_gid) < 0) // ustawienie grup drugoplanowych
95 eprintf("initgroups:");
96 if (setgid(pw->pw_gid) < 0) // ustawienie group id(s)
97 eprintf("setgid:");
98 if (setuid(pw->pw_uid) < 0) // ustawienie user id(s)
99 eprintf("setuid:");
100
101 if (lflag) {
102 return dologin(pw);
103 } else {
104 shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell;
105 newargv[0] = shell;
106 newargv[1] = NULL;
107 if (!pflag) {
108 setenv("HOME", pw->pw_dir, 1);
109 setenv("SHELL", shell, 1);
110 if (strcmp(pw->pw_name, "root") != 0) {
111 setenv("USER", pw->pw_name, 1);
112 setenv("LOGNAME", pw->pw_name, 1);
113 }
114 }
115 if (strcmp(pw->pw_name, "root") == 0)
116 setenv("PATH", ENV_SUPATH, 1);
117 else
118 setenv("PATH", ENV_PATH, 1);
119 execve(pflag ? getenv("SHELL") : shell,
120 newargv, environ);
121 weprintf("execve %s:", shell);
122 return (errno == ENOENT) ? 127 : 126;
123 }
124 return 0;
125 }
```
The getpwnam() function returns a pointer to a structure
containing the broken-out fields of the record in the password
database (e.g., the local password file /etc/passwd, NIS, and
LDAP) that matches the username name.
The initgroups() function initializes the group access list by
reading the group database /etc/group and using all groups of
which user is a member. The additional group group is also added
to the list.
## Zadanie 4
:::success
Autor: Julia Konefał

Program uprzywilejowany ma ustawiony bit set-user-ID (set-group-ID) przez co jego obowiązujący identyfikator ustawiany jest na identyfikator użytkownika, który go stworzył, a nie który go uruchomił.
Uprzywilejowane programy powinny być projektowane z jak najmniejszym zestawem uprawnień by zapobiegać problemom z bezpieczeństwem i żeby ograniczyć potencjalne szkody, które dany program mógłby wyrządzić.
Większość programów potrzebuje wykonać konkretne operacje (np. zapis do pliku, do którego nie ma dostępu), stąd też nie potrzebuje zestawu uprawnień superużytkownika. Wystarczy stworzyć grupę dającą odpowiednie przywileje. Zwiększy to nasze bezpieczeństwo i zmniejszy ewentualny zasięg błędów w przypadku nieprawidłowego działania.
Ponadto należy pamiętać o pozbywaniu się przywilejów jak szybko jest to możliwe. Używamy odpowiednich funkcji, gdy chcemy pozbyć się przywilejów tymczasowo (seteuid(), setegid()) lub permamentnie (setreuid(), setregid()) - należy pamiętać o zachowanym id.
Przed wywołaniem exec() powinniśmy pozbyć się niepotrzebnych przywilejów jak i zamknąć pliki, do których mieliśmy uprzywilejowany dostęp.
Należy też unikać wykonywania powłoki ani interpreterów takich jak awk bez uprzedniego pozbycia się przywilejów. Nawet jeśli nie umożliwia ona interakcji, nie da się przewidzieć ewentualnych luk bezpieczeństwa w tak złożonych programach.
Standardowy zestaw funkcji unix do implementacji programów uprzywilejowanych zawiera jedną dużą wadę. W przypadku, gdy chcemy pozwolić procesowi wykonać operację, do której uprawnienia ma tylko superużytkownik (np. zmiana czasu) musimy dać mu uprawnienia superużytkownika. Pozwalając mu także na np. wgląd i władzę nad ważnymi plikami. W ten sposób tworzymy dużą lukę bezpieczeństwa, która może zostać wykorzystana przez błąd programu, nieuwagę użytkownika lub po prostu złośliwe programy.
Zdolności dzielą przywileje superużytkownika na małe części. Tym samym chcąc upoważnić proces do zmiany czasu systemowego nie będzie on mógł przeczytać naszych kluczy prywatnych.
Zdolność:
CAP_DAC_READ_SEARCH
pozwala ominąć sprawdzanie uprawnień do odczytu pliku i odczytu i wykonywania katalogów (pozwala się przez nie swobodnie przemieszczać),
CAP_KILL
pozwala ominąć sprawdzanie uprawnień do wysyłania sygnałów,
Sygnały mogą być wysyłane do innego procesu, jeśli proces jest uprzywilejowany (zdolność CAP_KILL), rzeczywisty lub zachowany id procesu wysyłającego sygnał jest taki sam jak rzeczywisty lub zachowany id procesu docelowego. (wyjątkiem jest SIGCONT - wystarczy wtedy, że procesy należą do tej samej sesji)
:::
## Zadanie 5
:::success
Autor: Jarosław Kadecki


Podczas tworzenia nowego procesu przy użyciu fork(), nowo powstałe dziecko jest dokładną kopią rodzica wiec ma ten sam bufor I/O. Jeśli w momencie fork'a bufor jest niepusty to jego wartość może zostać wypisana dwukrotnie (np. po wywołaniu printf).


Jeśli więc istnieje możliwość takiego konfliktu, należy włączyć brak buforowania np. za pomocą setbuf(3), albo korzystać z write(2) (co jednak będzie nieefektywne), albo pamiętać o opróżnianu bufora poprzez flush() oraz dodawania znaku końca linii w przypadku buforowania liniami.
pliki terminala - buforowanie liniami (dopóki nie napotkamy ‘\n’, albo końca bufora)
pliki dyskowe - buforowanie pełne (dopóki bufor nie jest zapełniony)
stderr - niebuforowane; w przypadku obsługi błędów, chcemy je widzieć od razu
Aby poprawnie opróżnić wszystkie bufory przed zamknięciem programu, powinniśmy w procedurze obsługi sygnału SIGINT użyć funkcji tcdrain() która jest async -safe, dzięki czemu nie utracimy danych z buforów.

Do opróżnienia danych z buforów można też użyć tcflush(), jednak wtedy dane z nich zostaną odrzucone.



:::
## Zadanie 6
:::success
Autor: Daniel Sarniak

```c=
#include "csapp.h"
static noreturn void usage(int argc, char *argv[]) {
fprintf(stderr, "Usage: %s [-t times] [-l length] -s "
"[write|fwrite|fwrite-line|fwrite-full|writev]\n", argv[0]);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
int length = -1, times = -1;
bool dosync = false;
int opt;
while ((opt = getopt(argc, argv, "l:t:s")) != -1) {
if (opt == 'l')
length = atoi(optarg);
else if (opt == 't')
times = atoi(optarg);
else if (opt == 's')
dosync = true;
else
usage(argc, argv);
}
if (optind >= argc || length <= 0 || times <= 0)
usage(argc, argv);
char *choice = argv[optind];
char *line = malloc(length + 1);
memset(line, '*', length);
line[length] = '\n';
if (strcmp(choice, "write") == 0) {
for (int j = 0; j < times; j++)
for (int k = 0; k < length; k++)
write(STDOUT_FILENO, line + k, length + 1 - k);
}
if (strncmp(choice, "fwrite", 6) == 0) {
size_t size;
int mode;
void *buf;
if (strcmp(choice, "fwrite-line") == 0) {
mode = _IOLBF; //Line buffering − On output, data is written when a newline character is inserted into the stream or when the buffer is full, what so ever happens first. On Input, the buffer is filled till the next newline character when an input operation is requested and buffer is empty.
size = length + 1;
} else if (strcmp(choice, "fwrite-full") == 0) {
mode = _IOFBF; //Full buffering − On output, data is written once the buffer is full. On Input the buffer is filled when an input operation is requested and the buffer is empty.
size = getpagesize();
} else {
mode = _IONBF; //No buffering − No buffer is used. Each I/O operation is written as soon as possible. The buffer and size parameters are ignored.
size = 0;
}
/* TODO: Attach new buffer to stdout stream. */
// stdout − This is the pointer to a FILE object that identifies an open stream.
// buf − This is the user allocated buffer. If set to NULL, the function automatically allocates a buffer of the specified size.
// mode − This specifies a mode for file buffering
// size − This is the buffer size in bytes
//defines how a stream should be buffered
setvbuf(stdout, buf, mode, size);
for (int j = 0; j < times; j++)
for (int k = 0; k < length; k++)
fwrite(line + k, length + 1 - k, 1, stdout);
fflush(stdout);
free(buf);
}
if (strcmp(choice, "writev") == 0) {
int n = sysconf(_SC_IOV_MAX);
struct iovec iov[n];
/* TODO: Write file by filling in iov array and issuing writev. */
for (int k = 0; k < length; k++) {
iov[k].iov_base = line + k;
iov[k].iov_len = length + 1 - k;
}
for (int j = 0; j < times; j++)
Writev(STDOUT_FILENO, iov, length);
}
free(line);
if (dosync && !isatty(STDOUT_FILENO))
fsync(STDOUT_FILENO);
return 0;
}
```
Wydruk writeperf.sh po uzupełnieniu kodu
```
daniel@daniel-desktop:~/SO/Lista6$ ./writeperf.sh
Method: write
real 0m6,219s
user 0m0,244s
sys 0m5,968s
594c417685170dd3eb60286c0f634dc9 test
Method: fwrite
real 0m6,592s
user 0m0,372s
sys 0m6,197s
594c417685170dd3eb60286c0f634dc9 test
Method: fwrite-line
real 0m6,437s
user 0m0,544s
sys 0m5,888s
594c417685170dd3eb60286c0f634dc9 test
Method: fwrite-full
real 0m1,291s
user 0m0,152s
sys 0m1,137s
594c417685170dd3eb60286c0f634dc9 test
Method: writev
real 0m0,605s
user 0m0,000s
sys 0m0,604s
594c417685170dd3eb60286c0f634dc9 test
```
Jak widzimy `writev` działa najszybciej
Pokaz wydruku przy pomocy `writev`
```
daniel@daniel-desktop:~/SO/Lista6$ ./writeperf -t 1 -l 10 -s writev
**********
*********
********
*******
******
*****
****
***
**
*
```
```
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
99,99 8,583580 8 1000000 write
0,00 0,000160 20 8 mmap
0,00 0,000057 19 3 mprotect
0,00 0,000051 12 4 pread64
0,00 0,000042 21 2 openat
0,00 0,000034 34 1 munmap
0,00 0,000031 15 2 newfstatat
0,00 0,000024 12 2 close
0,00 0,000024 8 3 brk
0,00 0,000020 20 1 1 access
0,00 0,000014 14 1 read
0,00 0,000012 6 2 1 arch_prctl
0,00 0,000012 12 1 prlimit64
0,00 0,000012 12 1 getrandom
0,00 0,000011 11 1 rseq
0,00 0,000010 10 1 set_tid_address
0,00 0,000010 10 1 set_robust_list
0,00 0,000005 5 1 1 ioctl
0,00 0,000005 5 1 1 fsync
0,00 0,000000 0 1 execve
------ ----------- ----------- --------- --------- ----------------
100,00 8,584114 8 1000037 4 total
```
```
daniel@daniel-desktop:~/SO/Lista6$ strace -c ./writeperf -t 1000 -l 1000 -s fwrite 1>/dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100,00 8,944882 8 1000000 write
0,00 0,000006 6 1 1 fsync
0,00 0,000005 5 1 1 ioctl
0,00 0,000000 0 1 read
0,00 0,000000 0 2 close
0,00 0,000000 0 8 mmap
0,00 0,000000 0 3 mprotect
0,00 0,000000 0 1 munmap
0,00 0,000000 0 3 brk
0,00 0,000000 0 4 pread64
0,00 0,000000 0 1 1 access
0,00 0,000000 0 1 execve
0,00 0,000000 0 2 1 arch_prctl
0,00 0,000000 0 1 set_tid_address
0,00 0,000000 0 2 openat
0,00 0,000000 0 2 newfstatat
0,00 0,000000 0 1 set_robust_list
0,00 0,000000 0 1 prlimit64
0,00 0,000000 0 1 getrandom
0,00 0,000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100,00 8,944893 8 1000037 4 total
```
```
daniel@daniel-desktop:~/SO/Lista6$ strace -c ./writeperf -t 1000 -l 1000 -s fwrite-line 1>/dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100,00 9,328274 9 1000000 write
0,00 0,000010 5 2 2 ioctl
0,00 0,000010 10 1 1 fsync
0,00 0,000000 0 1 read
0,00 0,000000 0 2 close
0,00 0,000000 0 8 mmap
0,00 0,000000 0 3 mprotect
0,00 0,000000 0 1 munmap
0,00 0,000000 0 3 brk
0,00 0,000000 0 4 pread64
0,00 0,000000 0 1 1 access
0,00 0,000000 0 1 execve
0,00 0,000000 0 2 1 arch_prctl
0,00 0,000000 0 1 set_tid_address
0,00 0,000000 0 2 openat
0,00 0,000000 0 3 newfstatat
0,00 0,000000 0 1 set_robust_list
0,00 0,000000 0 1 prlimit64
0,00 0,000000 0 1 getrandom
0,00 0,000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100,00 9,328294 9 1000039 5 total
```
```
daniel@daniel-desktop:~/SO/Lista6$ strace -c ./writeperf -t 1000 -l 1000 -s fwrite-full 1>/dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100,00 0,920141 7 122437 write
0,00 0,000007 3 2 2 ioctl
0,00 0,000006 6 1 1 fsync
0,00 0,000000 0 1 read
0,00 0,000000 0 2 close
0,00 0,000000 0 8 mmap
0,00 0,000000 0 3 mprotect
0,00 0,000000 0 1 munmap
0,00 0,000000 0 3 brk
0,00 0,000000 0 4 pread64
0,00 0,000000 0 1 1 access
0,00 0,000000 0 1 execve
0,00 0,000000 0 2 1 arch_prctl
0,00 0,000000 0 1 set_tid_address
0,00 0,000000 0 2 openat
0,00 0,000000 0 3 newfstatat
0,00 0,000000 0 1 set_robust_list
0,00 0,000000 0 1 prlimit64
0,00 0,000000 0 1 getrandom
0,00 0,000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100,00 0,920154 7 122476 5 total
```
Jak widać mamy już mniej wywołań write
```
daniel@daniel-desktop:~/SO/Lista6$ strace -c ./writeperf -t 1000 -l 1000 -s writev 1>/dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
96,52 0,024319 24 1000 writev
1,85 0,000465 465 1 execve
0,46 0,000115 14 8 mmap
0,15 0,000039 13 3 mprotect
0,14 0,000036 9 4 pread64
0,13 0,000032 10 3 brk
0,11 0,000028 14 2 openat
0,10 0,000024 24 1 munmap
0,08 0,000021 10 2 newfstatat
0,07 0,000017 8 2 1 arch_prctl
0,06 0,000016 8 2 close
0,06 0,000014 14 1 1 access
0,04 0,000011 11 1 1 ioctl
0,04 0,000011 11 1 1 fsync
0,04 0,000009 9 1 read
0,04 0,000009 9 1 getrandom
0,03 0,000008 8 1 set_tid_address
0,03 0,000008 8 1 prlimit64
0,03 0,000007 7 1 set_robust_list
0,03 0,000007 7 1 rseq
------ ----------- ----------- --------- --------- ----------------
100,00 0,025196 24 1037 4 total
```
Podstawową przewagą `writev(2)` jest to, że przechowujemy dane w tablicy więc buffor jest kopiowany tylko jeden raz, co przy wielokrotnym wypisywaniu trójkąta pozwala oszczędzić bardzo dużo czasu.
:::
## Zadanie 7
:::success
Autor: Tomasz Wołczański
:::

```c=
#include "csapp.h"
static const char *uidname(uid_t uid) {
/* TODO: Something is missing here! */
struct passwd *pw = getpwuid(uid);
return pw->pw_name;
}
static const char *gidname(gid_t gid) {
/* TODO: Something is missing here! */
struct group *gr = getgrgid(gid);
return gr->gr_name;
}
static int getid(uid_t *uid_p, gid_t *gid_p, gid_t **gids_p) {
gid_t *gids = NULL;
int ngid = 2;
int groups;
/* TODO: Something is missing here! */
*uid_p = getuid();
*gid_p = getgid();
gids = malloc(ngid * sizeof(gid_t));
groups = getgroups(ngid, gids);
while (groups == -1 && errno == EINVAL) {
ngid *= 2;
gids = realloc(gids, ngid * sizeof(gid_t));
groups = getgroups(ngid, gids);
}
// alternative version
// groups = getgroups(0, gids);
// gids = malloc(groups * sizeof(gid_t));
// getgroups(groups, gids);
*gids_p = gids;
return groups;
}
int main(void) {
uid_t uid;
gid_t *gids, gid;
int groups = getid(&uid, &gid, &gids);
printf("uid=%d(%s) gid=%d(%s) ", uid, uidname(uid), gid, gidname(gid));
printf("groups=%d(%s)", gids[0], gidname(gids[0]));
for (int i = 1; i < groups; i++)
printf(",%d(%s)", gids[i], gidname(gids[i]));
putchar('\n');
free(gids);
return 0;
}
```