Как гарантировать, что указанный файл является устройством на BSD/Linux с PHP?

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

Самый простой способ - это что-то вроде:

/**
 * Is the given file a device?
 * 
 * @param string|resource $file
 * @return boolean
 */
function is_device($file)
{
    if (is_resource($file)) {
        $stat = fstat($file);            
    } elseif (is_readable($file) && !is_link($file)) {
        $stat = stat($file);
    } else {
        return false;
    }
    return $stat['rdev'] !== 0;
}

Мой вопрос в два раза:

  • Это лучший способ проверить, что этот файл является устройством?
  • Существуют ли случаи, когда проверка $stat['rdev'] !== 0 может завершиться неудачей?

Важно. Решение, которое мне нужно, должно быть на PHP без каких-либо расширений PECL или пользовательского кода C. Проект чистый PHP 5 polyfill функций PHP 7 random_bytes() и random_int() и предназначен для установки в любом другом PHP 5 проектов от Composer.

Ответы

Ответ 1

ну, вы можете использовать filetype().

если вы быстро произнесите уранд, вы увидите:

ll /dev/urandom 
crw-rw-rw- 1 root root 1, 9 Jul 26 17:38 /dev/urandom

что 'c' в начале означает, что это "характер" типа файла. вы можете проверить все различные типы файлов здесь:

https://en.wikipedia.org/wiki/Unix_file_types

это означает, что если вы запустите

filetype("/dev/urandom");

вы получите "char" назад, что означает символ filetype. это должно сделать трюк.

Ответ 2

Обновление

Мое первоначальное решение оказалось всего лишь повторной реализацией filetype($filepath) === 'char', поэтому filetype(), похоже, единственное, что вам нужно.


На основе ответа @frymaster...

Я посмотрел, как работает функция PHP stat(), ища "char" и нашла this.

В сочетании с руководством stat (2) для Linux и FreeBSD, а также комментарий к руководству PHP для stat(), я придумал следующее:

function is_device($filepath)
{
        if ( ! file_exists($filepath) OR (stripos(PHP_OS, 'Linux') === false && stripos(PHP_OS, 'BSD') === false))
        {
                return false;
        }

        $mode = stat($filepath)['mode'];
        return (020000 === ($mode & 0170000));
}

Работает на моей Linux-системе.

Обновить (чтобы ответить на второй вопрос)

Да, stat($file)['rdev'] !== 0 может выйти из строя. Из того, что я нашел, он может вернуть -1, если не поддерживается ОС, хотя даже положительное значение может указывать на другой тип устройства. Его значения также зависят от ОС.