Checking Your Access to a File
Problem
You want to see what you can do with a file: whether you have read, write, or (on Unix systems) execute permission on it.
Solution
Use the class methods File.readable?, File.writeable?, and File.executable?.
File.readable?('/bin/ls') # => true File.readable?('/etc/passwd-') # => false filename = 'test_file' File.open(filename, 'w') {} File.writable?(filename) # => true File.writable?('/bin/ls') # => false File.executable?('/bin/ls') # => true File.executable?(filename) # => false
Discussion
Ruby's file permission tests are Unix-centric, but readable? and writable? work on any platform; the rest fail gracefully when the OS doesn't support them. For instance, Windows doesn't have the Unix notion of execute permission, so File.executable? always returns true on Windows.
The return value of a Unix permission test depends in part on whether your user owns the file in question, or whether you belong to the Unix group that owns it. Ruby provides convenience tests File.owned? and File.grpowned? to check this.
File.owned? 'test_file' # => true File.grpowned? 'test_file' # => true File.owned? '/bin/ls' # => false
On Windows, File.owned? always returns true (even for a file that belongs to another user) and File.grpowned? always returns false.
The File methods described above should be enough to answer most permission questions about a file, but you can also see a file's Unix permissions in their native form by looking at the file's mode. The mode is a number, each bit of which has a different meaning within the Unix permission system.[1] You can view a file's mode with File::Lstat#mode.
[1] If you're not familiar with this, Recipe 6.3 describes the significance of the permission bits in a file's mode.
The result of mode contains some extra bits describing things like the type of a file. You probably want to strip that information out by masking those bits. This example demonstrates that the file originally created in the solution has a Unix permission mask of 0644:
File.lstat('test_file').mode & 0777 # Keep only the permission bits. # => 420 # That is, 0644 octal.
Setuid and setgid scripts
readable?, writable?, and executable? return answers that depend on the effective user and group ID you are using to run the Ruby interpreter. This may not be your actual user or group ID: the Ruby interpreter might be running setuid or setgid, or you might have changed their effective ID with Process.euid= or Process.egid=.
Each of the permission checks has a corresponding method that returns answers from the perspective of the process's real user and real group IDs: executable_real?, readable_real?, and writable_real?. If you're running the Ruby interpreter setuid, then readable_real? (for instance) will give different answers from readable?. You can use this to disallow users from reading or modifying certain files unless they actually are the root user, not just taking on the root users' privileges through setuid.
For instance, consider the following code, which prints our real and effective user and group IDs, then checks to see what it can do to a system file:
def what_can_i_do? sys = Process::Sys puts "UID=#{sys.getuid}, GID=#{sys.getgid}" puts "Effective UID=#{sys.geteuid}, Effective GID=#{sys.getegid}" file = '/bin/ls' can_do = [:readable?, :writable?, :executable?].inject([]) do |arr, method| arr << method if File.send( method, file); arr end puts "To you, #{file} is: #{can_do.join(', ')}" end
If you run this code as root, you can call this method and get one set of answers, then take on the guise of a less privileged user and get another set of answers:
what_can_i_do? # UID=0, GID=0 # Effective UID=0, Effective GID=0 # To you, /bin/ls is: readable?, writable?, executable? Process.uid = 1000 what_can_i_do? # UID=0, GID=0 # Effective UID=1000, Effective GID=0 # To you, /bin/ls is: readable?, executable?
See Also
- Recipe 6.3, "Changing the Permissions on a File"
- Recipe 23.3, "Running Code as Another User," has more on setting the effective user ID