Skip to content

Commit

Permalink
Merge pull request #3414 from rubinius/file-birthtime
Browse files Browse the repository at this point in the history
Implemented File birthtime
  • Loading branch information
brixen committed May 28, 2015
2 parents 6304e7d + 7843e68 commit af66706
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 66 deletions.
4 changes: 4 additions & 0 deletions configure
Expand Up @@ -1230,6 +1230,10 @@ int main() { return tgetnum(""); }
@defines << "HAVE_GETTID"
end

if has_struct_member("stat", "st_birthtimespec", ["sys/stat.h"])
@defines << "HAVE_ST_BIRTHTIME"
end

# glibc has useless lchmod() so we don't try to use lchmod() on linux
if !@linux and has_function("lchmod", ["sys/stat.h", "unistd.h"])
@have_lchmod = true
Expand Down
5 changes: 5 additions & 0 deletions kernel/bootstrap/stat.rb
Expand Up @@ -87,6 +87,11 @@ def ctime
raise PrimitiveFailure, "Rubinius::Stat#ctime primitive failed"
end

def birthtime
Rubinius.primitive :stat_birthtime
raise NotImplementedError, "birthtime() function is unimplemented on this machine"
end

def inspect
"#<#{self.class.name} dev=0x#{self.dev.to_s(16)}, ino=#{self.ino}, " \
"mode=#{sprintf("%07d", self.mode.to_s(8).to_i)}, nlink=#{self.nlink}, " \
Expand Down
123 changes: 72 additions & 51 deletions kernel/common/file.rb
Expand Up @@ -103,6 +103,58 @@ def self.atime(path)
Stat.new(path).atime
end

##
# Returns the change time for the named file (the
# time at which directory information about the
# file was changed, not the file itself).
#
# File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
def self.ctime(path)
Stat.new(path).ctime
end

##
# Returns the birthtime for the named file
#
# Note this is not supported on all platforms.
# See stat(2) for more information.
def self.birthtime(path)
Stat.new(path).birthtime
end

##
# Returns the modification time for the named file as a Time object.
#
# File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
def self.mtime(path)
Stat.new(path).mtime
end

##
# Sets the access and modification times of each named
# file to the first two arguments. Returns the number
# of file names in the argument list.
# #=> Integer
def self.utime(a_in, m_in, *paths)
a_in ||= Time.now
m_in ||= Time.now
FFI::MemoryPointer.new(POSIX::TimeVal, 2) do |ptr|
atime = POSIX::TimeVal.new ptr
mtime = POSIX::TimeVal.new ptr[1]
atime[:tv_sec] = a_in.to_i
atime[:tv_usec] = 0

mtime[:tv_sec] = m_in.to_i
mtime[:tv_usec] = 0

paths.each do |path|

n = POSIX.utimes(Rubinius::Type.coerce_to_path(path), ptr)
Errno.handle unless n == 0
end
end
end

##
# Returns the last component of the filename given
# in file_name, which must be formed using forward
Expand Down Expand Up @@ -304,16 +356,6 @@ def self.lchown(owner, group, *paths)
paths.size
end

##
# Returns the change time for the named file (the
# time at which directory information about the
# file was changed, not the file itself).
#
# File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
def self.ctime(path)
Stat.new(path).ctime
end

##
# Returns true if the named file is a directory, false otherwise.
#
Expand Down Expand Up @@ -816,14 +858,6 @@ def self.lstat(path)
Stat.lstat path
end

##
# Returns the modification time for the named file as a Time object.
#
# File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
def self.mtime(path)
Stat.new(path).mtime
end

def self.path(obj)
return obj.to_path if obj.respond_to? :to_path

Expand Down Expand Up @@ -1072,31 +1106,6 @@ def self.unlink(*paths)
paths.size
end

##
# Sets the access and modification times of each named
# file to the first two arguments. Returns the number
# of file names in the argument list.
# #=> Integer
def self.utime(a_in, m_in, *paths)
a_in ||= Time.now
m_in ||= Time.now
FFI::MemoryPointer.new(POSIX::TimeVal, 2) do |ptr|
atime = POSIX::TimeVal.new ptr
mtime = POSIX::TimeVal.new ptr[1]
atime[:tv_sec] = a_in.to_i
atime[:tv_usec] = 0

