Как обнаружить удаленный файл? - PullRequest
0 голосов
/ 07 ноября 2018

Запись в несуществующий файл не приводит к ошибке в Go.

Например, вот пример программы, записывающей в файл в цикле:

package main

import (
    "log"
    "os"
    "time"
)

func main() {

    f, err := os.OpenFile("mytest.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }

    for {

        n, err := f.WriteString("blah\n")
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("wrote %d bytes\n", n)
        time.Sleep(2 * time.Second)
    }
}

Во время работы я выдаю rm mytest.log из командной строки и наблюдаю, что программа не выдает ошибку при следующем вызове WriteString(). (Я тестировал на Linux, он может отличаться для других ОС)

Есть ли способ определить, был ли файл удален (кроме как выполнить статистику файла перед каждой записью)? И предположительно записанные байты просто удаляются операционной системой?

1 Ответ

0 голосов
/ 07 ноября 2018

Во время работы я запускаю rm mytest.log из командной строки и наблюдаю, что программа не выдает ошибку при следующем вызове WriteString()

Да, это именно то поведение, которое указано. Также файл не был удален. Единственное, что rm удаляет, - это конкретная запись пути в файловой системе. Один файл может иметь несколько путей, также называемых hardlinks .

Фактический файл удаляется только после закрытия последней ссылки на него, либо по записи файловой системы (ссылка), либо по дескриптору файла (файл, открытый в программе).

Это специфическое поведение файловой модели Unix долгое время использовалось для реализации "неназванной" разделяемой памяти, путем создания и открытия файла в /dev/shm, а затем удаления записи файловой системы - потому что этот особый способ работы вводит условие гонки, для чувствительных к безопасности приложений были введены новые системные вызовы, которые позволяют создавать анонимные карты памяти, и совсем недавно в Linux даже появилась функция для создания файла в файловой системе без создания записи пути (open с O_TMPFILE флагом).

В более поздних версиях Linux вы даже можете заново создавать / создавать записи файловой системы для файлов, последняя из которых уже была удалена с помощью системного вызова linkat.

Обновление

Вопрос в том, действительно ли вы хотите выдать ошибку, если последняя запись в файловой системе исчезнет? В конце концов, это не плохое состояние, вы можете спокойно писать и читать без проблем, просто имейте в виду, что как только вы закроете последний файловый дескриптор файла, он будет потерян.

Вполне возможно обнаружить, была ли удалена последняя запись файловой системы, и прервать файловые операции, если это так - однако имейте в виду, что такой код может создать свою собственную долю проблем, например, если программа ожидает создания новой запись в файловой системе, когда все правильно записано в файл, используя linkat.

В любом случае, вы можете fstat -ing файл (file.Stat в Go) и посмотреть на количество жестких ссылок в файле. Если это число падает до нуля, все записи файловой системы исчезают. На самом деле получить это число немного сложно в Go, это описано здесь Подсчет жестких ссылок на файл в Go

package main

import (
    "fmt"
    "log"
    "os"
    "syscall"
    "time"
)

func main() {
    fmt.Println("Test Operation")
    f, err := os.OpenFile("test.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }

    for {

        n, err := f.WriteString("blah\n")
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("wrote %d bytes\n", n)
        time.Sleep(2 * time.Second)
        stat, err := f.Stat()
        if err != nil{
            log.Fatal(err)
        }
        if sys := stat.Sys(); sys != nil {
            if stat, ok := sys.(*syscall.Stat_t); ok {
                nlink := uint64(stat.Nlink)
                if 0 == nlink {
                    log.Printf("All filesystem entries to original file removed, exiting")
                    break
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...