Операционная система UNIX - Андрей Робачевский 19 стр.


В качестве примера приведем упрощенную версию утилиты cp(1), копирующую один файл в другой с использованием отображения файла в память.

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <unistd.h>

#include <fcntl.h>

main(int argc, char *argv[]) {

int fd_src, fd_dst;

caddr_t addr_src, addr_dst;

struct stat filestat;

/* Первый аргумент - исходный файл, второй - целевой */

fd_dst=open(argv[2], O_RDWR | O_CREAT);

/* Определим размер исходного файла */

fstat(fd_src, &filestat);

/* Сделаем размер целевого файла равным исходному */

lseek(fd_dst, filestat.st_size - 1, SEEK_SET);

/* Зададим отображение */

addr_src=mmap((caddr_t)0, filestat.st_size,

PROT_READ, MAP_SHARED, fd_src, 0);

addr_dst=mmap((caddr_t)0, filestat.st_size,

PROT_READ | PROT_WRITE, MAP_SHARED, fd_dst, 0);

/* Копируем области памяти */

memcpy(addr_dst, addr_src, filestat.st_size);

exit(0);

}

Поскольку, как обсуждалось выше, с помощью вызова mmap(2) нельзя изменить размер файла, это было сделано с помощью вызова lseek(2) с последующей записью одного байта так, что размер целевого файла стал равным размеру исходного. При этом в целевом файле образуется "дыра", которая, к счастью, сразу же заполняется содержимым копируемого файла.

Владение файлами

Владелец-пользователь и владелец-группа файла могут быть изменены с помощью системных вызовов chown(2), fchown(2) и lchown(2):

#include <unistd.h>

#include <sys/types.h>

int chown(const char *path, uid_t owner, gid_t group);

int fchown(int fildes, uid_t owner, gid_t group);

int lchown(const char *path, uid_t owner, gid_t group);

Все три вызова работают одинаково за исключением ситуации, когда адресуемый файл является символической связью. В последнем случае вызов lchown(2) действует на сам файл - символическую связь, а не на целевой файл (т.е. не следует символической связи). В функциях chown(2) и lchown(2) файл адресуется по имени, а в fchown(2) - по файловому дескриптору. Если значение owner или group установлено равным -1, соответствующий владелец файла не изменяется.

В версиях BSD UNIX только суперпользователь может изменить владение файлом. Это ограничение призвано, в первую очередь, не допустить "скрытие" файлов под именем другого пользователя, например, при установке квотирования ресурсов файловой системы. Владельца-группу можно изменить только для файлов, которыми вы владеете, причем им может стать одна из групп, членом которой вы являетесь. Эти же ограничения определены и стандартом POSIX.1.

В системах ветви System V эти ограничения являются конфигурируемыми, и в общем случае в UNIX System V пользователь может изменить владельца собственных файлов.

В случае успешного изменения владельцев файла биты SUID и SGID сбрасываются, если процесс, вызвавший chown(2) не обладает правами суперпользователя.

Права доступа

Как уже обсуждалось в предыдущей главе, каждый процесс имеет четыре пользовательских идентификатора - UID, GID, EUID и EGID. В то время как UID и GID определяют реального владельца процесса, EUID и EGID определяют права доступа процесса к файлам в процессе выполнения. В общем случае реальные и эффективные идентификаторы эквивалентны. Это значит, что процесс имеет те же привилегии, что и пользователь, запустивший его. Однако, как уже обсуждалось выше, возникают ситуации, когда процесс должен получить дополнительные привилегии, чаще всего - привилегии суперпользователя. Это достигается установкой битов SUID и SGID. Примером такого процесса может служить утилита passwd(1), изменяющая пароль пользователя.

Права доступа к файлу могут быть изменены с помощью системных вызовов chmod(2) и fchmod(2):

#include <sys/types.h>

#include <sys/stat.h>

int chmod(const char *path, mode_t mode);

int fchmod(int fildes, mode_t mode);

Значение аргумента mode определяет устанавливаемые права доступа и дополнительные атрибуты (такие как SUID, SGID и Sticky bit), и создается путем логического объединения различных флагов, представленных в табл. 2.14. Вторая колонка таблицы содержит восьмеричные значения для девяти битов прав доступа (чтение, запись и выполнение для трех классов доступа) и трех битов дополнительных атрибутов.

Таблица 2.14. Флаги аргумента mode

