Skip to content

Commit

Permalink
Rename and rework File::Stat as File::Info
Browse files Browse the repository at this point in the history
  • Loading branch information
RX14 committed Jan 13, 2018
1 parent d294dd1 commit 030495b
Show file tree
Hide file tree
Showing 15 changed files with 261 additions and 303 deletions.
93 changes: 46 additions & 47 deletions spec/std/file_spec.cr
Expand Up @@ -314,7 +314,7 @@ describe "File" do
begin
File.write(path, "")
File.chmod(path, 0o775)
File.stat(path).perm.should eq(0o775)
File.stat(path).permissions.should eq(0o775)
ensure
File.delete(path) if File.exists?(path)
end
Expand All @@ -325,20 +325,31 @@ describe "File" do
begin
Dir.mkdir(path, 0o775)
File.chmod(path, 0o664)
File.stat(path).perm.should eq(0o664)
File.stat(path).permissions.should eq(0o664)
ensure
Dir.rmdir(path) if Dir.exists?(path)
end
end

it "can take File::Permissions" do
path = "#{__DIR__}/data/chmod.txt"
begin
File.write(path, "")
File.chmod(path, File::Permissions.flags(OwnerAll, GroupAll, OtherExecute, OtherRead))
File.stat(path).permissions.should eq(0o775)
ensure
File.delete(path) if File.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)
File.stat(link).permissions.should eq(0o775)
ensure
File.delete(path) if File.exists?(path)
File.delete(link) if File.symlink?(link)
Expand All @@ -354,61 +365,35 @@ describe "File" do

it "gets stat for this file" do
stat = File.stat(__FILE__)
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_true
stat.symlink?.should be_false
stat.socket?.should be_false
stat.type.should eq(File::Type::File)
end

it "gets stat for this directory" do
stat = File.stat(__DIR__)
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_true
stat.file?.should be_false
stat.symlink?.should be_false
stat.socket?.should be_false
stat.type.should eq(File::Type::Directory)
end

it "gets stat for a character device" do
stat = File.stat("/dev/null")
stat.blockdev?.should be_false
stat.chardev?.should be_true
stat.directory?.should be_false
stat.file?.should be_false
stat.symlink?.should be_false
stat.socket?.should be_false
stat.type.should eq(File::Type::CharacterDevice)
end

it "gets stat for a symlink" do
stat = File.lstat("#{__DIR__}/data/symlink.txt")
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_false
stat.symlink?.should be_true
stat.socket?.should be_false
stat.type.should eq(File::Type::SymbolicLink)
end

it "gets stat for open file" do
File.open(__FILE__, "r") do |file|
stat = file.stat
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_true
stat.symlink?.should be_false
stat.socket?.should be_false
stat.pipe?.should be_false
stat.type.should eq(File::Type::File)
end
end

it "gets stat for pipe" do
IO.pipe do |r, w|
r.stat.pipe?.should be_true
w.stat.pipe?.should be_true
r.stat.type.should eq(File::Type::Pipe)
w.stat.type.should eq(File::Type::Pipe)
end
end

Expand All @@ -421,9 +406,8 @@ describe "File" do
it "gets stat mtime for new file" do
tmp = Tempfile.new "tmp"
begin
(tmp.stat.atime - Time.utc_now).total_seconds.should be < 5
(tmp.stat.ctime - Time.utc_now).total_seconds.should be < 5
(tmp.stat.mtime - Time.utc_now).total_seconds.should be < 5
tmp.stat.modification_time.should be_close(Time.now, 5.seconds)
File.stat(tmp.path).modification_time.should be_close(Time.now, 5.seconds)
ensure
tmp.delete
end
Expand Down Expand Up @@ -680,11 +664,20 @@ describe "File" do
end
end

it "opens with perm" do
it "opens with perm (int)" do
filename = "#{__DIR__}/data/temp_write.txt"
perm = 0o600
File.open(filename, "w", perm) do |file|
file.stat.perm.should eq(perm)
file.stat.permissions.should eq(perm)
end
File.delete filename
end

it "opens with perm (File::Permissions)" do
filename = "#{__DIR__}/data/temp_write.txt"
perm = File::Permissions.flags(OwnerRead, OwnerWrite)
File.open(filename, "w", perm) do |file|
file.stat.permissions.should eq(perm)
end
File.delete filename
end
Expand Down Expand Up @@ -1039,8 +1032,7 @@ describe "File" do
File.utime(atime, mtime, filename)

