Рекурсивный итератор std::filesystem выдаст разрешение_denied, даже если используется skip_permission_denied

Я пишу программу командной строки Linux, которая вернет размер каталога. Программа работает так, как ожидалось, за исключением случаев, когда речь идет конкретно о корневых каталогах. Я знаю, что многие файлы в корневом каталоге не имеют размеров, потому что это специальные файлы, используемые для представления системной информации (например, /proc/) или что-то вроде /dev/null/, поэтому я использовал std::filesystem::directory_options::skip_permission_denied в своем цикле for, чтобы пропустить проблемы с разрешениями и Я использовал несколько блоков try/catch для перехвата исключений.

Однако даже при этом исключение отказа в разрешении все равно выдается. См. следующий код:

byte_size_and_num_files find_recursive(const std::filesystem::path& path)
{
    byte_size_and_num_files bsnf;
    std::filesystem::path pa;
    try
    {
      for(const auto& p: std::filesystem::recursive_directory_iterator(path, std::filesystem::directory_options::skip_permission_denied))
      {
          pa = p.path();
          if (std::filesystem::exists(p) && !std::filesystem::is_directory(p))
          {
            try
            {
              if(std::filesystem::is_regular_file(pa))
              {
                bsnf.size += std::filesystem::file_size(p);
                bsnf.files++;
              }
              else
                std::cout << "SKIPPED: size is not determinable: " << pa << "\n";
            }
            catch(std::filesystem::filesystem_error& e)
            {
              std::cout << "SKIPPED: size is not determinable: " << pa << "\n";
            }
            catch(std::bad_alloc)
            {
              std::cout << "Allocation error. Exiting..." << "\n";
              byte_size_and_num_files err;
              return err;
            }
          }
        }
    }
    catch(std::filesystem::filesystem_error& e)
    {
      std::cout << "Unable to access file or path " << pa <<": " << e.what() << "\n";
    }
    catch(std::bad_alloc)
    {
      std::cout << "Allocation error. Exiting..." << "\n";
      byte_size_and_num_files err;
      return err;
    }
    return bsnf;
}

Выход:

...
SKIPPED: size is not determinable: "/lib/systemd/system/motd.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountall-bootclean.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountall.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountdevsubfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountkernfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountnfs-bootclean.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountnfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rc.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rcS.service"
SKIPPED: size is not determinable: "/lib/systemd/system/reboot.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rmnologin.service"
SKIPPED: size is not determinable: "/lib/systemd/system/sendsigs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/single.service"
SKIPPED: size is not determinable: "/lib/systemd/system/stop-bootlogd-single.service"
SKIPPED: size is not determinable: "/lib/systemd/system/stop-bootlogd.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountnfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountroot.service"
SKIPPED: size is not determinable: "/lib/systemd/system/x11-common.service"
SKIPPED: size is not determinable: "/lib/systemd/system/lvm2.service"
Unable to access file or path "/proc/1/task/1/cwd": filesystem error: status: Permission denied [/proc/1/task/1/cwd]
/ is 262172.00 gigabytes with 143839 files.

Что дает? Его четкий отказ в разрешении всегда следует пропускать, но он все равно выдается. Ясно также, что файлы, размер которых невозможно определить, также пропускаются, но создается впечатление, что найдены и используются файлы неправильных размеров (262172,00 ГБ, конечно, не правильно). Это происходит ТОЛЬКО при переходе через корневой каталог, а не через какой-либо другой каталог, например домашний.

Может ли это быть ошибкой реализации?

РЕДАКТИРОВАТЬ: некоторая отладка выявила важную информацию. Похоже, что /dev/pts/ специально портит размеры файлов. Я не понимаю, почему файлы в этом каталоге не пропускаются, так как они не являются обычными файлами, поэтому их следует пропускать. Я предполагаю, что std::filesystem неправильно идентифицирует их как обычные файлы.

Кроме того, std::filesystem::directory_options::skip_permission_denied работает для каждого отдельного файла, кроме /proc/1/task/1/cwd. Я не уверен, почему именно этот файл не пропущен.

РЕДАКТИРОВАТЬ 2 Я отследил каталоги преступников, которые нарушают std::filesystem. Есть три,

/dev/
/proc/ 
/var/cache/

/dev/ по какой-то причине испортит размеры файлов, я думаю, std::filesystem считает эти обычные файлы, поэтому, когда вы пытаетесь получить размеры файлов, вы получаете номер мусора. Некоторые части /dev/ будут вызывать исключение разрешения, ДАЖЕ КОГДА используется разрешение пропуска.

/proc/ также искажает размеры файлов по той же причине, что и /dev/

/var/cache/, похоже, не искажает размеры файлов, но выдает исключение разрешения, даже если используется разрешение пропуска.

Я считаю, что это ошибки реализации, поскольку файлы в этих каталогах не являются обычными файлами, но std::filesystem рассматривает их как таковые.


person Lucas Streanga    schedule 10.03.2021    source источник
comment
Вот один из бесчисленных примеров того, как это сделать иначе:   -  person Chef Gladiator    schedule 10.03.2021
comment
Какую библиотеку времени выполнения C++ вы используете (я хотел бы взглянуть на исходники)? А может можно запустить программу под отладчиком и проверить, где именно выбрасывается исключение!?   -  person Werner Henze    schedule 10.03.2021
comment
@WernerHenze G++ 9.3.0 с флагами -std=c++17 и -lstdc++fs. Я запущу его под GDB и посмотрю, когда он кинет.   -  person Lucas Streanga    schedule 10.03.2021


Ответы (1)


В документах cppreference сказано, что skip_permission_denied означает пропускать каталоги, которые в противном случае это приведет к ошибкам отказа в разрешении.. Он ничего не говорит о файлах, только о каталогах. Опция означает, что в случае, если вы не можете прочитать /proc/sys/xyz, этот каталог пропускается и исключение не выдается. В вашем случае это соблюдается, и вы не получаете исключение filesystem_error из recursive_directory_iterator. Но это не связано ни с одним файлом, который возвращает этот итератор.

Но возникает исключение в std::filesystem::exists(p) и в std::filesystem::is_directory(p). Он вызывается std::filesystem::status, который внутренне вызывается обеими функциями. en.cppreference говорит, что в status симлинки переходят к своим целям . Это невозможно для /proc/1/task/1/cwd, если вы не root.

ls -alg /proc/1/task/1/cwd показывает мне файловый режим lrwxrwxrwx, но сообщение об отказе в разрешении.

$ ls -alg /proc/1/task/1/cwd
ls: Lesen der symbolischen Verknüpfung '/proc/1/task/1/cwd' без изменений: Keine Berechtigung
lrwxrwxrwx 1 root 0 Mär 11 08:53 /proc/1/task/1/cwd

Я полагаю, что recursive_directory_iterator смотрит только на файловый режим, но cwd кажется особым и применяются дополнительные правила доступа. Таким образом, решение для вас может состоять в том, чтобы перехватывать исключения, созданные std::filesystem::exists или std::filesystem::is_directory, игнорировать их и продолжать.

person Werner Henze    schedule 10.03.2021