1. Pascal / Говнокод #4966

    +109

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    procedure RemoveDir(path: string);
    var
      sr: TSearchRec;
    begin
      if FindFirst(path + '\*.*', faAnyFile, sr) = 0 then
      begin
        repeat     
          if sr.Attr and faDirectory = 0 then
            DeleteFile(path + '\' + sr.name);
          else
              RemoveDir(path + '\' + sr.name);
        until
          FindNext(sr) <> 0;
      end;
      FindClose(sr);
      RemoveDirectory(PChar(path));
    end;

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

    P.S. Нужно добавить проверку между 10 и 11 строкой:
    if (sr.Name <> '..') and (sr.Name <> '.') then

    Запостил: vanished, 18 Декабря 2010

    Комментарии (20) RSS

    • Реакция есть - дети будут...
      Ответить
    • показать все, что скрытолох блять
      Ответить
    • А я, когда писал нечто подобное, сразу сообразил, что надо этот случай проверить, иначе будет плохо.
      Или нет, не сообразил. Или сообразил только для "..", а для "." забыл. Не помню. Кажется, у меня рекурсия (на ".") вылезла за стек и всё благополучно повисло, и всё.
      Ответить
      • рекурсия со строковым параметром по значению обречена на переполнение стека
        Ответить
        • Не в этом случае - длина полного пути все равно ограничена MAX_PATH, так что глубина ррекурсии не будет большой.
          К тому же в стек помещается только указатель на строку, а сама строка - в динамической памяти через GetMem
          Ответить
      • А я не мог знать, что функция возвращает еще ".." и ".".
        Ответить
        • надо было у программиста спросить, прежде чем .. .
          Ответить
        • > не мог знать
          Плохая формулировка. Просто "не знал" лучше.
          Ответить
        • А в ДОСе был ещё один прикол. В FAT основным именем файла было короткое (8.3), а ассоциированные длинные имена хранились в записях с атрибутами скрытый-системный-только_для_чтения-метка_тома. При запуске программы из голого DOS (когда vfat.vxd не загружен) кроме файлов, директорий, '.' и '..', возвращалась ещё каша из фрагментов длинных имён, так что надо было убеждаться, что атрибут не равен $0F, иначе последствия вызова RemoveDir трудно угадать.
          Ответить
          • длинные имена в ДОСе?
            я что-то пропустил оО
            Ответить
            • быват, у INT 21 были функции при работе под VDM
              Ответить
            • А как по-вашему хранились папки «Program Files» и «Мои документы» в Windows 95 на FAT16/32?

              VFAT.VXD предоставлял API через INT 21 под VDM (смотрите Ralf Brown's Interrupt List, например). Этот же API предоставляет NTVDM, начиная с Windows XP (в более старых NT надо было ставить сторонний драйвер ntlfn).

              Этот же API эмулирует драйвер doslfn (он к тому же умеет читать длинные имена Joliet на сиди-дисках), а также какой-то драйвер из ДР-ДОСа, а ROM-DOS имеет встроенную поддержку длинных имён.

              Наконец, драйвер vfat в Линуксе откуда длинные имена берёт?
              Ответить
              • я полагал, что windows извращенно хакал, пряча их куда-то в заголовок файла, что бы ДОС о них не знал
                Ответить
                • В документации к линуксовому vfat, в Ralf Brown's Interrupt List, да ещё в куче источников (кажется, даже в самом Микрософте) подробно описан этот формат.

                  Обычный заголовок файла — 32 байта — содержит имя (8+3 символа), атрибуты, дату и время создания, размер, указатель на первый кластер и ещё кое-что.

                  Длинное имя разбивается на слоты, каждый слот содержит 13 уникодных символов (26 байт), символы запихиваются во все поля подряд (они не нужны), в атрибуте стоит 0xf - системный+скрытый+ридонли+метка, а сами слоты помещаются непосредственно перед коротким именем файла. Если мы не будем проверять атрибуты, то FindNext под голым ДОСом (без VFAT) будет находить слоты длинных имён как обычные файлы, размер и указатель на кластер этих слотов, естественно, будут содержать кашу.
                  Ответить
        • или посмотреть на отладке - дело то ответственное:)
          Ответить
    • Фу-фу-фу
      path + '\'
      IncludeTrailingPathDelimiter() 1 раз в начало дописать. Тем более, что path не const.
      Ответить
      • Кстати, path + '\' + sr.name два раза не будет сильно кушать стек?
        Ответить
        • Не будет. Длинные строки хранятся в куче. Тут sr основной пожиратель. Вот его бы не мешало сделать динамическим.
          Ответить
    • Ещё добавить EnterCriticalSection, выставить максимальный приоритет, поставить обработчик кнопки закрытия окна или вообще убить окно/консоль, спрятать в списке процессов и вишмастер готов.
      Ответить

    Добавить комментарий