Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

Linux программирование в примерах - Арнольд Роббинс

Читать онлайн Linux программирование в примерах - Арнольд Роббинс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 60 61 62 63 64 65 66 67 68 ... 165
Перейти на страницу:

8.4.1. Смена каталога: chdir() и fchdir()

В разделе 1.2 «Модель процессов Linux/Unix» мы говорили:

Текущим каталогом является каталог, относительно которого отсчитываются относительные пути (те, которые не начинаются с /). Это каталог, «в» котором вы находитесь, когда даете оболочке команду 'cd некоторое_место'.

У каждого процесса есть текущий рабочий каталог. Каждый новый процесс наследует свой текущий каталог от процесса, который его запустил (своего родителя). Две функции позволяют перейти в другой каталог:

#include <unistd.h>

int chdir(const char *path); /* POSIX */

int fchdir(int fd); /* XSI */

Функция chdir() принимает строку с названием каталога, тогда как fchdir() ожидает дескриптор файла, который был открыт для каталога с помощью open().[83] Обе возвращают 0 при успехе и -1 при ошибке (с errno, установленной соответствующим образом). Обычно, если open() для каталога завершается успешно, fchdir() также достигает цели, если кто-то не изменил права доступа к каталогу между вызовами, (fchdir() сравнительно новая функция; на старых системах Unix ее нет.)

Использование этих функций почти тривиально. Следующая программа, ch08-chdir.c, демонстрирует обе функции. Она демонстрирует также, что fchdir() может потерпеть неудачу, если права доступа открытого каталога не включают доступа на поиск (исполнение).

1  /* ch08-chdir.c --- демонстрация chdir() и fchdir().

2     Для краткости проверка ошибок опущена */

3

4  #include <stdio.h>

5  #include <fcntl.h>

6  #include <unistd.h>

7  #include <sys/types.h>

8  #include <sys/stat.h>

9

10 int main(void)

11 {

12  int fd;

13  struct stat sbuf;

14

15  fd = open(".", O_RDONLY); /* открыть каталог для чтения */

16  fstat(fd, &sbuf); /* получить сведения, нужны начальные права доступа */

17  chdir(".."); /* 'cd ..' */

18  fchmod(fd, 0); /* отменить права доступа каталога */

19

20  if (fchdir(fd) < 0) /* попытаться выполнить 'cd' обратно, должно завершиться неудачей */

21   perror("fchdxr back");

22

23  fchmod(fd, sbuf.st_mode & 07777); /* восстановить первоначальные права доступа */

24  close(fd); /* все сделано */

25

26  return 0;

27 }

Строка 15 открывает текущий каталог. Строка 16 вызывает fstat() для открытого каталога, так что мы получаем копию его прав доступа. Строка 17 использует chdir() для перемещения на один уровень в иерархии файлов. Строка 18 выполняет грязную работу, отменяя все права доступа первоначального каталога.

Строки 20–21 пытаются перейти обратно в первоначальный каталог. Ожидается, что эта попытка будет безуспешной, поскольку текущие права доступа не позволяют это. Строка 23 восстанавливает первоначальные права доступа, 'sbuf.st_mode & 07777' получает младшие 12 битов прав доступа; это обычные 9 битов rwxrwxrwx и биты setuid, setgid и «липкий» бит, которые мы обсудим в главе 11 «Права доступа и ID пользователя и группы». Наконец, строка 24 заканчивает работу, закрывая открытый дескриптор файла. Вот что происходит при запуске программы.

$ ls -ld . /* Показать текущие права доступа */

drwxr-xr-x 2 arnold devel 4096 Sep 9 16:42 .

$ ch08-chdir /* Запустить программу */

fchdir back: Permission denied /* Ожидаемая неудача */

$ ls -ld . /* Снова посмотреть на права доступа */

drwxr-xr-x 2 arnold devel 4096 Sep 9 16:42 /* Все восстановлено как раньше */

8.4.2. Получение текущего каталога: getcwd()

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

#include <unistd.h> /* POSIX */

char *getcwd(char *buf, size_t size);

Функция заносит в buf путь; ожидается, что размер buf равен size байтам. При успешном завершении функция возвращает свой первый аргумент. В противном случае, если требуется более size байтов, она возвращает NULL и устанавливает в errno ЕRANGE. Смысл в том, что если случится ERANGE, следует попытаться выделить буфер большего размера (с помощью malloc() или realloc()) и попытаться снова.

Если любой из компонентов каталога, ведущих к текущему каталогу, не допускает чтения или поиска, getcwd() может завершиться неудачей, а errno будет установлен в EACCESS. Следующая простая программа демонстрирует ее использование:

