Версия для печати

logo70-tran

У 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;
}

Комментарии:

  1. Если мы открываем файл fd = open («/mount/m7/tmp/test», O_WRONLY); пишем туда в цикле по 1024 байта (к примеру) rc = write(fd, buf, strlen(buf)); и переполняем дисковую квоту, то вызывая синхронизацию rc = fsync (fd); и закрытие файл rc = close (fd); не видно никаких признаков ошибки.
  2. Но если открыть файл по-другому, с флагом 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

По теме:

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


Антиспам-картинка