mtime[:tv_sec] = m_in.to_i
mtime[:tv_usec] = 0

paths.each do |path|

n = POSIX.utimes(Rubinius::Type.coerce_to_path(path), ptr)
Errno.handle unless n == 0
end
end
end

def self.world_readable?(path)
path = Rubinius::Type.coerce_to_path path
return nil unless exist? path
Expand Down Expand Up @@ -1222,10 +1231,30 @@ def initialize(path_or_fd, mode=undefined, perm=undefined, options=undefined)

private :initialize

##
# see File.atime
def atime
Stat.new(@path).atime
end

##
# see File.ctime
def ctime
Stat.new(@path).ctime
end

##
# see File.birthtime
def birthtime
Stat.new(@path).birthtime
end

##
# see File.mtime
def mtime
Stat.new(@path).mtime
end

def reopen(other, mode = 'r+')
rewind unless closed?
unless other.kind_of? IO
Expand All @@ -1234,10 +1263,6 @@ def reopen(other, mode = 'r+')
super(other, mode)
end

def ctime
Stat.new(@path).ctime
end

def flock(const)
const = Rubinius::Type.coerce_to const, Integer, :to_int

Expand All @@ -1251,10 +1276,6 @@ def lstat
Stat.lstat @path
end

def mtime
Stat.new(@path).mtime
end

def stat
Stat.fstat @descriptor
end
Expand Down
38 changes: 27 additions & 11 deletions spec/ruby/core/file/birthtime_spec.rb
Expand Up @@ -9,17 +9,25 @@
@file = nil
end

it "returns the birth time for the named file as a Time object" do
File.birthtime(@file)
File.birthtime(@file).should be_kind_of(Time)
end
platform_is :darwin do
it "returns the birth time for the named file as a Time object" do
File.birthtime(@file)
File.birthtime(@file).should be_kind_of(Time)
end

it "accepts an object that has a #to_path method" do
File.birthtime(mock_to_path(@file))
end

it "accepts an object that has a #to_path method" do
File.birthtime(mock_to_path(@file))
it "raises an Errno::ENOENT exception if the file is not found" do
lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
end
end

it "raises an Errno::ENOENT exception if the file is not found" do
lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
platform_is :windows, :linux, :openbsd, :freebsd, :netbsd do
it "raises an NotImplementedError" do
lambda { File.birthtime(@file) }.should raise_error(NotImplementedError)
end
end
end

Expand All @@ -33,8 +41,16 @@
@file = nil
end

it "returns the birth time for self" do
@file.birthtime
@file.birthtime.should be_kind_of(Time)
platform_is :darwin do
it "returns the birth time for self" do
@file.birthtime
@file.birthtime.should be_kind_of(Time)
end
end

platform_is :windows, :linux, :openbsd, :freebsd, :netbsd do
it "raises an NotImplementedError" do
lambda { @file.birthtime }.should raise_error(NotImplementedError)
end
end
end
4 changes: 0 additions & 4 deletions spec/tags/ruby/core/file/birthtime_tags.txt

This file was deleted.

8 changes: 8 additions & 0 deletions vm/builtin/stat.cpp
Expand Up @@ -77,5 +77,13 @@ namespace rubinius {
return Time::at(state, st_.st_ctime);
}

Object* Stat::stat_birthtime(STATE) {
#ifdef HAVE_ST_BIRTHTIME
return Time::at(state, st_.st_birthtimespec);
#else
return Primitives::failure();
#endif
}

}

3 changes: 3 additions & 0 deletions vm/builtin/stat.hpp
Expand Up @@ -73,6 +73,9 @@ namespace rubinius {
// Rubinius.primitive+ :stat_ctime
Time* stat_ctime(STATE);

// Rubinius.primitive+ :stat_birthtime
Object* stat_birthtime(STATE);

class Info : public TypeInfo {
public:
BASIC_TYPEINFO(TypeInfo)
Expand Down

0 comments on commit af66706

Please sign in to comment.