/* ch08-getcwd.c --- демонстрация getcwd().

Проверка ошибок для краткости опущена */

#include <stdio.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

int main(void) {

 char buf[PATH_MAX];

 char *cp;

 cp = getcwd(buf, sizeof(buf));

 printf("Current dir: %sn", buf);

 printf("Changing to ..n");

 chdir(".."); /* 'cd ..' */

 cp = getcwd(buf, sizeof(buf));

 printf("Current dir is now: %sn", buf);

 return 0;

}

Эта простая программа выводит текущий каталог, переходит в родительский каталог, затем выводит новый текущий каталог. (Переменная cp здесь на самом деле не нужна, но в реальной программе она была бы использована для проверки ошибок). При запуске программа создает следующий вывод:

$ ch08-getcwd

Current dir: /home/arnold/work/prenhall/progex/code/ch08

Changing to ..

Current dir is now: /home/arnold/work/prenhall/progex/code

Формально, если аргумент buf равен NULL, поведение getcwd() не определено. В данном случае версия GLIBC getcwd() вызовет malloc() за вас, выделяя буфер с размером size. Идя даже дальше, если size равен 0, выделяется «достаточно большой» буфер для вмещения возвращенного имени пути. В любом случае вы должны вызвать для возвращенного указателя free() после завершения работы с буфером.

Поведение GLIBC полезно, но не переносимо. Для кода, который должен работать на разных платформах, вы можете написать замещающую функцию, которая предоставляет те же самые возможности, в то же время заставив ее непосредственно вызывать getcwd() на системе с GLIBC.

Системы GNU/Linux предоставляют файл /proc/self/cwd. Этот файл является символической ссылкой на текущий каталог:

$ cd /tmp /* Сменить каталог */

$ ls -l /рroc/self/cwd /* Посмотреть на файл */

lrwxrwxrwx 1 arnold devel 0 Sep 9 17:29 /proc/self/cwd -> /tmp

$ cd /* Перейти в домашний каталог */

$ ls -l /proc/self/cwd /* Снова посмотреть на него */

lrwxrwxrwx 1 arnold devel 0 Sep 9 17:30 /proc/self/cwd -> /home/arnold

Это удобно на уровне оболочки, но представляет проблему на уровне программирования. В частности, размер файла равен нулю! (Это потому, что это файл в /proc, который продуцирует ядро; это не настоящий файл, находящийся на диске.)

Почему нулевой размер является проблемой? Если вы помните из раздела 5.4.5 «Работа с символическими ссылками», lstat() для символической ссылки возвращает в поле st_size структуры struct stat число символов в имени связанного файла. Это число может затем использоваться для выделения буфера соответствующего размера для использования с readlink(). Здесь это не будет работать, поскольку размер равен нулю. Вам придется использовать (или выделять) буфер, который, как вы полагаете, достаточно большой. Однако, поскольку readlink() не выдает символов больше, чем вы предоставили места, невозможно сказать, достаточен буфер или нет; readlink() не завершается неудачей, когда недостаточно места. (См. в разделе 5.4.5 «Работа с символическими ссылками» функцию Coreutils xreadlink(), которая решает проблему.)

В дополнение к getcwd() GLIBC имеет несколько других непереносимых процедур. Они избавляют вас от хлопот по управлению буферами и обеспечивают совместимость со старыми системами BSD. Подробности см в getcwd(3).

8.4.3. Перемещение по иерархии: nftw()

Обычной задачей программирования является обработка целых иерархий каталогов: выполнение действий над каждым файлом и каждым каталогом и подкаталогом в целостном дереве. Рассмотрите, например, команду du, которая выводит сведения об использовании диска, 'chown -R', которая рекурсивно изменяет владельцев, или программу find, которая находит файлы, подходящие по определенным критериям.

На данный момент вы знаете достаточно, чтобы написать собственный код для открывания и чтения каталогов вручную, вызова stat() (или lstat()) для каждого элемента и рекурсивной обработки подкаталогов. Однако, такой код трудно сделать правильным; можно выйти за пределы дескрипторов файлов, если вы будете оставлять родительские каталоги открытыми при обработке подкаталогов; нужно решить, обрабатывать ли символические ссылки как таковые или как файлы, на которые они указывают; нужно суметь справиться с каталогами, которые недоступны для чтения или поиска и т.д. Также трудно писать один и тот же код снова и снова, когда он требуется в нескольких приложениях.

1 ... 60 61 62 63 64 65 66 67 68 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии