Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект Лекций ПСРВ.doc
Скачиваний:
81
Добавлен:
12.02.2016
Размер:
2.73 Mб
Скачать

5.13. Функции базового ввода/вывода

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

5.13.1. Открытие файла

Для открытия или создания файла используется функция:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open(const char *path, int oflag [,mode_t mode]);

Функции open()позволяют процессу открыть файл (устройство), имя которого указано в path. Это имя может быть как абсолютным (полным, начинающимся с корневого каталога /), так и относительным (указанным относительно текущего каталога). Для открытого файла создается новый дескриптор, который не разделяется с каким-либо другим процессом в системе. Режим доступа к открытому файлу устанавливается согласно значению флагов, сформированных в разрядах аргумента oflag.

Значение oflag является целым значением и строится с использованием операции поразрядного "ИЛИ" над значениями флагов, символические константы для которых определены в заголовочном файле <fcntl.h>. Флаги позволяют определить режим открытия и доступа к существующему или вновь создаваемому файлу, а также уточнить реализацию выбранного режима доступа. Значения и семантика флагов следующая:

O_RDONLY - открыть существующий файл только для чтения;

O_RDWR - открыть для чтения и записи;

O_WRONLY - открыть только для записи.

Выбранный режим доступа к открываемому файлу уточняется флагами:

O_APPEND - если этот флаг установлен, то для режимов, допускающих запись в файл (O_RDWR и O_WRONLY), перед каждой операцией записи указатель файла будет устанавливаться в конец этого файла.

O_DSYNC - если этот флаг установлен, то каждый запрос write() будет ждать, пока все данные не будут успешно записаны. То есть все записи в файл, а также соответствующие им изменения в метаданных файла будут сохранены на внешнем носителе.

Ядро кэширует данные, считываемые или записываемые на внешний носитель, для ускорения этих операций. Обычно запись данных в файл ограничивается только записью в буферный кэш ядра операционной системы, данные из которого впоследствии записываются на внешний носитель. По умолчанию возврат из функции write() происходит после записи данных в буферный кэш, не дожидаясь записи на внешний носитель. Установка флага O_DSYNC гарантирует, что в результате завершения write() даже при фатальных нарушениях в системе (но исправности внешнего носителя) данные будут сохранены в файле и могут быть прочитаны при последующем открытии файла. Если носитель оборудован защитой от записи, то это интерпретируется, как поломка носителя, данные не будут записаны, даже если write() указывает, что все успешно.

O_RSYNC - если этот флаг установлен, то каждый запрос read() завершается только тогда, когда данные успешно прочитаны.

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

O_CLOEXEC - дескриптор файла не будет наследоватся вновь создаваемыми процессами (будет закрыт при запуске процесса).

O_CREAT - установка этого флага указывает, что если открываемый файл с указанным именем не существует, то необходимо создать новый файл и открыть его для записи. Если файл с указанным именем уже существует, то будет ли открыт этот файл, или создан новый файл для записи, или функция open() завершится с ошибкой определяется значением флага O_EXCL.

O_EXCL - флаг используется совместно с O_CREAT. При его установке новый файл будет создан только в том случае, если файл с указанным именем не существует и это же действие с тем же именем файла в данный момент не выполняется другими процессам. В противном случае возвращается ошибка создания файла. Если флаг не установлен, то при наличии файла с указанным именем флаг O_CREAT игнорируется и открывается существующий файл. Флаг O_EXCL без O_CREAT также игнорируется.

O_LARGEFILE - разрешает, чтобы указатель файла был длиной в 64 бита (при работе с огромными файлами).

O_NOCTTY - если этот флаг установлен и указанный файл является терминальным устройством, то функция open() не делает терминальное устройство управляющим терминалом для процесса.

O_NONBLOCK - если этот флаг установлен, то функция open() завершается без ожидания, когда устройство будет готовым или доступным. Последующее поведение устройства определяется его спецификой. Если флаг очищен, то функция open() ждет, прежде чем завершиться, когда устройство будет готовым или доступным. Готовность устройства определяется его спецификой. В некоторых случаях реакция на флаг не определена.

O_REALIDS - требует использовать реальные UID и GID процесса для проверки разрешенных прав доступа к файлу.

O_TRUNC - если файл существует, является обычным файлом и он успешно открыт в режиме O_WRONLY или O_RDWR, то длина файла усекается до нуля, а права доступа и владелец оставлены неизменными. Флаг не имеет никакого эффекта для канала FIFO, файлов устройств или каталогов. Если файл открывается как O_RDONLY, флаг O_TRUNC игнорируется.

Аргумент mode имеет значение только когда создается новый файл и позволяет задать для файла права доступа пользователей.

Значение mode является целым значением и строится с использованием операции поразрядного "ИЛИ" над значениями флагов, специфицирующих права доступа к вновь создаваемому файлу.

Устанавливаемые права доступа и дополнительные атрибуты файла (SUID, SGID и Sticky bit) определяются следующими символическими значениями флагов (определены в заголовочном файле <sys/stat.h>):

Флаг

Значение

S_ISUID

Установить для файла бит атрибута SUID

S_ISGID

Установить для файла бит атрибута SGID или установить обязательное блокирование файла (определяется в зависимости от значений других флагов)