stat = File.stat(filename)
stat.atime.should eq(atime)
stat.mtime.should eq(mtime)
stat.modification_time.should eq(mtime)

File.delete filename
end
Expand Down Expand Up @@ -1074,8 +1066,7 @@ describe "File" do
File.touch(filename, time)

stat = File.stat(filename)
stat.atime.should eq(time)
stat.mtime.should eq(time)
stat.modification_time.should eq(time)
ensure
File.delete filename
end
Expand All @@ -1088,8 +1079,7 @@ describe "File" do
File.touch(filename)

stat = File.stat(filename)
stat.atime.should be_close(time, 1.second)
stat.mtime.should be_close(time, 1.second)
stat.modification_time.should be_close(time, 1.second)
ensure
File.delete filename
end
Expand Down Expand Up @@ -1221,4 +1211,13 @@ describe "File" do
File.match?("ab{{c,d}ef,}", "abdef").should be_true
end
end

describe File::Permissions do
it "does to_s" do
perm = File::Permissions.flags(OwnerAll, GroupRead, GroupWrite, OtherRead)
perm.to_s.should eq("rwxrw-r-- (0o764)")
perm.inspect.should eq("rwxrw-r-- (0o764)")
perm.pretty_inspect.should eq("rwxrw-r-- (0o764)")
end
end
end
17 changes: 17 additions & 0 deletions spec/std/file_utils_spec.cr
Expand Up @@ -136,6 +136,23 @@ describe "FileUtils" do
end
end

it "copies permissions" do
src_path = File.join(__DIR__, "data/new_test_file.txt")
out_path = File.join(__DIR__, "data/test_file_cp.txt")
begin
File.write(src_path, "foo")
File.chmod(src_path, 0o700)

FileUtils.cp(src_path, out_path)

File.stat(out_path).permissions.should eq(0o700)
FileUtils.cmp(src_path, out_path).should be_true
ensure
File.delete(src_path) if File.exists?(out_path)
File.delete(out_path) if File.exists?(out_path)
end
end

it "raises an error if the directory doesn't exists" do
expect_raises(ArgumentError, "No such directory : not_existing_dir") do
FileUtils.cp({File.join(__DIR__, "data/test_file.text")}, "not_existing_dir")
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/cache_dir.cr
Expand Up @@ -107,7 +107,7 @@ module Crystal
private def cleanup_dirs(entries)
entries
.select { |dir| Dir.exists?(dir) }
.sort_by! { |dir| File.stat(dir).mtime rescue Time.epoch(0) }
.sort_by! { |dir| File.stat?(dir).try(&.modification_time) || Time.epoch(0) }
.reverse!
.skip(10)
.each { |name| `rm -rf "#{name}"` rescue nil }
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/macros/macros.cr
Expand Up @@ -158,7 +158,7 @@ class Crystal::Program
# Together with their timestamp
# (this is the list of all effective files that were required)
requires_with_timestamps = result.program.requires.map do |required_file|
epoch = File.stat(required_file).mtime.epoch
epoch = File.stat(required_file).modification_time.epoch
RequireWithTimestamp.new(required_file, epoch)
end

Expand Down Expand Up @@ -203,7 +203,7 @@ class Crystal::Program
end

new_requires_with_timestamps = required_files.map do |required_file|
epoch = File.stat(required_file).mtime.epoch
epoch = File.stat(required_file).modification_time.epoch
RequireWithTimestamp.new(required_file, epoch)
end

Expand Down
53 changes: 48 additions & 5 deletions src/crystal/system/unix/file.cr
Expand Up @@ -70,26 +70,69 @@ module Crystal::System::File
tmpdir.rchop(::File::SEPARATOR)
end

def self.stat?(path : String) : ::File::Stat?
def self.stat?(path : String) : ::File::Info?
if LibC.stat(path.check_no_null_byte, out stat) != 0
if {Errno::ENOENT, Errno::ENOTDIR}.includes? Errno.value
return nil
else
raise Errno.new("Unable to get stat for '#{path}'")
end
end
::File::Stat.new(stat)