ФлагБитыЗначение
S_ISUID04000Установить бит SUID
S_ISGID020#0Установить бит SGID, если # равно 7, 5, 3 или 1. Установить обязательное блокирование файла, если # равно 6, 4, 2 или 0
S_ISVTX01000Установить Sticky bit
S_IRWXU00700Установить право на чтение, запись и выполнение для владельца-пользователя
S_IRUSR00400Установить право на чтение для владельца-пользователя
S_IWUSR00200Установить право на запись для владельца-пользователя
S_IXUSR00100Установить право на выполнение для владельца-пользователя
S_IRWXG00070Установить право на чтение, запись и выполнение для владельца-группы
S_IRGRP00040Установить право на чтение для владельца-группы
S_IWGRP00020Установить право на запись для владельца-группы
S_IXGRP00010Установить право на выполнение для владельца-группы
S_IRWXO00007Установить право на чтение, запись и выполнение для остальных пользователей
S_IROTH00004Установить право на чтение для остальных пользователей
S_IWOTH00002Установить право на запись для остальных пользователей
S_IXOTH00001Установить право на выполнение для остальных пользователей

Некоторые флаги, представленные в таблице, уже являются объединением нескольких флагов. Так, например, флаг S_RWXU эквивалентен S_IRUSR | S_IWUSR | S_IXUSR. Значение флага S_ISGID зависит от того, установлено или нет право на выполнение для группы (S_IXGRP). В первом случае, он будет означать установку SGID, а во втором - обязательное блокирование файла.

Для иллюстрации приведем небольшую программу, создающую файл с полными правами доступа для владельца, а затем изменяющую их. После каждой установки прав доступа в программе вызывается библиотечная функция system(3S), позволяющая запустить утилиту ls(1) и отобразить изменение прав доступа и дополнительных атрибутов.

#include <sys/types.h>

#include <sys/stat.h>

#include <stdlib.h>

main() {

int fd;

/* Создадим файл с правами rwx------ */

fd = creat("my_file", S_IRUSR | S_IWUSR | S_IXUSR);

system("ls -l my_file");

/*Добавим флаг SUID */

fchmod(fd, S_IRWXU | S_ISUID);

/* Установим блокирование записей файла */

fchmod(fd, S_IRWXU | S_ISUID | S_ISGID);

system("ls -l my_file");

/* Теперь установим флаг SGID */

fchmod(fd, S_IRWXU | S_ISUID | S_ISGID | S_IXGRP);

system("ls -l my_file");

}

В результате запуска программы на выполнение, получим следующий вывод:

$ a.out

-rwx------ 1 andy user 0 Jan 6 19:28 my_file

-rws------ 1 andy user 0 Jan 6 19:28 my_file

-rws--1--- 1 andy user 0 Jan 6 19:28 my_file

-rws--s--- 1 andy user 0 Jan 6 19:28 my_file

Перемещение по файловой системе

Каждый процесс имеет два атрибута, связанных с файловой системой - корневой каталог (root directory) и текущий рабочий каталог (current working directory). Когда некоторый файл адресуется по имени (например, в системных вызовах open(2), creat(2) или readlink(2)), ядро системы производит поиск файла, начиная с корневого каталога, если имя файла задано как абсолютное, либо текущего каталога, если имя файла является относительным. Абсолютное имя файла начинается с символа '/', обозначающего корневой каталог. Все остальные имена файлов являются относительными. Например, имя /usr/bin/sh является абсолютным, в то время как mydir/test1.c или ../andy/mydir/test1.c - относительным, при котором фактическое расположение файла в файловой системе зависит от текущего каталога.

Процесс может изменить свой корневой каталог с помощью системного вызова chroot(2) или fchroot(2).

#include <unistd.h>

int chroot (const char *path);

int fchroot(int fildes);

После этого поиск всех адресуемых файлов с абсолютными именами будет производиться, начиная с нового каталога, указанного аргументом path. Например, после изменения корневого каталога на домашний каталог пользователя абсолютное имя инициализационного скрипта .profile станет /.profile.

Изменение корневого каталога может потребоваться, например, при распаковке архива, созданного с абсолютными именами файла, в другом месте файловой системы, либо при работе над большим программным проектом, затрагивающим существенную часть корневой файловой системы. В этом случае для отладочной версии удобно создать собственную корневую иерархию.

Процесс также может изменить и текущий каталог. Для этого используются системные вызовы chdir(2) или fchdir(2):

#include <unistd.h>

int chdir(const char* path);

int fchdir(int fildes);

