lib/ffi/stat.rb in ffi-libfuse-0.0.1.rctest12 vs lib/ffi/stat.rb in ffi-libfuse-0.1.0.rc20220550

- old
+ new

@@ -1,30 +1,21 @@ # frozen_string_literal: true require_relative 'struct_wrapper' -require_relative 'stat/native' require_relative 'stat/constants' module FFI # Ruby representation of stat.h struct class Stat - class << self - # @return [Stat] Newly allocated stat representing a regular file - see {Stat#file} - def file(**fields) - new.file(**fields) - end - - # @return [Stat] Newly allocated stat representing a directory - see {Stat#dir} - def dir(**fields) - new.dir(**fields) - end - alias directory dir - end - - # We need to be a StructWrapper because of clash with #size + # Use a StructWrapper because of clash with #size and the ability to attach functions include StructWrapper + extend FFI::Library + ffi_lib FFI::Library::LIBC + + # stat/native will attach functions to Stat + require_relative 'stat/native' native_struct(Native) # @!attribute [rw] mode # @return [Integer] file mode (type | perms) @@ -54,20 +45,16 @@ # @return [Time] time of last modification # @!attribute [rw] ctime # @return [Time] time of last status change - time_members = Native - .members - .select { |m| m.to_s.start_with?('st_') && m.to_s.end_with?('timespec') } - .map { |m| m[3..-5].to_sym } + time_members = Native.members.select { |m| m.to_s =~ /^st_.*timespec$/ }.map { |m| m[3..-5].to_sym } ffi_attr_reader(*time_members, format: 'st_%sspec', &:time) ffi_attr_writer(*time_members, format: 'st_%sspec', simple: false) do |sec, nsec = 0| - t = self[__method__[0..-2].to_sym] - t.set_time(sec, nsec) + self[__method__[0..-2].to_sym].set_time(sec, nsec) end # Fill content for a regular file # @param [Integer] mode # @param [Integer] size @@ -85,12 +72,114 @@ # @param [Integer] nlink # @param [Integer] uid # @param [Integer] gid # @param [Hash] args additional system specific stat fields # @return [self] - def dir(mode:, nlink: 1, uid: Process.uid, gid: Process.gid, **args) + def dir(mode:, nlink: 3, uid: Process.uid, gid: Process.gid, **args) mode = ((S_IFDIR & S_IFMT) | (mode & 0o777)) fill(mode: mode, uid: uid, gid: gid, nlink: nlink, **args) end alias directory dir + + # Fill attributes from file (using native LIBC calls) + # @param [Integer|:to_s] file descriptor or a file path + # @param [Boolean] follow links + # @return [self] + def from(file, follow: true) + return fstat(file) if file.is_a?(Integer) + + return stat(file.to_s) if follow + + lstat(file.to_s) + end + + # @!method stat(path) + # Fill attributes from file, following links + # @param [:to_s] path a file path + # @raise [SystemCallError] on error + # @return [self] + + # @!method lstat(path) + # Fill attributes from file path, without following links + # @param [:to_s] path + # @raise [SystemCallError] on error + # @return [self] + + # @!method fstat(fileno) + # Fill attributes from file descriptor + # @param [:to_i] fileno file descriptor + # @raise [SystemCallError] on error + # @return [self] + + %i[stat lstat fstat].each do |m| + define_method(m) do |file| + res = self.class.send("native_#{m}", (m == :fstat ? file.to_i : file.to_s), native) + raise SystemCallError.new('', FFI::LastError.error) unless res.zero? + + self + end + end + + # Apply permissions mask to mode + # @param [Integer] mask (see umask) + # @param [Hash] overrides see {fill} + # @return self + def mask(mask = 0o4000, **overrides) + fill(mode: mode & (~mask), **overrides) + end + + def file? + mode & S_IFREG != 0 + end + + def directory? + mode & S_IFDIR != 0 + end + + def setuid? + mode & S_ISUID != 0 + end + + def setgid? + mode & S_ISGID != 0 + end + + def sticky? + mode & S_ISVTX != 0 + end + + class << self + # @!method file(stat,**fields) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#file + + # @!method dir(stat,**fields) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#dir + %i[file dir].each { |m| define_method(m) { |stat = new, **args| stat.send(m, **args) } } + alias directory dir + + # @!method from(file, stat = new(), follow: false) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#from + + # @!method stat(file, stat = new()) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#stat + + # @!method lstat(file, stat = new()) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#lstat + + # @!method fstat(file, stat = new()) + # @return [Stat] + # @raise [SystemCallError] + # @see Stat#fstat + %i[from stat lstat fstat].each { |m| define_method(m) { |file, stat = new, **args| stat.send(m, file, **args) } } + end end end