S_ISVTX

Установить Sticky bit

S_IRWXU

Установить право на чтение, запись и выполнение для владельца

S_IRUSR

Установить право на чтение для владельца

S_IWUSR

Установить право на запись для владельца

S_IXUSR

Установить право на выполнение для владельца

S_IRWXG

Установить право на чтение, запись и выполнение для группы

S_IRGRP

Установить право на чтение для группы

S_IWGRP

Установить право на запись для группы

S_IXGRP

Установить право на выполнение для группы

S_IRWXO

Установить право на чтение, запись и выполнение для остальных

S_IROTH

Установить право на чтение для остальных

S_IWOTH

Установить право на запись для остальных

S_IXOTH

Установить право на выполнение для остальных

Атрибуты SUID и SGID имеют смысл только для исполняемых файлов. Назначение атрибутов заключается в следующем. При запуске дочернего процесса (на основе исполняемого файла) его идентификаторы UID, GID, EUID, EGID устанавливаются равными соответствующим идентификаторам родительского процесса. Если же для исполняемого файла установлен атрибут SUID, то идентификаторы дочернего процесса будут установлены равными идентификатору владельца исполняемого файла. Атрибут SGID исполняемого файла делает то же с групповым идентификатором процесса. То есть, если существует, например, файл утилиты myprog, владельцем которого является пользователь с именем "user1", с правами доступа на выполнение файла для всех пользователей, то процесс, созданный при запуске утилиты myprog пользователем "user2", будет иметь UID и EUID унаследованные не от "user2", как следовало бы ожидать, а равные значениям UID и EUID владельца файла утилиты myprog - "user2". Не трудно догадаться, что установка для исполняемого файла атрибутов SUID и SGID не безобидна с точки зрения безопасности информации. Но иногда без них нельзя обойтись, пример – для файла утилиты passwd, позволяющей пользователю изменить свой пароль. Изменение пароля требует изменения содержимого системных файлов /etc/passwd и /etc/shadow. Понятно, что предоставление права на запись в эти файлы всем пользователям системы является отнюдь не лучшим решением. С другой стороны необходимо, чтобы процесс, запущенный на основе исполняемого файла утилиты passwd, был согласован по правам доступа с файлами /etc/passwd и /etc/shadow. Установка атрибута SUID исполняемому файлу утилиты /usr/bin/passwd позволяет изящно разрешить это противоречие. Поскольку владельцем файла утилиты /usr/bin/passwd является пользователь root (системный администратор), то кто бы ни запустил утилиту passwd на выполнение, соответствующий процесс приобретает права системного администратора, т.е. может производить запись в системные файлы, защищенные от остальных пользователей. Понятно, что требование по безопасности для программ, подобных passwd, должны быть повышены за счет ограничения их функциональных возможностей. Они не должны выполнять никаких операций, способствующих наследованию прав доступа другими процессами (например, вызов других программ).

Значение флага S_ISGID зависит от того, установлено или нет право на выполнение для группы - S_IXGRP. В первом случае он будет означать установку SGID, а во втором - обязательное блокирование файла.

Блокирование файлов позволяет устранить возможность конфликта, когда более одного процесса одновременно работают с одним и тем же файлом. Файловая система разрешает нескольким процессам одновременный доступ к файлу для чтения и записи. Хотя операции записи и чтения, осуществляемые с помощью системных вызовов read() и write(), являются атомарными, по умолчанию отсутствует синхронизация между отдельными вызовами. Другими словами, между двумя последовательными вызовами read() одного процесса другой процесс может модифицировать данные файла. Это, в частности, может привести к несогласованным операциям с файлом, и как следствие, к нарушению целостности его данных.

Если создается новый файл, то идентификатор владельца файла устанавливается равным эффективному идентификатору владельца процесса, выполняющего функцию open() (UID=EUID), соответственно идентификатор группы устанавливается равным эффективному идентификатору группы процесса (GID=EGID) или идентификатору группы родительского каталога (в котором создается файл), если для родительского каталога установлен флаг SGID.

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

Замечание. При запуске программы для неё автоматически создаются три дескриптора: стандартный ввод – 0, стандартный вывод – 1, стандартный вывод сообщений об ошибках – 2.

Частным случаем функции open() является функция:

#include <fcntl.h>

int creat(const char *pathname, mode_t mode]);

Эта функция создает и открывает новый файл с именем, указанным в pathname, и правами доступа, заданными в mode. Она эквивалентна вызову:

open("myfile.dat",O_WRONLY|O_CREAT|O_TRUNC,mode);

При успешном выполнении функция creat() возвращает дескриптор файла, в случае ошибки возвращается -1 и устанавливает errno.

Пример:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdlib.h>

 

int main( void ) {

int fd;

 

/* создать и открыть файл для записи*/

/* заменить существующий файл*/

/* создать с правами чтения/записи для владельца */

 

fd=open("myfile.dat",O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);

 

/* открыть существующий файл для чтения */

 

fd = open("myfile.dat", O_RDONLY);

 

/* открыть существующий файл для дозаписи или создать новый файл для записи, если такой файл не существует, с правами доступа на чтение/запись для всех*/

 

fd = open("myfile.dat", O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);

returnEXIT_SUCCESS;

}