---
# System prepended metadata

title: 'Ćwiczenia 6, grupa cz. 10-12, 24 listopada 2022'
tags: [SO22, pwit, ćwiczenia]

---

# Ć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
![](https://i.imgur.com/HS3gras.png)

|      | 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	
:::
![](https://i.imgur.com/mU91xlD.png)
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ł
![](https://i.imgur.com/65U2JOC.png)


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	
![](https://i.imgur.com/GdsV94J.png)

![](https://i.imgur.com/qOdRi1f.png)


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).

![](https://i.imgur.com/zvWXrdr.png)
![](https://i.imgur.com/Um8WQ4m.png)

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.

![](https://i.imgur.com/svd0nFi.png)

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

![](https://i.imgur.com/gLY472d.png)


![](https://i.imgur.com/UoLVafj.png)
![](https://i.imgur.com/YAHuyAJ.png)



:::

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

![](https://i.imgur.com/HQRhUWT.png)

```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	
:::
![](https://i.imgur.com/dcdMWFd.png)

```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;
}
```