
У NFS клиента в 2.6.x ядре появилась неприятная особенность. Он никак не сигнализирует о невозможности записать данные по причине превышения файловой квоты. А наоборот всем своим поведением говорит о том, что всё хорошо. Все операции, включая fsync() и close() заканчиваются успешно. Более того, даже ls после этого показывает якобы всё записано(размер файла равен записанному, что превышает квоту). Но через несколько секунд размер файла «укорачивается» до того размера, что поместилось в квоту (что в общем-то и есть правда).
Вот небольшая тестовая программа test_nfswrite.c, которая пишет 1024 байтные блоки. Тестовый файл /mount/m7/tmp/test должен быть на NFS сервере (не важно с каким ядром). А клиент, который пишет с ядром 2.6.x.
Компилить надо так: gcc test_nfswrite.c -o test_nfswrite
Запускать так: ./test_nfswrite 500
где 500 — это количество блоков по 1024 байта. Если указать большое количество блоков, которое не влезает в квоту, то вы должны наблюдать ошибки.
#include
#include
#include
#include
#include
#include
#include
#include
int main(int arc, char * argv[]) {
int fd;
int rc;
int i;
int size = 2049;
char *buf;
int it;
it = atoi(argv[1]);
buf = (char*)malloc(size * sizeof(char));
memset(buf, 'a', size - 1);
buf[size - 1] = '\0';
/* fd = open("/mount/m7/tmp/test", O_WRONLY|O_SYNC); */
fd = open("/mount/m7/tmp/test", O_WRONLY);
for(i = 0; i < it; i++) {
rc = write(fd, buf, strlen(buf));
printf("write: %d, errno = %s\n", rc, strerror(errno));
}
rc = fsync(fd);
printf("fsync: %d, errno = %s\n", rc, strerror(errno));
rc = close(fd);
printf("close: %d\n", rc);
free(buf);
return 0;
}
Комментарии:
- Если мы открываем файл fd = open («/mount/m7/tmp/test», O_WRONLY); пишем туда в цикле по 1024 байта (к примеру) rc = write(fd, buf, strlen(buf)); и переполняем дисковую квоту, то вызывая синхронизацию rc = fsync (fd); и закрытие файл rc = close (fd); не видно никаких признаков ошибки.
- Но если открыть файл по-другому, с флагом O_SYNC fd = open («/mount/m7/tmp/test», O_WRONLY | O_SYNC);, то ошибка появится. И при попытке записи и при синхронизации: write: -1, errno = Input/output error, fsync: -1, errno = Disk quota exceeded.
Данное исследование проводилось в связи с тем, что MTA Zmailer имеет mailbox.c LDA (доставщик сообщений в ящик пользователя), который подвержен этой проблеме. При использовании NFS хранилища для ящиков и ядер 2.6.x на клиенте, доставка сообщений в переполненный ящик проводит к разрушению структуры ящика.
Небольшой патч включает O_SYNC режим записи и исправляет проблему:
--- ./zmailer.orig/transports/mailbox/mailbox.c 2006-06-26 20:28:03.000000000 +0400
+++ ./zmailer/transports/mailbox/mailbox.c 2009-06-22 16:49:18.000000000 +0400
@@ -1753,7 +1753,8 @@
"To open a file with euid=%d egid=%d ismbox=%d file='%s'\n",
(int)geteuid(), (int)getegid(), ismbox, file);
- fdmail = open(file, O_RDWR|O_APPEND);
+/* +++ */
+ fdmail = open(file, O_RDWR|O_APPEND|O_SYNC);
if (fdmail < 0) {
char fmtbuf[512];
@@ -1997,6 +1998,11 @@
int rc = write(sffileno(sfp), p, len);
int e = errno;
+/* +++ */
+ if ((rc < 0) && (fsync(sffileno(sfp)) < 0)) {
+ if (errno == EDQUOT) e = errno;
+ }
+
if (verboselog)
fprintf(verboselog, " mbox_sfwrite(ptr, len=%d) rc=%d errno=%d\n",
(int)len, rc, e);
Больше спасибо Mike Fandorin <fandorin (at) rol.ru> за исследование проблемы.
Cross-posted to ZMailer mailing list.
UPD: Похоже в 2.6.30 наконец-то эту проблему. fsync() теперь работает в синхронном режиме не зависимо от опций открытия файла:
ext3: Use WRITE_SYNC for commits which are caused by fsync() Theodore Ts'o [Sat, 28 Mar 2009 02:14:27 +0000 (22:14 -0400)] If a commit is triggered by fsync(), set a flag indicating the journal blocks associated with the transaction should be flushed out using WRITE_SYNC. Signed-off-by: "Theodore Ts'o" Acked-by: Jan Kara









Оставить комментарий