Проблема с резервными копиями

Только технические вопросы по ЯОС и MINOS. Терминология и прочее - в других форумах.
Ответить
БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Проблема с резервными копиями

Сообщение БудДен » 07.09.22 00:40

Концептуальная информация про файловую систему Win.

При открытии файла ему назначается ключ - произвольное целое число (оно растёт).
При повторном открытии файла с таким же именем файловая система пытается найти уже открытый дескриптор файла.
и возвращает его, если он есть.
Файл кладётся в коллекцию финализируемых объектов и закрывается финализатором.
При создании резервной копии в редакторе файл переименовывается в .bak
При переименовании происходит поиск уже открытого описателя файла с основным именем (возвращается его ключ),
если он находится, то помечается как временный. Временный файл в финализаторе пытаемся удалить.
Последний раз редактировалось БудДен 24.09.22 18:32, всего редактировалось 3 раза.

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 07.09.22 01:23

Крокодил не ловится, не растёт кокос. Нужно найти место, где временный файл превращается обратно в обычный. Как вариант, включить трассировку файловой системы. ИЛи же найти прямо в коде, где это происходит именно для сохранения файла в ИСР. Но уже не сегодня.

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 19.09.22 00:43

Удалось включить трассировку (заодно нашлась проблема с литералами множеств, завёл задачу про это).
Похоже, что дело как-то связано с финализацией, потому что падает после неё.
Но странно, раньше-то всё работало. Либо на новом компьютере (более медленном)
срабатывают какие-то гонки, которые раньше не срабатывали, либо я что-то сломал. Попробую ещё попродвигаться
в направлении поиска гонок, но потом, видимо, придётся отступать в прошлое и искать момент поломки.

В плане гонок вызывает подозрение поиск объекта в множестве объектов, которые могут финализироваться.
Проблема в том, что поиск проводится процедурой, которая может возвращать даже те объекты, которые
уже приговорены к финализации. Как бы не получилось, что мы тем самым воскрешаем финализируемый объект.

Это не так сложно проверить, но странно, почему раньше такой беды никогда не случалось.

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 19.09.22 00:56

Ура, версия от 15.02 (SHA 6c1561acf1) тоже так падает. Т.е. если это и я что-то сломал, то не в последнее время. А на тот момент мой старый компьютер ещё работал. Значит, дело в переходе на новый компьютер. Видимо, нужно более внимательно посмотреть на статусы возврата и поискать гонки между операциями над файлами.

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 23.09.22 17:57

Шапками не закидать. Попробую проследить жизненный путь файла. Window.LoadВнутр:

Код: Выделить всё

