Наличие дыр означает, что номинальный размер файла может быть больше, чем занимаемый им объем дискового пространства (иногда существенно больше). Запись байтов в середину дыры сократит объем свободного дискового пространства, поскольку для заполнения дыры ядро выделит блоки, даже притом, что размер файла не изменится. Подобное случается редко, но это все равно следует иметь в виду.
В SUSv3 определена функция posix_fallocate(fd, offset, len). Она гарантирует выделение дискового пространства для байтового диапазона, указанного аргументами offset и len для дискового файла, ссылка на который дается в дескрипторе fd. Это позволяет приложению получить гарантию, что при последующем вызове write() в отношении данного файла не будет сбоя, связанного с исчерпанием дискового пространства (который в противном случае может произойти при заполнении дыры в файле или потреблении дискового пространства каким-нибудь другим приложением). Исторически, реализация этой функции в glibc достигает нужного результата, записывая в каждый блок указанного диапазона нули. Начиная с версии 2.6.23, в Linux предоставляется системный вызов fallocate(). Он предлагает более эффективный способ обеспечения выделения необходимого пространства, и реализация posix_fallocate() в glibc использует этот системный вызов при его доступности.
В разделе 14.4 описывается способ представления дыр в файле, а в разделе 15.1 рассматривается системный вызов stat(), который способен сообщить о текущем размере файла, а также о количестве блоков, фактически выделенных файлу.
В листинге 4.3 показывается использование вызова lseek() в сочетании с read() и write(). Первый аргумент, передаваемый в командной строке для запуска этой программы, является именем открываемого файла. В остальных аргументах указываются операции ввода-вывода, выполняемые в отношении файла. Название каждой из этих операций состоит из буквы, за которой следует связанное с ней значение (без разделительного пробела):
• soffset — установка байтового смещения offset с начала файла;
• rlength — чтение length байтов из файла, начиная с текущего файлового смещения, и вывод их в текстовой форме;
• Rlength — чтение length байтов из файла, начиная с текущего файлового смещения и вывод их в виде шестнадцатеричных чисел;
• wstr — запись строки символов, указанной в str, начиная с позиции текущего файлового смещения.
Листинг 4.3. Демонстрация работы read(), write() и lseek()
fileio/seek_io.c
#include
#include
#include
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
size_t len;
off_t offset;
int fd, ap, j;
char *buf;
ssize_t numRead, numWritten;
if (argc < 3 || strcmp(argv[1], "-help") == 0)
usageErr("%s file {r
argv[0]);
fd = open(argv[1], O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH); /* rw-rw-rw- */
if (fd == -1)
errExit("open");
for (ap = 2; ap < argc; ap++) {
switch (argv[ap][0]) {
case 'r': /* Вывод байтов с позиции текущего смещения в виде текста */
case 'R': /* Вывод байтов с позиции текущего смещения в виде hex-чисел */
len = getLong(&argv[ap][1], GN_ANY_BASE, argv[ap]);
buf = malloc(len);
if (buf == NULL)
errExit("malloc");
numRead = read(fd, buf, len);
if (numRead == -1)
errExit("read");
if (numRead == 0) {
printf("%s: end-of-file\n", argv[ap]);
} else {
printf("%s: ", argv[ap]);
for (j = 0; j < numRead; j++) {
if (argv[ap][0] == 'r')
printf("%c", isprint((unsigned char) buf[j])?
buf[j]: '?');
else
printf("%02x", (unsigned int) buf[j]);
}
printf("\n");
}
free(buf);
break;
case 'w': /* Запись строки, начиная с позиции текущего смещения */
numWritten = write(fd, &argv[ap][1], strlen(&argv[ap][1]));
if (numWritten == -1)
errExit("write");
printf("%s: wrote %ld bytes\n", argv[ap], (long) numWritten);
break;
case 's': /* Изменение файлового смещения */
offset = getLong(&argv[ap][1], GN_ANY_BASE, argv[ap]);
if (lseek(fd, offset, SEEK_SET) == -1)
errExit("lseek");
printf("%s: seek succeeded\n", argv[ap]);
break;
default:
cmdLineErr("Argument must start with [rRws]: %s\n", argv[ap]);
}
}
exit(EXIT_SUCCESS);
}
fileio/seek_io.c
Использование программы, приведенной в листинге 4.3, показано в следующих сессиях командной оболочки, с демонстрацией того, что произойдет при попытке чтения байтов из файловой дыры:
$ touch tfile
$ ./seek_io tfile s100000 wabc
s100000: seek succeeded
wabc: wrote 3 bytes
$ ls — l tfile
— rw-r-r- 1 mtk users 100003 Feb 10 10:35 tfile
$ ./seek_io tfile s10000 R5
s10000: seek succeeded
R5: 00 00 00 00 00