lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb
module RubySMB
class Server
module Share
module Provider
class VirtualDisk < Disk
# This object emulates Ruby's builtin File::Stat object but uses a virtual file system instead of the real
# local one. The current implementation is limited to only representing directories and standard files. All
# attributes are read-only and once the object is created, it is immutable.
class VirtualStat
# All of the keyword arguments are the keys of the attributes to set. The names are left as is, maintaining
# a direct 1 to 1 relationship. See the Ruby docs for File::Stat
# (https://ruby-doc.org/core-3.0.2/File/Stat.html) for a list of all the attributes that can be set. Some
# values are calculated based on others such as the mode readable? being calculated based on the mode.
def initialize(**kwargs)
# directory and file both default to being the opposite of each other, one or both can be specified
# but they can not both be true at the same time
is_dir = !!kwargs.fetch(:directory?, !kwargs.fetch(:file?, false)) # defaults to not file which defaults to false
is_fil = !!kwargs.fetch(:file?, !kwargs.fetch(:directory?, true)) # defaults to not directory which defaults to true
raise ArgumentError.new('must be either a file or a directory') unless is_dir ^ is_fil
@values = kwargs.dup
# the default is a directory
@values[:directory?] = !@values.delete(:file?) if @values.key?(:file?) # normalize on directory? if file? was specified.
@birthtime = kwargs[:birthtime] || Time.now
end
def blksize
@values.fetch(:blksize, 4096)
end
def blockdev?
false
end
def blocks
@values.fetch(:blocks, 0)
end
def chardev?
false
end
def pipe?
false
end
def socket?
false
end
def symlink?
false
end
def directory?
@values.fetch(:directory?, true)
end
def file?
!directory?
end
def ftype
raise Errno::ENOENT.new('No such file or directory') unless file? || directory?
file? ? 'file' : 'directory'
end
def size
@values.fetch(:size, 0)
end
def zero?
file? && size == 0
end
def nlink
@values.fetch(:nlink, 0)
end
def dev
@values[:dev] ||= rand(1..0xfe)
end
def ino
@values[:ino] ||= rand(1..0xfffe)
end
def gid
@values.fetch(:gid, Process.gid)
end
def grpowned?
gid == Process.gid
end
def uid
@values.fetch(:uid, Process.uid)
end
def owned?
uid == Process.uid
end
# last access time
def atime
@values.fetch(:atime, @birthtime)
end
# modification time
def mtime
@values.fetch(:mtime, @birthtime)
end
# change time
def ctime
@values.fetch(:ctime, @birthtime)
end
# the permission bits, normalized based on the standard GNU representation,
# see: https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
def mode
@values.fetch(:mode, (file? ? 0o644 : 0o755))
end
def setuid?
mode & 0o04000 != 0
end
def setgid?
mode & 0o02000 != 0
end
def sticky?
mode & 0o01000 != 0
end
def readable?
return true if owned? && (mode & 1 << 8 != 0)
return true if grpowned? && (mode & 1 << 5 != 0)
return true if world_readable?
return false
end
def world_readable?
mode & 1 << 2 != 0
end
def writable?
return true if owned? && (mode & 1 << 7 != 0)
return true if grpowned? && (mode & 1 << 4 != 0)
return true if world_writable?
return false
end
def world_writable?
mode & 1 << 1 != 0
end
def executable?
return true if owned? && (mode & 1 << 6 != 0)
return true if grpowned? && (mode & 1 << 3 != 0)
return true if mode & 1 != 0
return false
end
attr_reader :birthtime
end
end
end
end
end
end