file := Files.Old(name);
если (file # НУЛЬ) то
	file.GetName(fullname);
	readonly := readonly или (Files.ReadOnly в file.flags);
иначе (* эта ветка неинтересна *) всё;

(* определяем формат файла, при этом открываем его ещё раз *)
decoder := TextUtilities.DecodeAutoJQ(Ю32.Ю8_вСвежуюСтроку(fullname)^, autoCodecFormat);

(* 3-й раз открываем файл *)
in := Codecs.OpenInputStream(fullname);

(* file - локальная переменная внтури LoadВнутр, она бросается *)

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Что происходит при сохранении?

Сообщение БудДен » 24.09.22 14:41

Window.Store:

Код: Выделить всё

oldFile := Files.Old(nameA); (* это сам файл, со старым содержимым пока что *)
если (Files.ReadOnly в oldFile.flags) то ошибка и возврат всё;
CreateBackupFile
  Строки32.Склей(filename, Лит32(".Bak"), backName);
  Files.Rename(Ю32.СтрокуВСвежуюЮ8(filename)^, Ю32.СтрокуВСвежуюЮ8(backName)^, res);
    key := nfs.FileKey(fold);
    если key # 0 то f := FindOpenFile(seacher, nfs, key) иначе f := НУЛЬ всё;
      nfs.Rename0(fold, fnew, f, res)

Код: Выделить всё

        Fn := collection.ByNameNotGC( Ю16.Ю16_вСвежуюЮ8(fnnewU2)^ );
        если Fn # НУЛЬ то
          если ~Fn.ToTemp() то 
            трассируй("Rename0: не удалось превратить мешающий новый файл во временный"); 
            возврат всё всё;
        если fold # НУЛЬ то
          Fo := fold( File );
          если ~Fo.ToTemp() то
               трассируй("Rename0: не удалось превратить старый новый файл во временный");  возврат всё;
          трассируй(Fo.tfname^);
          ret := Kernel32.CopyFileW( Fo.tfName^, fnnewU2, 0 )
        иначе ret := Kernel32.MoveFileExW( fnoldU2, fnnewU2, {MoveFileReplaceExisting, MoveFileCopyAllowed} )

Код: Выделить всё

oldFile := НУЛЬ;
file := Files.New(nameA);
file.GetName(fullnameA);
w := Codecs.OpenOutputStream(fullnameA);
encoder.Open(w);
encoder.WriteText(currentPage.редакторТекстаДокумента.text, res);
w.ПротолкниБуферВПоток; (w бросается)

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 24.09.22 19:21

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

Код: Выделить всё

Collections.FinalizeFile C:\ob\jaos\source\Archives.Mod, key = 0
File.Finalize C:\ob\jaos\source\Archives.Mod
Collections.FinalizeFile C:\ob\jaos\source\Archives.Mod, key = -315
File.Finalize C:\ob\jaos\source\Archives.Mod
Collections.FinalizeFile C:\ob\jaos\source\Archives.Mod, key = -315
File.Finalize C:\ob\jaos\source\Archives.Mod
Ключ, равный нулю, возникает в процедуре Windows.WinFs.Mod/.../Unregister.Mod, а она вызывается только из ToTemp, а также в процедуре Windows.WinFs...New0, для нового файла.

Плюс к тому, дважды вызывается финализатор с одинаковым ключём. Это "жжж" явно неспроста.

БудДен
Сообщения: 2839
Зарегистрирован: 07.10.18 14:01

Re: Проблема с резервными копиями

Сообщение БудДен » 25.09.22 14:27

Уфф. Проблема была в Windows Defender. При многократном копировании он внедряет какой-то свой перехватчик копирования. Не знаю уж, что там дальше идёт не так, но если его отключить или добавить oberon.exe в список исключений Защитника Windows, то проблема исчезает.

MpDetoursCopyAccelerator.dll

Из проблем других людей:
Когда CopyFile вызывается игрой несколько раз (по нашему опыту, от 5 до 7 раз), игра внезапно загружает MpDetoursCopyAccelerator.dll, а другой процесс (я полагаю, процесс Defender) заботится о фактической копии. Поскольку этот другой процесс не работает в контексте нашей виртуальной файловой системы, операция копирования завершится неудачно
Не знаю, что они понимают под своей "виртуальной файловой системой", но, по всей вероятности, копирование падает по той причине, что файл всё ещё открыт в нашем процессе oberon.exe. В этом случае можно было бы попробовать закрывать файл, однако в общем случае неясно, как это сделать. Там построена хрупкая конструкция, позволяющая нескольким клиентам пользоваться одним и тем же файловым дескриптором от ОС. Для новых файлов это не работает. Файлу нужно по идее делать Register, но некоторые файлы бросаются с одним только New, но без Register. Короче говоря, там всё в таком состоянии, что "работает - не трогай".

Но понятно, что придётся заняться файловой системой вплотную. Обероновская идея о монопольном владении файлом не работает, когда мы находимся внутри другой ОС. Значит тут даже на идейном уровне нужно что-то переделывать, и эта переделка будет большой и затронет всю работу с файлами. Т.к. закрывать файлы тут не принято, принято их бросать и это носит тотальный характер.

Кроме того, работа в условиях монопольного владения файловой системой кардинально отличается от работы во внешнем мире, когда нужно учитывать существование других игроков. Т.е. и прикладная логика усложняется. Короче, это фундаментальная проблема, если мы хотим не только режим ОС, но и режим приложения. Получается, что код и бизнес-логика должны нести издержки за немонопольность, даже если мы работаем в режиме ОС. Ого-го.

Ответить