class VirtFS::Context

FS-specific state under which FS calls occur.

VirtFS maps an independent context instance to each Ruby thread group, and internally switches to it before dispatching target FS calls from that thread. This class implements the core functionality behind the FS context

Attributes

key[R]

Public Class Methods

new() click to toggle source
# File lib/virtfs/context.rb, line 11
def initialize
  @mount_points = []
  @fs_lookup    = {}
  @mount_mutex  = Mutex.new
  @dir_mutex    = Mutex.new
  @saved_root   = nil
  @saved_cwd    = nil
  @root         = VfsRealFile::SEPARATOR
  @cwd          = @root
  @key          = nil
end

Public Instance Methods

chdir(dir) click to toggle source

Change virtual filesystem working directory

@param dir [String] new dir to assign to virtfs cwd

# File lib/virtfs/context.rb, line 161
def chdir(dir)
  fs = path = nil
  @dir_mutex.synchronize do
    nwd = remove_root(local_path(dir, @cwd, @root), @root)
    fs, path = mount_lookup(nwd)
    @cwd = nwd
  end
  fs.dir_chdir(path) if fs.respond_to?(:dir_chdir)
end
chroot(dir) click to toggle source

Change virtual file system root, after which all root calls to mount point will be mapped to specified dir

@param dir [String] new dir to assign as virtfs context root @raise [SystemCallError] if specified dir does not exist

# File lib/virtfs/context.rb, line 115
def chroot(dir)
  raise SystemCallError.new(dir, Errno::ENOENT::Errno) unless dir_exist?(dir)
  @dir_mutex.synchronize do
    @root = full_path(dir, true, @cwd, @root)
    @cwd  = VfsRealFile::SEPARATOR
  end
  0
end
cwd_root() click to toggle source

Helper to change virtual filesystem working directory to filesystem root @api private

# File lib/virtfs/context.rb, line 236
def cwd_root
  @dir_mutex.synchronize do
    return @cwd, @root
  end
end
dir_exist?(dir) click to toggle source

@return [Boolean] indicating if specified dir exists

# File lib/virtfs/context.rb, line 149
def dir_exist?(dir)
  begin
    fs, p = path_lookup(dir)
  rescue Errno::ENOENT
    return false
  end
  VirtFS.fs_call(fs) { dir_exist?(p) }
end
fs_on(mount_point) click to toggle source
# File lib/virtfs/context.rb, line 97
def fs_on(mount_point)
  mp = full_path(mount_point, true, *cwd_root)
  mp += VfsRealFile::SEPARATOR unless mp.end_with?(VfsRealFile::SEPARATOR)
  @mount_mutex.synchronize do
    @fs_lookup[mp]
  end
end
getwd() click to toggle source

@return [String] current filesystem working directory

# File lib/virtfs/context.rb, line 172
def getwd
  @cwd
end
key=(val) click to toggle source

Set key used to uniquely identify the context

@param val [String] context identifier @raise [RuntimeError] if key already assigned

# File lib/virtfs/context.rb, line 27
def key=(val)
  @dir_mutex.synchronize do
    raise "Context already assigned to key: #{@key}" if !@key.nil? && !val.nil?
    @key = val
  end
end
mount(fs_instance, mount_point) click to toggle source

Mount the specified FS instance at the specified mount point. This registers specified fs to be accessed through the specified mount point via internal mechanisms. After this point any calls to this mount point through VirtFS under this context will be mapped through the specified fs instance

@param fs_instance [VirtFS::FS] instance of VirtFS implementation corresponding

to filesystem to mount

@param mount_point [String] path which to mount filesystem under

@raise [SystemCallError] if mount point cannot be resolved @raise [RuntimeError] if mount point is being used

# File lib/virtfs/context.rb, line 46
def mount(fs_instance, mount_point)
  mp_display  = mount_point

  raise "mount: invalid filesystem object #{fs_instance.class.name}" unless fs_instance.respond_to?(:mount_point)
  raise "mount: filesystem is busy" if fs_instance.mount_point

  begin
    mount_point = full_path(mount_point, true, *cwd_root)
  rescue Errno::ENOENT
    raise SystemCallError.new(mp_display, Errno::ENOENT::Errno)
  end
  mount_point += VfsRealFile::SEPARATOR unless mount_point.end_with?(VfsRealFile::SEPARATOR)

  @mount_mutex.synchronize do
    raise "mount: mount point #{mp_display} is busy" if @fs_lookup[mount_point]
    fs_instance.mount_point = mount_point
    @fs_lookup[mount_point] = fs_instance
    @mount_points.push(mount_point).sort_by!(&:length).reverse!
  end
  nil
end
mount_points() click to toggle source

@return [Array<String>] array of mount points

# File lib/virtfs/context.rb, line 85
def mount_points
  @mount_mutex.synchronize do
    @mount_points.collect do |p|
      if p == VfsRealFile::SEPARATOR
        VfsRealFile::SEPARATOR
      else
        p.chomp(VfsRealFile::SEPARATOR)
      end
    end
  end
