如何保证指定文件是PHPBSD/Linux上的设备?

How to guarantee a specified file is a device on BSD/Linux from 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;
}

我的问题有两个:

  1. 这是检查此文件是否为设备的最佳方法吗?
  2. 是否存在此 $stat['rdev'] !== 0 检查可能失败的情况?

重要:我需要的解决方案必须在 PHP 中,而不依赖于任何 PECL 扩展或自定义 C 代码。该项目是 a pure PHP 5 polyfill of PHP 7's random_bytes() and random_int() functions,旨在由 Composer 安装在其他任何人的 PHP 5 个项目中。

嗯,你可以使用 filetype()。

如果你在 urandom 上做一个快速 ll,你会看到:

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

开头的 'c' 表示它是 "character" 文件类型。您可以在此处查看所有不同的文件类型:

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

这意味着如果你 运行

filetype("/dev/urandom");

你会得到 "char" 返回,意思是字符文件类型。 应该 做到这一点。

更新

我最初的解决方案原来只是 filetype($filepath) === 'char' 的重新实现,所以 filetype() 似乎是您唯一需要的。


基于@frymaster 的回答...

我查看了 PHP 的 stat() 函数是如何工作的,寻找 "char" 并找到了 this

结合 Linux and FreeBSD, as well as a comment on PHP's manual for stat() 的 stat(2) 手册,我得出以下结论:

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 可能会失败。根据我的发现,如果 OS 不支持,它可能 return -1,而即使是正值也可能指向不同的设备类型。它的值似乎也依赖于 OS。