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…pliknto pliki, które mają otrzymać wybrane uprawnienia -
wyrażenieto wyrażenie, które określa jak należy zmienic uprawnienia, odpowiadające następującemu wyrażeniu regularnemu[ugoa][=-+][rwx]+, gdzie:-
uoznacza uprawnienia przypisane użytkownikowi,ggrupie,opozostałym -
+oznacza dodanie uprawnień do istniejącego zbioru,-usunięcie uprawnień z istniejącego zbioru,=nadpisanie uprawnień -
roznacza prawo do odczytu,wdo zapisu,xdo 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ą