end
mounted?(mount_point) click to toggle source

@return [Boolean] indicating if mount point is mounted

# File lib/virtfs/context.rb, line 106
def mounted?(mount_point)
  !fs_on(mount_point).nil?
end
path_lookup(path, raise_full_path = false, include_last = true) click to toggle source

Expand symbolic links and perform mount indirection look up.

@param path [String] path to lookup @param raise_full_path [Boolean] indicates if error should be raised if lookup fails @param include_last [Boolean] indicates if last path component should be returned

@raise [RunTimeError] if path could not be looked up and raise_full_path is true @raise [SystemCallError] if path could not be looked up

@api private @see mount_lookup @see expand_links

# File lib/virtfs/context.rb, line 189
def path_lookup(path, raise_full_path = false, include_last = true)
  mount_lookup(full_path(path, include_last, *cwd_root))
rescue Errno::ENOENT
  raise if raise_full_path
  # so we report the original path.
  raise SystemCallError.new(path, Errno::ENOENT::Errno)
end
restore_cwd_root(cwd, root) click to toggle source
# File lib/virtfs/context.rb, line 242
def restore_cwd_root(cwd, root)
  @dir_mutex.synchronize do
    @cwd  = cwd  if cwd
    @root = root if root
  end
end
umount(mount_point) click to toggle source

Unmount the FS mounted at the specified mount point

@param mount_point [String] mount point to unmount @raise [RuntimeError] if mount point is not mounted

# File lib/virtfs/context.rb, line 72
def umount(mount_point)
  mount_point = full_path(mount_point, true, *cwd_root)
  @mount_mutex.synchronize do
    mp_display = mount_point
    mount_point += VfsRealFile::SEPARATOR unless mount_point.end_with?(VfsRealFile::SEPARATOR)
    raise "umount: nothing mounted on #{mp_display}" unless @fs_lookup[mount_point]
    @fs_lookup.delete(mount_point).umount
    @mount_points.delete(mount_point)
  end
  nil
end
with_root(dir) { || ... } click to toggle source

Invoke block with the specified root, restoring before returning

@see chroot

# File lib/virtfs/context.rb, line 127
def with_root(dir)
  raise SystemCallError.new(dir, Errno::ENOENT::Errno) unless dir_exist?(dir)
  @dir_mutex.synchronize do
    raise "Cannot nest with_root blocks" unless @saved_root.nil?
    @saved_root = @root
    @saved_cwd  = @cwd
    @root = full_path(dir, true, @cwd, @root)
    @cwd  = VfsRealFile::SEPARATOR
  end
  begin
    yield
  ensure
    @dir_mutex.synchronize do
      @root       = @saved_root
      @cwd        = @saved_cwd
      @saved_root = nil
      @saved_cwd  = nil
    end
  end
end

Private Instance Methods

apply_root(path, root) click to toggle source
# File lib/virtfs/context.rb, line 288
def apply_root(path, root)
  return path if root == VfsRealFile::SEPARATOR
  VfsRealFile.join(root, path)
end
full_path(path, include_last, cwd, root) click to toggle source
# File lib/virtfs/context.rb, line 258
def full_path(path, include_last, cwd, root)
  expand_links(local_path(path, cwd, root), include_last)
end
local_path(path, cwd, root) click to toggle source
# File lib/virtfs/context.rb, line 251
def local_path(path, cwd, root)
  lpath = path || cwd
  lpath = VfsRealFile.join(cwd, path) if Pathname(path).relative?
  lpath = VirtFS.normalize_path(lpath)
  apply_root(lpath, root)
end
mount_lookup(path) click to toggle source

Mount indirection look up. Given a path, return its corresponding file system and the part of the path relative to that file system. It assumes symbolic links have already been expanded. @api private

# File lib/virtfs/context.rb, line 269
def mount_lookup(path) # private
  spath = "#{path}#{VfsRealFile::SEPARATOR}"
  @mount_mutex.synchronize do
    @mount_points.each do |mp|
      next if mp.length > spath.length
      next unless spath.start_with?(mp)
      return @fs_lookup[mp], path if mp == VfsRealFile::SEPARATOR  # root
      return @fs_lookup[mp], VfsRealFile::SEPARATOR if mp == spath # path is the mount point
      return @fs_lookup[mp], path.sub(mp, VfsRealFile::SEPARATOR)
    end
  end
  raise SystemCallError.new(path, Errno::ENOENT::Errno)
end
remove_root(path, root) click to toggle source
# File lib/virtfs/context.rb, line 293
def remove_root(path, root)
  return path if root == VfsRealFile::SEPARATOR
  return VfsRealFile::SEPARATOR if path == root
  return path unless under_root?(path, root)
  path.sub(root, "")
end
under_root?(path, root) click to toggle source
# File lib/virtfs/context.rb, line 283
def under_root?(path, root)
  return true if path == root
  path.start_with?(root + VfsRealFile::SEPARATOR)
end