Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to guarantee a specified file is a device on BSD/Linux from PHP?

While working on a project that reads from /dev/urandom to generate random bytes, it was suggested that I check to make sure that /dev/urandom is a device not just a file.

The most straightforward way seems to be something like:

/**
 * 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;
}

My question is two-fold:

  1. Is this the best way to check that this file is a device?
  2. Are there circumstances where this $stat['rdev'] !== 0 check can fail?

Important: The solution I need must be in PHP without depending on any PECL extensions or custom C code. The project is a pure PHP 5 polyfill of PHP 7's random_bytes() and random_int() functions and is intended to be installable in anyone else's PHP 5 projects by Composer.

like image 328
Scott Arciszewski Avatar asked Jul 25 '15 20:07

Scott Arciszewski


2 Answers

well, you can use filetype().

if you do a fast ll on urandom, you will see:

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

that 'c' at the beginnng means it's a "character" filetype. you can check out all the different filetypes here:

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

this means that if you run

filetype("/dev/urandom");

you will get "char" back, meaning character filetype. that should do the trick.

like image 81
frymaster Avatar answered Oct 20 '22 02:10

frymaster


Update

My original solution turned out to be just a re-implementation of filetype($filepath) === 'char', so filetype() seems to be the only thing you need.


Based on @frymaster's answer ...

I looked at how PHP's stat() function works, looking for "char" and found this.

Combined with the stat(2) manual for both Linux and FreeBSD, as well as a comment on PHP's manual for stat(), I came up with the following:

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));
}

Works on my Linux system.

Update (to answer the second question)

Yes, stat($file)['rdev'] !== 0 can fail. From what I found, it may return -1 if not supported by the OS, while even a positive value may point at a different device type. Its values also appear to be OS-dependent.

like image 40
Narf Avatar answered Oct 20 '22 03:10

Narf