Zadania
-
chmod.c
: przygotuj uproszczoną implementację programuchmod
, która może zostać wywołana przez użytkownika w następującej formie./chmod <wyrażenie> <plik1> <plik2> … <plikn>
, gdzie:-
plik1
…plikn
to pliki, które mają otrzymać wybrane uprawnienia -
wyrażenie
to wyrażenie, które określa jak należy zmienic uprawnienia, odpowiadające następującemu wyrażeniu regularnemu[ugoa][=-+][rwx]+
, gdzie:-
u
oznacza uprawnienia przypisane użytkownikowi,g
grupie,o
pozostałym -
+
oznacza dodanie uprawnień do istniejącego zbioru,-
usunięcie uprawnień z istniejącego zbioru,=
nadpisanie uprawnień -
r
oznacza prawo do odczytu,w
do zapisu,x
do wykonania
-
-
implementacja wykorzystuje zdefiniowane przez osobę studencką flagi bitowe przy pomocy typu wyliczeniowego!
-
po zastosowaniu uprawnień wypisywane jest podsumowanie aktualnego stanu uprawnień
-
errno
errno
jest globalną zmienną wykorzystywaną do przechowywania kodów błędów zwracanych przez funkcje biblioteki standardowej C oraz przez funkcje realizujące wywoływanie wywołań systemowych.
Dostępna jest w pliku nagłówkowym <errno.h>
.
$ cat example.c
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main()
{
FILE *f = fopen("/path/to/file/that/doesnt/exists", "r");
if (f) {
printf("File exists\n");
fclose(f);
} else {
printf("errno = %d\n", errno);
printf("strerror(errno) = %s\n", strerror(errno));
perror("perror(...)");
}
return 0;
}
$ cc -o example example.c
$ ./example
errno = 2
strerror(errno) = No such file or directory
perror(...): No such file or directory
Komenda errno -l
pozwala na wypisanie możliwych wartości zmiennej errno
(niestety nie dostępna na wydziale).
Każdy wątek posiada własną zmienną errno
.
$ errno -l | head -n5
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
Uprawienia w systemie Linux
System Linux kontroluje dostęp do plików przez system uprawnień.
Dla każdego pliku możemy kontrolować możliwość zapisu (w
), odczytu (r
) i wykonania (x
) oraz kto daną możliwość ma: użytkownik, do którego należy dany plik (u
), grupa do której należy dany plik (g
) oraz pozostali użytkownicy systemu (o
).
Aby zmienić uprawnienia pliku używamy komendy chmod
, aby zmienić użytkownika lub grupę posiadającą plik chown
, aby wypisać uprawnienia możemy użyć komendy stat
lub ls -l
.
$ touch a
$ ls -l a
-rw-r--r-- 1 anon anon 0 kwi 14 15:18 a
$ stat a
File: a
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 0,34 Inode: 581 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ anon) Gid: ( 1000/ anon)
Access: 2025-04-14 15:18:45.974560933 +0200
Modify: 2025-04-14 15:18:45.974560933 +0200
Change: 2025-04-14 15:18:45.974560933 +0200
Birth: 2025-04-14 15:18:45.974560933 +0200
$ ./a
bash: ./a: Permission denied
$ chmod u+x a
$ ls -l a
-rwxr--r-- 1 anon anon 0 kwi 14 15:18 a
$ ./a
$ cat a
$ chmod u-rwx a
$ cat a
cat: a: Permission denied
$ ls -l a
----r--r-- 1 anon anon 0 kwi 14 15:18 a
$ rm a
rm: remove write-protected regular empty file 'a'? y
Funkcja chmod
Plik nagłówkowy: <sys/stat.h>
, dokumentacja: chmod(2)
int chmod(const char *pathname, mode_t mode);
Przykład użycia: nadania uprawnień 644
plikowi test.c
(odczyt i zapis dla użytkownika, odczyt dla grupy i pozostałych):
#include <stdio.h>
#include <sys/stat.h>
int main()
{
if (chmod("test.c", 0644) < 0) {
perror("chmod");
return 1;
}
return 0;
}
Funkcja stat
Plik nagłówkowy: <sys/stat.h>
, dokumentacja: stat(2), stat(3type)
struct stat {
mode_t st_mode;
...
};
int stat(const char *restrict pathname, struct stat *restrict statbuf);
Przykład odczytu uprawnień pliku test.c
:
#include <stdio.h>
#include <sys/stat.h>
int main()
{
struct stat s;
if (stat("test.c", &s) < 0) {
perror("stat");
return 1;
}
printf("test.c perm decimal: %d\n", s.st_mode);
printf("test.c perm octal: %o\n", s.st_mode);
printf("test.c perm binary: ");
for (int i = 3 * 3 - 1; i >= 0; --i) {
printf("%d", (s.st_mode & (1 << i)) > 0 ? 1 : 0);
}
printf("\n");
return 0;
}
Typy wyliczeniowe (enum
)
Typ wyliczeniowy pozwala na stworzenie dedykowanego typu liczbowego dla zbioru stałych (zazwyczaj jednego rodzaju).
Przykład:
#include <stdio.h>
#include <assert.h>
enum weekday
{
MONDAY = 1,
TUESDAY, /* = 2, domyślnie ma wartość poprzednią + 1 */
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
MIN_WEEKDAY = MONDAY,
MAX_WEEKDAY = SUNDAY,
};
char const* pretty(enum weekday wd)
{
switch (wd) {
case MONDAY: return "poniedziałek";
case TUESDAY: return "wtorek";
case WEDNESDAY: return "środa";
case THURSDAY: return "czwartek";
case FRIDAY: return "piątek";
case SATURDAY: return "sobota";
case SUNDAY: return "niedziela";
}
static_assert(MAX_WEEKDAY - MIN_WEEKDAY == 6, "Number of days in a week has changed");
}
int main()
{
enum weekday wd;
do {
printf("Wybierz dzień tygodnia: ");
scanf("%d", &wd);
} while (wd < MIN_WEEKDAY || wd > MAX_WEEKDAY);
printf("Wybrano dzień tygodnia: %s\n", pretty(wd));
return 0;
}
Flagi bitowe
Przykład:
#include <stdio.h>
#include <assert.h>
enum when
{
ON_MONDAY = 0b0000001,
ON_TUESDAY = 0b0000010,
ON_WEDNESDAY = 0b0000100,
ON_THURSDAY = 0b0001000,
ON_FRIDAY = 0b0010000,
ON_SATURDAY = 0b0100000,
ON_SUNDAY = 0b1000000,
ON_WORKDAY = ON_MONDAY | ON_TUESDAY | ON_WEDNESDAY | ON_THURSDAY | ON_FRIDAY,
ON_WEEKEND = ON_SATURDAY | ON_SUNDAY,
EVERYDAY = ON_WORKDAY | ON_WEEKEND,
};
struct todo
{
enum when when;
char const *what;
};
struct todo todos[] = {
{ .when = EVERYDAY, .what = "Drink water" },
{ .when = ON_MONDAY, .what = "Go to the gym" },
{ .when = ON_WEEKEND, .what = "Relax" },
};
int main()
{
enum when today = ON_MONDAY;
for (int i = 0; i < sizeof(todos) / sizeof(*todos); ++i) {
struct todo todo = todos[i];
if ((todo.when & today) == today) {
printf("%s\n", todo.what);
}
}
}
X-makra
Pozwalają na ułatwienie definicji funkcji wykorzystujących wyliczenia:
#include <stdio.h>
#include <assert.h>
#define WEEKDAY_ENUM(X) \
X(MONDAY, 1, "poniedziałek") \
X(TUESDAY, 2, "wtorek") \
X(WEDNESDAY, 3, "środa") \
X(THURSDAY, 4, "czwartek") \
X(FRIDAY, 5, "piątek") \
X(SATURDAY, 6, "sobota") \
X(SUNDAY, 7, "niedziela")
enum weekday
{
#define X(label, number, name) label = number,
WEEKDAY_ENUM(X)
#undef X
MIN_WEEKDAY = MONDAY,
MAX_WEEKDAY = SUNDAY,
};
char const* pretty(enum weekday wd)
{
switch (wd) {
#define X(label, number, name) case label: return name;
WEEKDAY_ENUM(X)
#undef X
}
static_assert(MAX_WEEKDAY - MIN_WEEKDAY == 6, "Number of days in a week has changed");
}
int main()
{
enum weekday wd;
do {
printf("Wybierz dzień tygodnia: ");
scanf("%d", &wd);
} while (wd < MIN_WEEKDAY || wd > MAX_WEEKDAY);
printf("Wybrano dzień tygodnia: %s\n", pretty(wd));
return 0;
}
Wyniki ankiety
Stan z 2025-04-14 14:50: odpowiedziały dwie osoby.
Należy powtórzyć (lista uporządkowana wg priorytetu):
-
wyliczenia
-
proces kompilacji i linkowania
-
tablice dynamiczne
-
struktury i unie
-
struktury danych
Na zajęciach brakuje:
-
przykładowych rozwiązań zadań domowych i ich omówienia na zajęciach.
-
szkieletu rozwiązania zadania domowego, szczególnie dla większych zadań (stos, BST, lista)
-
omówienia funkcji, które mamy wykorzystać w zadaniach
-
wskazówki jak korzystać z tak zaawanasowanej dokumentacji jak en.cppreference.com
-
brak notatek z zagadnień związanych z architekturą komputera (np. w formie linków do dalszych materiałów)
Oceny formy zajęć: pozytywne, podkreślono wartość dodatkowego kontekstu dawanego przez osobę prowadzącą