devtype = "char";
else if (S_ISBLK(sbuf.st_mode))
devtype = "block";
else {
fprintf(stderr, "%s is not a block or character device\n",
argv[1]);
exit(1);
}
printf("%s: major: %d, minor: %d\n", devtype,
major(sbuf.st_rdev), minor(sbuf.st_rdev));
exit(0);
}
Вот что происходит при запуске программы:
$ <b>ch05-devnum /tmp </b>/* Попробовать не устройство */
/tmp is not a block or character device
$ <b>ch05-devnum /dev/null</b> /* Символьное устройство */
char: major: 1, minor: 3
$ <b>ch05-devnum /dev/hda2</b> /* Блочное устройство */
block: major: 3, minor: 2
К счастью, вывод согласуется с выводом
ls
, давая нам уверенность
[59], что мы в самом деле написали правильный код.
Воспроизведение вывода ls замечательно и хорошо, но действительно ли это полезно? Ответ — да. Любое приложение, работающее с иерархиями файлов, должно быть способно различать различные типы файлов. Подумайте об архиваторе, таком как
tar
или
cpio
. Было бы пагубно, если бы такая программа рассматривала файл дискового устройства как обычный файл, пытаясь прочесть его и сохранить его содержимое в архиве! Или подумайте о
find
, которая может выполнять произвольные действия, основываясь на типе и других атрибутах файлов, с которыми она сталкивается, (
find
является сложной программой; посмотрите
find(1), если вы с ней не знакомы.) Или даже нечто простое, как пакет, оценивающий свободное дисковое пространство, тоже должно отличать обычные файлы от всего остального.
5.4.4.2. Возвращаясь к V7
cat
В разделе 4.4.4 «Пример: Unix cat» мы обещали вернуться к программе V7
cat
, чтобы посмотреть, как она использует системный вызов
stat()
. Первая группа строк, использовавшая ее, была такой:
31 fstat(fileno(stdout), &statb);
32 statb.st_mode &= S_IFMT;
33 if (statb.st_mode != S_IFCHR && statb.st_mode != S_IFBLK) {
34 dev = statb.st_dev;
35 ino = statb.st_ino;
36 }
Этот код теперь должен иметь смысл. В строке 31 вызывается
fstat()
для стандартного вывода, чтобы заполнить структуру
statb
. Строка 32 отбрасывает всю информацию в
statb.st_mode
за исключением типа файла, используя логическое AND с маской
S_IFMT
. Строка 33 проверяет, что используемый для стандартного вывода файл не является файлом устройства. В таком случае программа сохраняет номера устройства и индекса в
dev
и
ino
. Эти значения затем проверяются для каждого входного файла в строках 50–56.
50 fstat(fileno(fi), &statb);
51 if (statb.st_dev == dev && statb.st_ino == ino) {
52 fprintf(stderr, "cat: input %s is output\n",
53 ffig ? "-" : *argv);
54 fclose(fi);
55 continue;
56 }
Если значения
st_dev
и
st_ino
входного файла совпадают с соответствующими значениями выходного файла,
cat
выдает сообщение и продолжает со следующего файла, указанного в командной строке.
Проверка сделана безусловно, хотя
dev
и
ino
устанавливаются, лишь если вывод не является файлом устройства. Это срабатывает нормально из-за того, как эти переменные объявлены:
int dev, ino = -1;
Поскольку
ino
инициализирован значением (-1), ни один действительный номер индекса не будет ему соответствовать
[60]. То, что
dev
не инициализирован так, является небрежным, но не представляет проблемы, поскольку тест в строке 51 требует, чтобы были равными значения как устройства, так и индекса. (Хороший компилятор выдаст предупреждение, что
dev
используется без инициализации: '
gcc -Wall
' сделает это.)
Обратите также внимание, что ни один вызов
fstat()
не проверяется на ошибки. Это также небрежность, хотя не такая большая, маловероятно, что
fstat()
завершится неудачей с действительным дескриптором файла
Проверка того, что входной файл не равен выходному файлу, осуществляется лишь для файлов, не являющихся устройствами. Это дает возможность использовать
cat
для копирования ввода из файлов устройств в самих себя, как в случае с терминалами:
$ <b>tty</b> /* Вывести имя устройства текущего терминала */
/dev/pts/3
$ <b>cat /dev/pts/3 > /dev/pts/3</b> /* Копировать ввод от клавиатуры на экран */
<b>this is a line of text</b> /* Набираемое в строке */
this is a line of text /* cat это повторяет */
5.4.5. Работа с символическими ссылками
В общем, символические ссылки ведут себя подобно прямым ссылкам; файловые операции, такие, как
open()
и
stat()
, применяются к указываемому файлу вместо самой символической ссылки. Однако, бывают моменты, когда в самом деле необходимо работать с символической ссылкой вместо файла, на которую она указывает.
По этой причине существует системный вызов
lstat()
. Он действует точно также, как
stat()
, но если проверяемый файл окажется символической ссылкой, возвращаемые сведения относятся к символической ссылке, а не к указываемому файлу. А именно: