Changing the Permissions on a File

Problem

You want to control access to a file by modifying its Unix permissions. For instance, you want to make it so that everyone on your system can read a file, but only you can write to it.

Solution

Unless you've got a lot of Unix experience, it's hard to remember the numeric codes for the nine Unix permission bits. Probably the first thing you should do is define constants for them. Here's one constant for every one of the permission bits. If these names are too concise for you, you can name them USER_READ, GROUP_WRITE, OTHER_ EXECUTE, and so on.

	class File
	 U_R = 0400
	 U_W = 0200
	 U_X = 0100
	 G_R = 0040
	 G_W = 0020
	 G_X = 0010
	 O_R = 0004
	 O_W = 0002
	 O_X = 0001
	end

You might also want to define these three special constants, which you can use to set the user, group, and world permissions all at once:

	class File
	 A_R = 0444
	 A_W = 0222
	 A_X = 0111
	 end

Now you're ready to actually change a file's permissions. Every Unix file has a permission bitmap, or mode, which you can change (assuming you have the permissions!) by calling File.chmod. You can manipulate the constants defined above to get a new mode, then pass it in along with the filename to File.chmod.

The following three chmod calls are equivalent: for the file my_file, they give readwrite access to to the user who owns the file, and restrict everyone else to read-only access. This is equivalent to the permission bitmap 11001001, the octal number 0644, or the decimal number 420.

open("my_file", "w") {}

File.chmod(File::U_R | File::U_W | File::G_R | File::O_R, "my_file")
File.chmod(File::A_R | File::U_W, "my_file")
File.chmod(0644, "my_file") # Bitmap: 110001001

File::U_R | File::U_W | File::G_R | File::O_R # => 420
File::A_R | File::U_W # => 420
0644 # => 420
File.lstat("my_file").mode & 0777 # => 420

Note how I build a full permission bitmap by combining the permission constants with the OR operator (|).

Discussion

A Unix file has nine associated permission bits that are consulted whenever anyone tries to access the file. They're divided into three sets of three bits. There's one set for the user who owns the file, one set is for the user group who owns the file, and one set is for everyone else.

Each set contains one bit for each of the three basic things you might do to a file in Unix: read it, write it, or execute it as a program. If the appropriate bit is set for you, you can carry out the operation; if not, you're denied access.

When you put these nine bits side by side into a bitmap, they form a number that you can pass into File.chmod. These numbers are difficult to construct and read without a lot of practice, which is why I recommend you use the constants defined above. It'll make your code less buggy and more readable.[2]

[2] It's true that it's more macho to use the numbers, but if you really wanted to be macho you'd be writing a shell script, not a Ruby program.

File.chmod completely overwrites the file's current permission bitmap with a new one. Usually you just want to change one or two permissions: make sure the file isn't world-writable, for instance. The simplest way to do this is to use File.lstat#mode to get the file's current permission bitmap, then modify it with bit operators to add or remove permissions. You can pass the result into File.chmod.

Use the XOR operator (^) to remove permissions from a bitmap, and the OR operator, as seen above, to add permissions:

	# Take away the world's read access.
	new_permission = File.lstat("my_file").mode ^ File::O_R
	File.chmod(new_permission, "my_file")
	
	File.lstat("my_file").mode & 0777 # => 416 # 0640 octal
	
	# Give everyone access to everything
	new_permission = File.lstat("my_file").mode | File::A_R | File::A_W | File::A_X
	File.chmod(new_permission, "my_file")
	
	File.lstat("my_file").mode & 0777 # => 511 # 0777 octal
	
	# Take away the world's write and execute access
	new_permission = File.lstat("my_file").mode ^ (File::O_W | File::O_X)
	File.chmod(new_permission, "my_file")
	
	File.lstat("my_file").mode & 0777 # => 508 # 0774 octal

If doing bitwise math with the permission constants is also too complicated for you, you can use code like this to parse a permission string like the one accepted by the Unix chmod command:

	class File
	 def File.fancy_chmod(permission_string, file)
	 mode = File.lstat(file).mode
 	 permission_string.scan(/[ugoa][+-=][rwx]+/) do |setting|
	 who = setting[0..0]
	 setting[2..setting.size].each_byte do |perm|
	 perm = perm.chr.upcase
	 mask = eval("File::#{who.upcase}_#{perm}")
	 (setting[1] == ?+) ? mode |= mask : mode ^= mask
	 end
	 end
	 File.chmod(mode, file)
	end
	end
	# Give the owning user write access
	File.fancy_chmod("u+w", "my_file")

	File.lstat("my_file").mode & 0777 # => 508 # 0774 octal

	# Take away the owning group's execute access
	File.fancy_chmod("g-x", "my_file")

	File.lstat("my_file").mode & 0777 # => 500 # 0764 octal
	# Give everyone access to everything

	File.fancy_chmod("a+rwx", "my_file")

	File.lstat("my_file").mode & 0777 # => 511 # 0777 octal

	# Give the owning user access to everything. Then take away the
	# execute access for users who aren't the owning user and aren't in
	# the owning group.
	File.fancy_chmod("u+rwxo-x", "my_file")
	File.lstat("my_file").mode & 0777 # => 510 # 0774 octal

Unix-like systems such as Linux and Mac OS X support the full range of Unix permissions. On Windows systems, the only one of these operations that makes sense is adding or subtracting the U_W bit of a filemaking a file read-only or not. You can use File.chmod on Windows, but the only bit you'll be able to change is the user write bit.

See Also

  • Recipe 6.2, "Checking Your Access to a File"
  • Recipe 23.9, "Normalizing Ownership and Permissions in User Directories"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net