to_file_info(stat)
end

def self.lstat?(path : String) : ::File::Stat?
def self.lstat?(path : String) : ::File::Info?
if LibC.lstat(path.check_no_null_byte, out stat) != 0
if {Errno::ENOENT, Errno::ENOTDIR}.includes? Errno.value
return nil
else
raise Errno.new("Unable to get lstat for '#{path}'")
end
end
::File::Stat.new(stat)

to_file_info(stat)
end

def self.to_file_info(stat)
size = stat.st_size.to_u64

permissions = ::File::Permissions.new((stat.st_mode & 0o777).to_i16)

case stat.st_mode & LibC::S_IFMT
when LibC::S_IFBLK
type = ::File::Type::BlockDevice
when LibC::S_IFCHR
type = ::File::Type::CharacterDevice
when LibC::S_IFDIR
type = ::File::Type::Directory
when LibC::S_IFIFO
type = ::File::Type::Pipe
when LibC::S_IFLNK
type = ::File::Type::SymbolicLink
when LibC::S_IFREG
type = ::File::Type::File
when LibC::S_IFSOCK
type = ::File::Type::Socket
else
raise "BUG: unknown File::Type"
end

flags = ::File::Flags::None
flags |= ::File::Flags::SetUser if (stat.st_mode & LibC::S_ISUID) != 0
flags |= ::File::Flags::SetGroup if (stat.st_mode & LibC::S_ISGID) != 0
flags |= ::File::Flags::Sticky if (stat.st_mode & LibC::S_ISVTX) != 0

{% if flag?(:darwin) %}
modification_time = ::Time.new(stat.st_mtimespec, ::Time::Kind::Utc)
{% else %}
modification_time = ::Time.new(stat.st_mtim, ::Time::Kind::Utc)
{% end %}

owner = stat.st_uid.to_u32
group = stat.st_gid.to_u32

::File::Info.new(size, permissions, type, flags, modification_time, owner, group)
end

def self.exists?(path)
Expand Down Expand Up @@ -121,7 +164,7 @@ module Crystal::System::File
raise Errno.new("Error changing owner of '#{path}'") if ret == -1
end

def self.chmod(path, mode : Int)
def self.chmod(path, mode)
if LibC.chmod(path, mode) == -1
raise Errno.new("Error changing permissions of '#{path}'")
end
Expand Down
3 changes: 2 additions & 1 deletion src/crystal/system/unix/file_descriptor.cr
Expand Up @@ -61,7 +61,8 @@ module Crystal::System::FileDescriptor
if LibC.fstat(@fd, out stat) != 0
raise Errno.new("Unable to get stat")
end
::File::Stat.new(stat)

File.to_file_info(stat)
end

private def system_seek(offset, whence : IO::Seek) : Nil
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/unix/getrandom.cr
Expand Up @@ -15,7 +15,7 @@ module Crystal::System::Random
@@getrandom_available = true
else
urandom = ::File.open("/dev/urandom", "r")
return unless urandom.stat.chardev?
return unless urandom.stat.type.character_device?

urandom.close_on_exec = true
urandom.sync = true # don't buffer bytes
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/unix/urandom.cr
Expand Up @@ -9,7 +9,7 @@ module Crystal::System::Random
@@initialized = true

urandom = ::File.open("/dev/urandom", "r")
return unless urandom.stat.chardev?
return unless urandom.stat.type.character_device?

urandom.close_on_exec = true
urandom.sync = true # don't buffer bytes
Expand Down
2 changes: 1 addition & 1 deletion src/dir.cr
Expand Up @@ -194,7 +194,7 @@ class Dir
# Returns `true` if the given path exists and is a directory
def self.exists?(path) : Bool
if stat = File.stat?(path)
stat.directory?
stat.type.directory?
else
false
end
Expand Down
10 changes: 5 additions & 5 deletions src/dir/glob.cr
Expand Up @@ -267,11 +267,11 @@ class Dir
end

private def self.dir?(path)
return true unless path
stat = File.lstat(path)
stat.directory? && !stat.symlink?
rescue Errno
false
if stat = File.lstat?(path)
stat.type.directory?
else
false
end
end

private def self.join(path, entry)
Expand Down

0 comments on commit 030495b

Please sign in to comment.