Например, внутренняя команда командного интерпретатора cd может быть реализована следующим кодом:

...

char newdir[PATH_MAX];

...

/* Предположим, что имя нового каталога,

введенного пользователем, уже находится

в переменной newdir*/

if (chdir(newdir) == -1) perror("sh: cd");

...

Метаданные файла

Как уже говорилось, каждый файл помимо собственно данных содержит метаданные, описывающие его характеристики, например, владельцев, права доступа, тип и размер файла, а также содержащие указатели на фактическое расположение данных файла. Метаданные файла хранятся в структуре inode. Часть полей этой структуры могут быть получены с помощью системных вызовов stat(2):

#include <sys/types.h>

#include <sys/stat.h>

int stat(const char *path, struct stat *buf);

int lstat (const char *path, struct stat *buf);

int fstat(int fildes, struct stat *buf);

В качестве аргумента функции принимают имя файла или файловый дескриптор (fstat(2)) и возвращают заполненные поля структуры stat, которые приведены в табл. 2.15.

Таблица 2.15. Поля структуры stat

ПолеЗначение
mode_t st_modeТип файла и права доступа
ino_t st_inoНомер inode. Поля st_ino и st_dev однозначно определяют обычные файлы
dev_t st_devНомер устройства, на котором расположен файл (номер устройства файловой системы)
dev_t st_rdevДля специального файла устройства содержит номер устройства, адресуемого этим файлом
nlink_t st_linkЧисло жестких связей
uid_t st_uidИдентификатор пользователя-владельца файла
gid_t st_gidИдентификатор группы-владельца файла
off_t st_sizeРазмер файла в байтах. Для специальных файлов устройств это поле не определено
time_t st_atimeВремя последнего доступа к файлу
time_t st_mtimeВремя последней модификации данных файла
time_t st_ctimeВремя последней модификации метаданных файла
long st_blksizeОптимальный размер блока для операций ввода/вывода. Для специальных файлов устройств и каналов это поле не определено
long st_blocksЧисло размещенных 512-байтовых блоков хранения данных. Для специальных файлов устройств это поле не определено

Для определения типа файла служат следующие макроопределения, описанные в файле <sys/stat.h>:

Таблица 2.16. Определение типа файла

МакроопределениеТип файла
S_ISFIFO(mode)FIFO
S_ISCHR(mode)Специальный файл символьного устройства
S_ISDIR(mode)Каталог
S_ISBLK(mode)Специальный файл блочного устройства
S_ISREG(mode)Обычный файл
S_ISLNK(mode)Символическая связь
S_ISSOCK(mode)Сокет

Все значения времени, связанные с файлом (время доступа, модификации данных и метаданных) хранятся в секундах, прошедших с 0 часов 1 января 1970 года. Заметим, что информация о времени создания файла отсутствует.

Приведенная ниже программа выводит информацию о файле, имя которого передается ей в качестве аргумента:

#include <sys/types.h>

#include <sys/stat.h>

#include <time.h>

main(int argc, char *argv[]) {

struct stat s;

char* ptype;

lstat(argv[1] , &s); /* Определим тип файла */

if (S_ISREG(s.st_mode)) ptype = "Обычный файл";

else if (S_ISDIR(s.st_mode)) ptype = "Каталог";

else if (S_ISLNK(s.st_mode)) ptype = "Симв. Связь";

else if (S_ISCHR(s.st_mode)) ptype = "Симв. Устройство";

else if (S_ISBLK(s.st_mode)) ptype = "Бл.устройство";

else if (S_ISSOCK(s.st_mode)) ptype = "Сокет";

else if (S_ISFIFO(s.st_mode)) ptype = "FIFO";

else ptype = "Неизвестный тип";

/* Выведем информацию о файле */

/* Его тип */

printf("type = %s\n", ptype);

/* Права доступа */

printf("perm =%o\n", s.st_mode & S_IAMB);

/* Номер inode */

printf("inode = %d\n", s.st_ino);

/* Число связей */

printf("nlink = %d\n", s.st_nlink);

/* Устройство, на котором хранятся данные файла */

printf("dev = (%d, %d)\n", major(s.st_dev), minor(s.st_dev));

/* Владельцы файла */

printf("UID = %d\n", s.st_uid);

printf("GID = %d\n", s.st_gid);

/* Для специальных файлов устройств - номера устройства */

printf("rdev = (%d, %d)\n", major(s.st_rdev),

minor(s.st_rdev));

/* Размер файла */

Назад Дальше