diff --git a/src/crystal/system/file.cr b/src/crystal/system/file.cr new file mode 100644 index 000000000000..69747b58d2a2 --- /dev/null +++ b/src/crystal/system/file.cr @@ -0,0 +1,5 @@ +# :nodoc: +module Crystal::System::FileMixin +end + +require "./unix/file" diff --git a/src/crystal/system/unix/file.cr b/src/crystal/system/unix/file.cr new file mode 100644 index 000000000000..982cd9dc5513 --- /dev/null +++ b/src/crystal/system/unix/file.cr @@ -0,0 +1,236 @@ +require "c/sys/file" + +# :nodoc: +module Crystal::System + module File + extend self + + def open(filename, mode, perm) + oflag = open_flag(mode) | LibC::O_CLOEXEC + + fd = LibC.open(filename.check_no_null_byte, oflag, perm) + if fd < 0 + raise Errno.new("Error opening file '#{filename}' with mode '#{mode}'") + end + fd + end + + private def open_flag(mode) + if mode.size == 0 + raise "Invalid access mode #{mode}" + end + + m = 0 + o = 0 + case mode[0] + when 'r' + m = LibC::O_RDONLY + when 'w' + m = LibC::O_WRONLY + o = LibC::O_CREAT | LibC::O_TRUNC + when 'a' + m = LibC::O_WRONLY + o = LibC::O_CREAT | LibC::O_APPEND + else + raise "Invalid access mode #{mode}" + end + + case mode.size + when 1 + # Nothing + when 2 + case mode[1] + when '+' + m = LibC::O_RDWR + when 'b' + # Nothing + else + raise "Invalid access mode #{mode}" + end + else + raise "Invalid access mode #{mode}" + end + + oflag = m | o + end + + def mktemp(name, extension) + tmpdir = tempdir + ::File::SEPARATOR + path = "#{tmpdir}#{name}.XXXXXX#{extension}" + + if extension + fd = LibC.mkstemps(path, extension.bytesize) + else + fd = LibC.mkstemp(path) + end + + raise Errno.new("mkstemp") if fd == -1 + {fd, path} + end + + def tempdir + tmpdir = ENV["TMPDIR"]? || "/tmp" + tmpdir.rchop(::File::SEPARATOR) + end + + def stat(path) + if LibC.stat(path.check_no_null_byte, out stat) != 0 + raise Errno.new("Unable to get stat for '#{path}'") + end + ::File::Stat.new(stat) + end + + def lstat(path) + if LibC.lstat(path.check_no_null_byte, out stat) != 0 + raise Errno.new("Unable to get lstat for '#{path}'") + end + ::File::Stat.new(stat) + end + + def empty?(path) + begin + stat(path).size == 0 + rescue Errno + raise Errno.new("Error determining size of '#{path}'") + end + end + + def exists?(path) + accessible?(path, LibC::F_OK) + end + + def readable?(path) : Bool + accessible?(path, LibC::R_OK) + end + + def writable?(path) : Bool + accessible?(path, LibC::W_OK) + end + + def executable?(path) : Bool + accessible?(path, LibC::X_OK) + end + + private def accessible?(path, flag) + LibC.access(path.check_no_null_byte, flag) == 0 + end + + def file?(path) : Bool + if LibC.stat(path.check_no_null_byte, out stat) != 0 + if Errno.value == Errno::ENOENT + return false + else + raise Errno.new("stat") + end + end + ::File::Stat.new(stat).file? + end + + def chown(path, uid : Int, gid : Int, follow_symlinks) + 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 + + def chmod(path, mode : Int) + if LibC.chmod(path, mode) == -1 + raise Errno.new("Error changing permissions of '#{path}'") + end + end + + def delete(path) + err = LibC.unlink(path.check_no_null_byte) + if err == -1 + raise Errno.new("Error deleting file '#{path}'") + end + end + + def real_path(path) + real_path_ptr = LibC.realpath(path, nil) + raise Errno.new("Error resolving real path of #{path}") unless real_path_ptr + String.new(real_path_ptr).tap { LibC.free(real_path_ptr.as(Void*)) } + end + + def link(old_path, new_path) + ret = LibC.link(old_path.check_no_null_byte, new_path.check_no_null_byte) + raise Errno.new("Error creating link from #{old_path} to #{new_path}") if ret != 0 + ret + end + + def symlink(old_path, new_path) + ret = LibC.symlink(old_path.check_no_null_byte, new_path.check_no_null_byte) + raise Errno.new("Error creating symlink from #{old_path} to #{new_path}") if ret != 0 + ret + end + + def symlink?(path) + if LibC.lstat(path.check_no_null_byte, out stat) != 0 + if Errno.value == Errno::ENOENT + return false + else + raise Errno.new("stat") + end + end + (stat.st_mode & LibC::S_IFMT) == LibC::S_IFLNK + end + + def rename(old_filename, new_filename) + code = LibC.rename(old_filename.check_no_null_byte, new_filename.check_no_null_byte) + if code != 0 + raise Errno.new("Error renaming file '#{old_filename}' to '#{new_filename}'") + end + end + + def utime(atime : ::Time, mtime : ::Time, filename : String) : Nil + timevals = uninitialized LibC::Timeval[2] + timevals[0] = to_timeval(atime) + timevals[1] = to_timeval(mtime) + ret = LibC.utimes(filename, timevals) + if ret != 0 + raise Errno.new("Error setting time to file '#{filename}'") + end + end + + private def to_timeval(time : ::Time) + t = uninitialized LibC::Timeval + t.tv_sec = typeof(t.tv_sec).new(time.to_local.epoch) + t.tv_usec = typeof(t.tv_usec).new(0) + t + end + end + + module FileMixin + private def platform_truncate(size) : Nil + flush + code = LibC.ftruncate(fd, size) + if code != 0 + raise Errno.new("Error truncating file '#{path}'") + end + end + + private def platform_flock_shared(blocking) + flock LibC::FlockOp::SH, blocking + end + + private def platform_flock_exclusive(blocking) + flock LibC::FlockOp::EX, blocking + end + + private def platform_flock_unlock + flock LibC::FlockOp::UN + end + + private def flock(op : LibC::FlockOp, blocking : Bool = true) + op |= LibC::FlockOp::NB unless blocking + + if LibC.flock(@fd, op) != 0 + raise Errno.new("flock") + end + + nil + end + end +end diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index b0a89e47a889..7b66c7b77dfa 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -56,7 +56,7 @@ module Crystal::System::FileDescriptorMixin if LibC.fstat(@fd, out stat) != 0 raise Errno.new("Unable to get stat") end - File::Stat.new(stat) + ::File::Stat.new(stat) end private def platform_seek(offset, whence : IO::Seek) diff --git a/src/crystal/system/unix/getrandom.cr b/src/crystal/system/unix/getrandom.cr index 616345173347..becf2946afa4 100644 --- a/src/crystal/system/unix/getrandom.cr +++ b/src/crystal/system/unix/getrandom.cr @@ -6,7 +6,7 @@ require "c/sys/syscall" module Crystal::System::Random @@initialized = false @@getrandom_available = false - @@urandom : File? + @@urandom : ::File? private def self.init @@initialized = true @@ -14,7 +14,7 @@ module Crystal::System::Random if sys_getrandom(Bytes.new(16)) >= 0 @@getrandom_available = true else - urandom = File.open("/dev/urandom", "r") + urandom = ::File.open("/dev/urandom", "r") return unless urandom.stat.chardev? urandom.close_on_exec = true diff --git a/src/crystal/system/unix/urandom.cr b/src/crystal/system/unix/urandom.cr index 424641890d40..a11d076f5839 100644 --- a/src/crystal/system/unix/urandom.cr +++ b/src/crystal/system/unix/urandom.cr @@ -3,12 +3,12 @@ module Crystal::System::Random @@initialized = false - @@urandom : File? + @@urandom : ::File? private def self.init @@initialized = true - urandom = File.open("/dev/urandom", "r") + urandom = ::File.open("/dev/urandom", "r") return unless urandom.stat.chardev? urandom.close_on_exec = true diff --git a/src/file.cr b/src/file.cr index 38ad2c8bd3ee..38c0a1ceb9a0 100644 --- a/src/file.cr +++ b/src/file.cr @@ -1,10 +1,8 @@ -require "c/fcntl" -require "c/stdio" -require "c/stdlib" -require "c/sys/stat" -require "c/unistd" +require "crystal/system/file" class File < IO::FileDescriptor + private alias Sys = Crystal::System::File + # The file/directory separator character. `'/'` in Unix, `'\\'` in Windows. SEPARATOR = {% if flag?(:windows) %} '\\' @@ -22,6 +20,8 @@ class File < IO::FileDescriptor # :nodoc: DEFAULT_CREATE_MODE = LibC::S_IRUSR | LibC::S_IWUSR | LibC::S_IRGRP | LibC::S_IROTH + include Crystal::System::FileMixin + # This constructor is provided for subclasses to be able to initialize an # `IO::FileDescriptor` with a *path* and *fd*. private def initialize(@path, fd, blocking = false, encoding = nil, invalid = nil) @@ -29,56 +29,9 @@ class File < IO::FileDescriptor super(fd, blocking) end - def initialize(filename : String, mode = "r", perm = DEFAULT_CREATE_MODE, encoding = nil, invalid = nil) - oflag = open_flag(mode) | LibC::O_CLOEXEC - - fd = LibC.open(filename.check_no_null_byte, oflag, perm) - if fd < 0 - raise Errno.new("Error opening file '#{filename}' with mode '#{mode}'") - end - - @path = filename - self.set_encoding(encoding, invalid: invalid) if encoding - super(fd, blocking: true) - end - - protected def open_flag(mode) - if mode.size == 0 - raise "Invalid access mode #{mode}" - end - - m = 0 - o = 0 - case mode[0] - when 'r' - m = LibC::O_RDONLY - when 'w' - m = LibC::O_WRONLY - o = LibC::O_CREAT | LibC::O_TRUNC - when 'a' - m = LibC::O_WRONLY - o = LibC::O_CREAT | LibC::O_APPEND - else - raise "Invalid access mode #{mode}" - end - - case mode.size - when 1 - # Nothing - when 2 - case mode[1] - when '+' - m = LibC::O_RDWR - when 'b' - # Nothing - else - raise "Invalid access mode #{mode}" - end - else - raise "Invalid access mode #{mode}" - end - - oflag = m | o + def self.new(filename : String, mode = "r", perm = DEFAULT_CREATE_MODE, encoding = nil, invalid = nil) + fd = Sys.open(filename, mode, perm) + new(filename, fd, blocking: true, encoding: encoding, invalid: invalid) end getter path : String @@ -93,10 +46,7 @@ class File < IO::FileDescriptor # File.stat("foo").mtime # => 2015-09-23 06:24:19 UTC # ``` def self.stat(path) : Stat - if LibC.stat(path.check_no_null_byte, out stat) != 0 - raise Errno.new("Unable to get stat for '#{path}'") - end - Stat.new(stat) + Sys.stat(path) end # Returns a `File::Stat` object for the file given by *path* or raises @@ -109,10 +59,7 @@ class File < IO::FileDescriptor # File.lstat("foo").mtime # => 2015-09-23 06:24:19 UTC # ``` def self.lstat(path) : Stat - if LibC.lstat(path.check_no_null_byte, out stat) != 0 - raise Errno.new("Unable to get lstat for '#{path}'") - end - Stat.new(stat) + Sys.lstat(path) end # Returns `true` if *path* exists else returns `false` @@ -124,7 +71,7 @@ class File < IO::FileDescriptor # File.exists?("foo") # => true # ``` def self.exists?(path) : Bool - accessible?(path, LibC::F_OK) + Sys.exists?(path) end # Returns `true` if the file at *path* is empty, otherwise returns `false`. @@ -137,11 +84,7 @@ class File < IO::FileDescriptor # File.empty?("foo") # => false # ``` def self.empty?(path) : Bool - begin - stat(path).size == 0 - rescue Errno - raise Errno.new("Error determining size of '#{path}'") - end + Sys.empty?(path) end # Returns `true` if *path* is readable by the real user id of this process else returns `false`. @@ -151,7 +94,7 @@ class File < IO::FileDescriptor # File.readable?("foo") # => true # ``` def self.readable?(path) : Bool - accessible?(path, LibC::R_OK) + Sys.readable?(path) end # Returns `true` if *path* is writable by the real user id of this process else returns `false`. @@ -161,7 +104,7 @@ class File < IO::FileDescriptor # File.writable?("foo") # => true # ``` def self.writable?(path) : Bool - accessible?(path, LibC::W_OK) + Sys.writable?(path) end # Returns `true` if *path* is executable by the real user id of this process else returns `false`. @@ -171,12 +114,7 @@ class File < IO::FileDescriptor # File.executable?("foo") # => false # ``` def self.executable?(path) : Bool - accessible?(path, LibC::X_OK) - end - - # Convenience method to avoid code on LibC.access calls. Not meant to be called by users of this class. - private def self.accessible?(path, flag) - LibC.access(path.check_no_null_byte, flag) == 0 + Sys.executable?(path) end # Returns `true` if given *path* exists and is a file. @@ -189,14 +127,7 @@ class File < IO::FileDescriptor # File.file?("foobar") # => false # ``` def self.file?(path) : Bool - if LibC.stat(path.check_no_null_byte, out stat) != 0 - if Errno.value == Errno::ENOENT - return false - else - raise Errno.new("stat") - end - end - File::Stat.new(stat).file? + Sys.file?(path) end # Returns `true` if the given *path* exists and is a directory. @@ -280,13 +211,8 @@ class File < IO::FileDescriptor # 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 + def self.chown(path, uid : Int = -1, gid : Int = -1, follow_symlinks = false) + Sys.chown(path, uid, gid, follow_symlinks) end # Changes the permissions of the specified file. @@ -302,9 +228,7 @@ class File < IO::FileDescriptor # File.stat("foo").perm # => 0o700 # ``` def self.chmod(path, mode : Int) - if LibC.chmod(path, mode) == -1 - raise Errno.new("Error changing permissions of '#{path}'") - end + Sys.chmod(path, mode) end # Delete the file at *path*. Deleting non-existent file will raise an exception. @@ -315,10 +239,7 @@ class File < IO::FileDescriptor # File.delete("./bar") # raises Errno (No such file or directory) # ``` def self.delete(path) - err = LibC.unlink(path.check_no_null_byte) - if err == -1 - raise Errno.new("Error deleting file '#{path}'") - end + Sys.delete(path) end # Returns *filename*'s extension, or an empty string if it has no extension. @@ -389,36 +310,23 @@ class File < IO::FileDescriptor # Resolves the real path of *path* by following symbolic links. def self.real_path(path) : String - real_path_ptr = LibC.realpath(path, nil) - raise Errno.new("Error resolving real path of #{path}") unless real_path_ptr - String.new(real_path_ptr).tap { LibC.free(real_path_ptr.as(Void*)) } + Sys.real_path(path) end # Creates a new link (also known as a hard link) at *new_path* to an existing file # given by *old_path*. def self.link(old_path, new_path) - ret = LibC.link(old_path.check_no_null_byte, new_path.check_no_null_byte) - raise Errno.new("Error creating link from #{old_path} to #{new_path}") if ret != 0 - ret + Sys.link(old_path, new_path) end # Creates a symbolic link at *new_path* to an existing file given by *old_path. def self.symlink(old_path, new_path) - ret = LibC.symlink(old_path.check_no_null_byte, new_path.check_no_null_byte) - raise Errno.new("Error creating symlink from #{old_path} to #{new_path}") if ret != 0 - ret + Sys.symlink(old_path, new_path) end # Returns `true` if the *path* is a symbolic link. def self.symlink?(path) : Bool - if LibC.lstat(path.check_no_null_byte, out stat) != 0 - if Errno.value == Errno::ENOENT - return false - else - raise Errno.new("stat") - end - end - (stat.st_mode & LibC::S_IFMT) == LibC::S_IFLNK + Sys.symlink?(path) end # Opens the file named by *filename*. If a file is being created, its initial @@ -583,23 +491,13 @@ class File < IO::FileDescriptor # File.exists?("afile") # => false # File.exists?("afile.cr") # => true # ``` - def self.rename(old_filename, new_filename) - code = LibC.rename(old_filename.check_no_null_byte, new_filename.check_no_null_byte) - if code != 0 - raise Errno.new("Error renaming file '#{old_filename}' to '#{new_filename}'") - end - code + def self.rename(old_filename, new_filename) : Nil + Sys.rename(old_filename, new_filename) end # Sets the access and modification times of *filename*. def self.utime(atime : Time, mtime : Time, filename : String) : Nil - timevals = uninitialized LibC::Timeval[2] - timevals[0] = to_timeval(atime) - timevals[1] = to_timeval(mtime) - ret = LibC.utimes(filename, timevals) - if ret != 0 - raise Errno.new("Error setting time to file '#{filename}'") - end + Sys.utime(atime, mtime, filename) end # Attempts to set the access and modification times of the file named @@ -611,13 +509,6 @@ class File < IO::FileDescriptor utime time, time, filename end - private def self.to_timeval(time : Time) - t = uninitialized LibC::Timeval - t.tv_sec = typeof(t.tv_sec).new(time.to_local.epoch) - t.tv_usec = typeof(t.tv_usec).new(0) - t - end - # Return the size in bytes of the currently opened file. def size stat.size @@ -625,13 +516,9 @@ class File < IO::FileDescriptor # Truncates the file to the specified *size*. Requires that the current file is opened # for writing. - def truncate(size = 0) + def truncate(size = 0) : Nil flush - code = LibC.ftruncate(fd, size) - if code != 0 - raise Errno.new("Error truncating file '#{path}'") - end - code + platform_truncate(size) end # Yields an `IO` to read a section inside this file. @@ -661,6 +548,44 @@ class File < IO::FileDescriptor io << ">" io end + + # TODO: use fcntl/lockf instead of flock (which doesn't lock over NFS) + # TODO: always use non-blocking locks, yield fiber until resource becomes available + + def flock_shared(blocking = true) + flock_shared blocking + begin + yield + ensure + flock_unlock + end + end + + # Place a shared advisory lock. More than one process may hold a shared lock for a given file at a given time. + # `Errno::EWOULDBLOCK` is raised if *blocking* is set to `false` and an existing exclusive lock is set. + def flock_shared(blocking = true) + platform_flock_shared(blocking) + end + + def flock_exclusive(blocking = true) + flock_exclusive blocking + begin + yield + ensure + flock_unlock + end + end + + # Place an exclusive advisory lock. Only one process may hold an exclusive lock for a given file at a given time. + # `Errno::EWOULDBLOCK` is raised if *blocking* is set to `false` and any existing lock is set. + def flock_exclusive(blocking = true) + platform_flock_exclusive(blocking) + end + + # Remove an existing advisory lock held by this process. + def flock_unlock + platform_flock_unlock + end end require "./file/*" diff --git a/src/file/flock.cr b/src/file/flock.cr deleted file mode 100644 index b88634251ea5..000000000000 --- a/src/file/flock.cr +++ /dev/null @@ -1,61 +0,0 @@ -# TODO: use fcntl/lockf instead of flock (which doesn't lock over NFS) -# TODO: always use non-blocking locks, yield fiber until resource becomes available - -lib LibC - @[Flags] - enum FlockOp - SH = 0x1 - EX = 0x2 - NB = 0x4 - UN = 0x8 - end - - fun flock(fd : Int, op : FlockOp) : Int -end - -class File - def flock_shared(blocking = true) - flock_shared blocking - begin - yield - ensure - flock_unlock - end - end - - # Place a shared advisory lock. More than one process may hold a shared lock for a given file at a given time. - # `Errno::EWOULDBLOCK` is raised if *blocking* is set to `false` and an existing exclusive lock is set. - def flock_shared(blocking = true) - flock LibC::FlockOp::SH, blocking - end - - def flock_exclusive(blocking = true) - flock_exclusive blocking - begin - yield - ensure - flock_unlock - end - end - - # Place an exclusive advisory lock. Only one process may hold an exclusive lock for a given file at a given time. - # `Errno::EWOULDBLOCK` is raised if *blocking* is set to `false` and any existing lock is set. - def flock_exclusive(blocking = true) - flock LibC::FlockOp::EX, blocking - end - - # Remove an existing advisory lock held by this process. - def flock_unlock - flock LibC::FlockOp::UN - end - - private def flock(op : LibC::FlockOp, blocking : Bool = true) - op |= LibC::FlockOp::NB unless blocking - - if LibC.flock(@fd, op) != 0 - raise Errno.new("flock") - end - - nil - end -end diff --git a/src/lib_c/aarch64-linux-gnu/c/sys/file.cr b/src/lib_c/aarch64-linux-gnu/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/aarch64-linux-gnu/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/amd64-unknown-openbsd/c/sys/file.cr b/src/lib_c/amd64-unknown-openbsd/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/amd64-unknown-openbsd/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/arm-linux-gnueabihf/c/sys/file.cr b/src/lib_c/arm-linux-gnueabihf/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/arm-linux-gnueabihf/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/i686-linux-gnu/c/sys/file.cr b/src/lib_c/i686-linux-gnu/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/i686-linux-gnu/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/i686-linux-musl/c/sys/file.cr b/src/lib_c/i686-linux-musl/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/i686-linux-musl/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/x86_64-linux-gnu/c/sys/file.cr b/src/lib_c/x86_64-linux-gnu/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/x86_64-linux-gnu/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/x86_64-linux-musl/c/sys/file.cr b/src/lib_c/x86_64-linux-musl/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/x86_64-linux-musl/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/x86_64-macosx-darwin/c/sys/file.cr b/src/lib_c/x86_64-macosx-darwin/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/x86_64-macosx-darwin/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/lib_c/x86_64-portbld-freebsd/c/sys/file.cr b/src/lib_c/x86_64-portbld-freebsd/c/sys/file.cr new file mode 100644 index 000000000000..e2bd3fd4b816 --- /dev/null +++ b/src/lib_c/x86_64-portbld-freebsd/c/sys/file.cr @@ -0,0 +1,11 @@ +lib LibC + @[Flags] + enum FlockOp + SH = 0x1 + EX = 0x2 + NB = 0x4 + UN = 0x8 + end + + fun flock(fd : Int, op : FlockOp) : Int +end diff --git a/src/tempfile.cr b/src/tempfile.cr index 56e68c0dce0a..175f19a8d29f 100644 --- a/src/tempfile.cr +++ b/src/tempfile.cr @@ -43,17 +43,7 @@ class Tempfile < File # # *encoding* and *invalid* are passed to `IO#set_encoding`. def initialize(name, extension = nil, encoding = nil, invalid = nil) - tmpdir = self.class.dirname + File::SEPARATOR - path = "#{tmpdir}#{name}.XXXXXX#{extension}" - - if extension - fileno = LibC.mkstemps(path, extension.bytesize) - else - fileno = LibC.mkstemp(path) - end - - raise Errno.new("mkstemp") if fileno == -1 - + fileno, path = Crystal::System::File.mktemp(name, extension) super(path, fileno, blocking: true, encoding: encoding, invalid: invalid) end @@ -89,11 +79,7 @@ class Tempfile < File # Tempfile.dirname # => "/tmp" # ``` def self.dirname : String - unless tmpdir = ENV["TMPDIR"]? - tmpdir = "/tmp" - end - tmpdir = tmpdir + File::SEPARATOR unless tmpdir.ends_with? File::SEPARATOR - File.dirname(tmpdir) + Crystal::System::File.tempdir end # Deletes this tempfile.