Skip to content

Commit

Permalink
Showing 3 changed files with 90 additions and 1 deletion.
50 changes: 50 additions & 0 deletions spec/std/file_spec.cr
Original file line number Diff line number Diff line change
@@ -184,6 +184,56 @@ describe "File" do
File.join(["/foo/", "/bar/", "/baz/"]).should eq("/foo/bar/baz/")
end

assert "chown" do
# changing owners requires special privileges, so we test that method calls do compile
typeof(File.chown("/tmp/test"))
typeof(File.chown("/tmp/test", uid: 1001, gid: 100, follow_symlinks: true))
end

describe "chmod" do
it "changes file permissions" do
path = "#{__DIR__}/data/chmod.txt"
begin
File.write(path, "")
File.chmod(path, 0o775)
File.stat(path).perm.should eq(0o775)
ensure
File.delete(path) if File.exists?(path)
end
end

it "changes dir permissions" do
path = "#{__DIR__}/data/chmod"
begin
Dir.mkdir(path, 0o775)
File.chmod(path, 0o664)
File.stat(path).perm.should eq(0o664)
ensure
Dir.rmdir(path) if Dir.exists?(path)
end
end

it "follows symlinks" do
path = "#{__DIR__}/data/chmod_destination.txt"
link = "#{__DIR__}/data/chmod.txt"
begin
File.write(path, "")
File.symlink(path, link)
File.chmod(link, 0o775)
File.stat(link).perm.should eq(0o775)
ensure
File.delete(path) if File.exists?(path)
File.delete(link) if File.symlink?(link)
end
end

it "raises when destination doesn't exist" do
expect_raises(Errno) do
File.chmod("#{__DIR__}/data/unknown_chmod_path.txt", 0o664)
end
end
end

it "gets stat for this file" do
stat = File.stat(__FILE__)
stat.blockdev?.should be_false
39 changes: 39 additions & 0 deletions src/file.cr
Original file line number Diff line number Diff line change
@@ -240,6 +240,45 @@ class File < IO::FileDescriptor
basename(path).chomp(suffix)
end

# Changes the owner of the specified file.
#
# ```
# File.chown("/foo/bar/baz.cr", 1001, 100)
# File.chown("/foo/bar", gid: 100)
# ```
#
# Unless *follow_symlinks* is set to true, then the owner symlink itself will
# be changed, otherwise the owner of the symlink destination file will be
# changed. For example, assuming symlinks as `foo -> bar -> baz`:
#
# ```
# File.chown("foo", gid: 100) # changes foo's gid
# File.chown("foo", gid: 100, follow_symlinks: true) # changes baz's gid
# ```
def self.chown(path, uid : Int? = -1, gid : Int = -1, follow_symlinks = false)
ret = if !follow_symlinks && symlink?(path)
LibC.lchown(path, uid, gid)
else
LibC.chown(path, uid, gid)
end
raise Errno.new("Error changing owner of '#{path}'") if ret == -1
end

# Changes the permissions of the specified file.
#
# Symlinks are dereferenced, so that only the permissions of the symlink
# destination are changed, never the permissions of the symlink itself.
#
# ```
# File.chmod("foo/bin", 0o755)
# File.chmod("foo/bin/exec", 0o700)
# ```
def self.chmod(path, mode : Int)
if LibC.chmod(path, mode) == -1
raise Errno.new("Error changing permissions of '#{path}'")
end
end

# Delete the file at *path*. Deleting non-existent file will raise an exception.
#
# ```
2 changes: 1 addition & 1 deletion src/file/stat.cr
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ class File
io << " dev=0x"
dev.to_s(16, io)
io << ", ino=" << ino
io << ", mode=0"
io << ", mode=0o"
mode.to_s(8, io)
io << ", nlink=" << nlink
io << ", uid=" << uid

0 comments on commit c2cabff

Please sign in to comment.