homefs 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/homefs/debug.rb +4 -0
 - data/lib/homefs/homefs.rb +110 -255
 - metadata +2 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 9aa5fb16b6856552286724fd6fd1e45f46bc5a6f
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: ca26c140a4ad789d383edbe43a7040d2fa23b8f4
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7294eef3cd9569a078789e8b148de97054584084e792deb5bee24d5c5d234314fc7ea6d0d7dc11bc69d93002ec1e34cf9e455edc54b5af75e9428e1517e87349
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 5b350ef36937e79201cf61c005acb21447ad014f51c96a1dd06f52753402e19d189e083f339fec10e1536e038bc41a257452878bc52f494ba6bbb1166ab75ddd
         
     | 
    
        data/lib/homefs/debug.rb
    CHANGED
    
    
    
        data/lib/homefs/homefs.rb
    CHANGED
    
    | 
         @@ -22,7 +22,6 @@ class HomeFS 
     | 
|
| 
       22 
22 
     | 
    
         
             
                def initialize(path, options = Hash.new)
         
     | 
| 
       23 
23 
     | 
    
         
             
                    @relpath = path
         
     | 
| 
       24 
24 
     | 
    
         
             
                    read_passwd
         
     | 
| 
       25 
     | 
    
         
            -
                    read_group
         
     | 
| 
       26 
25 
     | 
    
         
             
                end
         
     | 
| 
       27 
26 
     | 
    
         | 
| 
       28 
27 
     | 
    
         
             
                # Read or re-read /etc/passwd to update the internal table of home
         
     | 
| 
         @@ -55,30 +54,6 @@ class HomeFS 
     | 
|
| 
       55 
54 
     | 
    
         
             
                    }
         
     | 
| 
       56 
55 
     | 
    
         
             
                end
         
     | 
| 
       57 
56 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                # Read or re-read /etc/group to update the internal table of
         
     | 
| 
       59 
     | 
    
         
            -
                # group membership.
         
     | 
| 
       60 
     | 
    
         
            -
                # @return [void]
         
     | 
| 
       61 
     | 
    
         
            -
                def read_group
         
     | 
| 
       62 
     | 
    
         
            -
                    # Try to read from getent
         
     | 
| 
       63 
     | 
    
         
            -
                    group = `getent group`
         
     | 
| 
       64 
     | 
    
         
            -
                    if $? != 0
         
     | 
| 
       65 
     | 
    
         
            -
                        group = File.read('/etc/group')
         
     | 
| 
       66 
     | 
    
         
            -
                    end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                    @groups = Hash.new
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                    group.each_line.map { |line|
         
     | 
| 
       71 
     | 
    
         
            -
                        next if line.strip.empty?
         
     | 
| 
       72 
     | 
    
         
            -
                        _name, _, gid, members = line.split(':')
         
     | 
| 
       73 
     | 
    
         
            -
                        members = members.strip.split(',').map {|member| @usernames[member]}
         
     | 
| 
       74 
     | 
    
         
            -
                        [Integer(gid), members]
         
     | 
| 
       75 
     | 
    
         
            -
                    }.reject { |line|
         
     | 
| 
       76 
     | 
    
         
            -
                        line.nil?
         
     | 
| 
       77 
     | 
    
         
            -
                    }.each { |gid, members|
         
     | 
| 
       78 
     | 
    
         
            -
                        @groups[gid] = members
         
     | 
| 
       79 
     | 
    
         
            -
                    }
         
     | 
| 
       80 
     | 
    
         
            -
                end
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
57 
     | 
    
         
             
                # Return the path to the root of HomeFS relative to the underlying
         
     | 
| 
       83 
58 
     | 
    
         
             
                # filesystem. If _path_ is specified, it is taken to be relative to the
         
     | 
| 
       84 
59 
     | 
    
         
             
                # root of HomeFS.
         
     | 
| 
         @@ -99,162 +74,28 @@ class HomeFS 
     | 
|
| 
       99 
74 
     | 
    
         
             
                    end
         
     | 
| 
       100 
75 
     | 
    
         
             
                end
         
     | 
| 
       101 
76 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                #  
     | 
| 
       103 
     | 
    
         
            -
                #  
     | 
| 
       104 
     | 
    
         
            -
                #  
     | 
| 
       105 
     | 
    
         
            -
                #  
     | 
| 
       106 
     | 
    
         
            -
                #  
     | 
| 
       107 
     | 
    
         
            -
                #  
     | 
| 
       108 
     | 
    
         
            -
                #  
     | 
| 
       109 
     | 
    
         
            -
                #
         
     | 
| 
       110 
     | 
    
         
            -
                 
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
                # It is recommended to see
         
     | 
| 
       120 
     | 
    
         
            -
                #   man chmod
         
     | 
| 
       121 
     | 
    
         
            -
                # to get a more detailed description about file modes.
         
     | 
| 
       122 
     | 
    
         
            -
                #
         
     | 
| 
       123 
     | 
    
         
            -
                # @example Test if we can write to a file
         
     | 
| 
       124 
     | 
    
         
            -
                #   mode_mask("/example", 0222, true)
         
     | 
| 
       125 
     | 
    
         
            -
                #   mode_mask("/example", 0222)
         
     | 
| 
       126 
     | 
    
         
            -
                # @example Test if a file is executable by anyone
         
     | 
| 
       127 
     | 
    
         
            -
                #   mode_mask("/binary", 0111, false)
         
     | 
| 
       128 
     | 
    
         
            -
                # @example Test if a directory has the sticky bit set
         
     | 
| 
       129 
     | 
    
         
            -
                #   mode_mask("/tmp", 01000, false)
         
     | 
| 
       130 
     | 
    
         
            -
                #
         
     | 
| 
       131 
     | 
    
         
            -
                # @param [String] file the path to the file to check
         
     | 
| 
       132 
     | 
    
         
            -
                # @param [Integer] mask the mask against which to check the file's mode.
         
     | 
| 
       133 
     | 
    
         
            -
                #   It is recommended you write this in octal (with a leading 0).
         
     | 
| 
       134 
     | 
    
         
            -
                # @param [Integer] uid if not nil, only check user and group permissions
         
     | 
| 
       135 
     | 
    
         
            -
                #   if the user it represents is, respectively, the owner of the
         
     | 
| 
       136 
     | 
    
         
            -
                #   file/in the file's group.
         
     | 
| 
       137 
     | 
    
         
            -
                # @return [Boolean] whether the mode of _file_ matches the given mask
         
     | 
| 
       138 
     | 
    
         
            -
                def mode_mask(file, mask, uid = nil)
         
     | 
| 
       139 
     | 
    
         
            -
                    stat = File.stat(file)
         
     | 
| 
       140 
     | 
    
         
            -
                    fmode = stat.mode
         
     | 
| 
       141 
     | 
    
         
            -
                    if uid
         
     | 
| 
       142 
     | 
    
         
            -
                        fuid, fgid = stat.uid, stat.gid
         
     | 
| 
       143 
     | 
    
         
            -
                        # Zero out the third digit (in octal).
         
     | 
| 
       144 
     | 
    
         
            -
                        # We could use a constant here, but this works
         
     | 
| 
       145 
     | 
    
         
            -
                        # for a mask of any length
         
     | 
| 
       146 
     | 
    
         
            -
                        if uid != fuid
         
     | 
| 
       147 
     | 
    
         
            -
                            mask &= ~(mask & 0700)
         
     | 
| 
       148 
     | 
    
         
            -
                        end
         
     | 
| 
       149 
     | 
    
         
            -
                        if !@groups[fgid].include?(uid)
         
     | 
| 
       150 
     | 
    
         
            -
                            mask &= ~(mask & 0070)
         
     | 
| 
      
 77 
     | 
    
         
            +
                # If HomeFS is running as root, drop_priv sets the effective user ID
         
     | 
| 
      
 78 
     | 
    
         
            +
                # and effective group ID to _uid_ and _gid_, respectively, and then
         
     | 
| 
      
 79 
     | 
    
         
            +
                # calls the block passed to the method. Before returning, drop_priv
         
     | 
| 
      
 80 
     | 
    
         
            +
                # that the EUID and EGID are each set back to 0.
         
     | 
| 
      
 81 
     | 
    
         
            +
                # @param [Integer] uid the user ID to set the effective user ID to
         
     | 
| 
      
 82 
     | 
    
         
            +
                # @param [Integer] gid the group ID to set the effective group ID to
         
     | 
| 
      
 83 
     | 
    
         
            +
                # @yield an environment with the EUID and EGID set to _uid_ and _gid_
         
     | 
| 
      
 84 
     | 
    
         
            +
                # @return the result of the block
         
     | 
| 
      
 85 
     | 
    
         
            +
                def drop_priv(uid, gid)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    if Process::Sys.getuid == 0
         
     | 
| 
      
 87 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 88 
     | 
    
         
            +
                            Process::Sys.setegid(gid)
         
     | 
| 
      
 89 
     | 
    
         
            +
                            Process::Sys.seteuid(uid)
         
     | 
| 
      
 90 
     | 
    
         
            +
                            ret = yield
         
     | 
| 
      
 91 
     | 
    
         
            +
                        ensure
         
     | 
| 
      
 92 
     | 
    
         
            +
                            Process::Sys.seteuid(0)
         
     | 
| 
      
 93 
     | 
    
         
            +
                            Process::Sys.setegid(0)
         
     | 
| 
       151 
94 
     | 
    
         
             
                        end
         
     | 
| 
       152 
     | 
    
         
            -
                    end
         
     | 
| 
       153 
     | 
    
         
            -
                    fmode & mask != 0
         
     | 
| 
       154 
     | 
    
         
            -
                end
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                # Test whether we can write to the given path. If no file exists at _path_,
         
     | 
| 
       157 
     | 
    
         
            -
                # we test if the parent directory is either writable or has the sticky bit
         
     | 
| 
       158 
     | 
    
         
            -
                # set.
         
     | 
| 
       159 
     | 
    
         
            -
                # @param [String] path the path to test
         
     | 
| 
       160 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of. If nil, this
         
     | 
| 
       161 
     | 
    
         
            -
                # method returns whether the file is writable by anyone.
         
     | 
| 
       162 
     | 
    
         
            -
                # @return [Boolean] whether the given path is writable
         
     | 
| 
       163 
     | 
    
         
            -
                def writable?(path, uid = nil)
         
     | 
| 
       164 
     | 
    
         
            -
                    if !File.exist?(path)
         
     | 
| 
       165 
     | 
    
         
            -
                        mode_mask(File.dirname(path), 01222, uid)
         
     | 
| 
       166 
95 
     | 
    
         
             
                    else
         
     | 
| 
       167 
     | 
    
         
            -
                         
     | 
| 
       168 
     | 
    
         
            -
                    end
         
     | 
| 
       169 
     | 
    
         
            -
                end
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
                # Test whether the user described by _uid_ can read from the given
         
     | 
| 
       172 
     | 
    
         
            -
                # path. If no file exists at the given path, an Errno::ENOENT is
         
     | 
| 
       173 
     | 
    
         
            -
                # raised.
         
     | 
| 
       174 
     | 
    
         
            -
                # @param [String] path the path to test
         
     | 
| 
       175 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of. If nil, this
         
     | 
| 
       176 
     | 
    
         
            -
                # method returns whether the file is readable by anyone.
         
     | 
| 
       177 
     | 
    
         
            -
                # @return [Boolean] whether the given path is readable
         
     | 
| 
       178 
     | 
    
         
            -
                # @raise [Errno::ENOENT] if no file exists at _path_
         
     | 
| 
       179 
     | 
    
         
            -
                def readable?(path, uid = nil)
         
     | 
| 
       180 
     | 
    
         
            -
                    mode_mask(path, 0444, uid)
         
     | 
| 
       181 
     | 
    
         
            -
                end
         
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
     | 
    
         
            -
                # Test whether the user described by _uid_ can execute the given
         
     | 
| 
       184 
     | 
    
         
            -
                # path. If no file exists at the given path, an Errno::ENOENT is
         
     | 
| 
       185 
     | 
    
         
            -
                # raised.
         
     | 
| 
       186 
     | 
    
         
            -
                # @param [String] path the path to test
         
     | 
| 
       187 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of. If nil, this
         
     | 
| 
       188 
     | 
    
         
            -
                # method returns whether the file is executable by anyone.
         
     | 
| 
       189 
     | 
    
         
            -
                # @return [Boolean] whether the given path is executable
         
     | 
| 
       190 
     | 
    
         
            -
                # @raise [Errno::ENOENT] if no file exists at _path_
         
     | 
| 
       191 
     | 
    
         
            -
                def executable?(path, uid = nil)
         
     | 
| 
       192 
     | 
    
         
            -
                    mode_mask(path, 0111, uid)
         
     | 
| 
       193 
     | 
    
         
            -
                end
         
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
       195 
     | 
    
         
            -
                # This method raises an Errno::EACCES if the user given by _uid_ cannot
         
     | 
| 
       196 
     | 
    
         
            -
                # write to the given path. The check is the same as in {#writable?}.
         
     | 
| 
       197 
     | 
    
         
            -
                # @param [String] relpath the path to test, relative to the root of
         
     | 
| 
       198 
     | 
    
         
            -
                # HomeFS (not the actual filesystem)
         
     | 
| 
       199 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of
         
     | 
| 
       200 
     | 
    
         
            -
                # @return [void]
         
     | 
| 
       201 
     | 
    
         
            -
                # @raise [Errno::EACCES] if the path is not writable by the given user
         
     | 
| 
       202 
     | 
    
         
            -
                def check_writable(uid, relpath)
         
     | 
| 
       203 
     | 
    
         
            -
                    hpath = homepath(uid, relpath)
         
     | 
| 
       204 
     | 
    
         
            -
                    unless writable?(hpath, uid)
         
     | 
| 
       205 
     | 
    
         
            -
                        fail Errno::EACCES, relpath
         
     | 
| 
       206 
     | 
    
         
            -
                    end
         
     | 
| 
       207 
     | 
    
         
            -
                end
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                # This method raises an Errno::EACCES if the user given by _uid_ cannot
         
     | 
| 
       210 
     | 
    
         
            -
                # read from the given path.
         
     | 
| 
       211 
     | 
    
         
            -
                # @param [String] relpath the path to test, relative to the root of
         
     | 
| 
       212 
     | 
    
         
            -
                # HomeFS (not the actual filesystem)
         
     | 
| 
       213 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of
         
     | 
| 
       214 
     | 
    
         
            -
                # @return [void]
         
     | 
| 
       215 
     | 
    
         
            -
                # @raise [Errno::EACCES] if the path is not readable by the given user
         
     | 
| 
       216 
     | 
    
         
            -
                # @raise [Errno::ENOENT] if no file or directory exists at the given
         
     | 
| 
       217 
     | 
    
         
            -
                # path
         
     | 
| 
       218 
     | 
    
         
            -
                def check_readable(uid, relpath)
         
     | 
| 
       219 
     | 
    
         
            -
                    hpath = homepath(uid, relpath)
         
     | 
| 
       220 
     | 
    
         
            -
                    unless readable?(hpath, uid)
         
     | 
| 
       221 
     | 
    
         
            -
                        fail Errno::EACCES, relpath
         
     | 
| 
       222 
     | 
    
         
            -
                    end
         
     | 
| 
       223 
     | 
    
         
            -
                end
         
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
                # This method raises an Errno::EACCES if the user given by _uid_ cannot
         
     | 
| 
       226 
     | 
    
         
            -
                # list the given directory, i.e., if the user does not have the execute
         
     | 
| 
       227 
     | 
    
         
            -
                # and read permissions on the given directory. If the path given is not
         
     | 
| 
       228 
     | 
    
         
            -
                # a directory, the dirname of the given path is tested.
         
     | 
| 
       229 
     | 
    
         
            -
                # @param [String] relpath the path to test, relative to the root of
         
     | 
| 
       230 
     | 
    
         
            -
                # HomeFS (not the actual filesystem)
         
     | 
| 
       231 
     | 
    
         
            -
                # @param [Integer] uid the UID to check the privilage of
         
     | 
| 
       232 
     | 
    
         
            -
                # @return [void]
         
     | 
| 
       233 
     | 
    
         
            -
                # @raise [Errno::EACCES] if the path is not readable by the given user
         
     | 
| 
       234 
     | 
    
         
            -
                def check_listable(uid, relpath)
         
     | 
| 
       235 
     | 
    
         
            -
                    hpath = homepath(uid, relpath)
         
     | 
| 
       236 
     | 
    
         
            -
                    unless File.directory?(hpath)
         
     | 
| 
       237 
     | 
    
         
            -
                        hpath = File.dirname(hpath)
         
     | 
| 
       238 
     | 
    
         
            -
                    end
         
     | 
| 
       239 
     | 
    
         
            -
                    unless readable?(hpath) && executable?(hpath)
         
     | 
| 
       240 
     | 
    
         
            -
                        fail Errno::EACCES, relpath
         
     | 
| 
       241 
     | 
    
         
            -
                    end
         
     | 
| 
       242 
     | 
    
         
            -
                end
         
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
                # This method raises an Errno::EACCES if the user given by _uid_ is not
         
     | 
| 
       245 
     | 
    
         
            -
                # the owner of the file described by _relpath_
         
     | 
| 
       246 
     | 
    
         
            -
                # @param [String] relpath the path to test, relative to the root of
         
     | 
| 
       247 
     | 
    
         
            -
                # HomeFS (not the actual filesystem)
         
     | 
| 
       248 
     | 
    
         
            -
                # @param [Integer] uid the UID to check ownership of
         
     | 
| 
       249 
     | 
    
         
            -
                # @return [void]
         
     | 
| 
       250 
     | 
    
         
            -
                # @raise [Errno::EACCES] if the path is not owned by the given user
         
     | 
| 
       251 
     | 
    
         
            -
                # @raise [Errno::ENOENT] if no file or directory exists at the given
         
     | 
| 
       252 
     | 
    
         
            -
                # path
         
     | 
| 
       253 
     | 
    
         
            -
                def check_owner(uid, relpath)
         
     | 
| 
       254 
     | 
    
         
            -
                    hpath = File.dirname(homepath(uid, relpath))
         
     | 
| 
       255 
     | 
    
         
            -
                    unless File.stat(hpath).uid == uid
         
     | 
| 
       256 
     | 
    
         
            -
                        fail Errno::EACCES, relpath
         
     | 
| 
      
 96 
     | 
    
         
            +
                        ret = yield
         
     | 
| 
       257 
97 
     | 
    
         
             
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
                    return ret
         
     | 
| 
       258 
99 
     | 
    
         
             
                end
         
     | 
| 
       259 
100 
     | 
    
         | 
| 
       260 
101 
     | 
    
         
             
                # Check access permissions
         
     | 
| 
         @@ -269,8 +110,16 @@ class HomeFS 
     | 
|
| 
       269 
110 
     | 
    
         
             
                #   called directly
         
     | 
| 
       270 
111 
     | 
    
         
             
                def access(context, path, mode)
         
     | 
| 
       271 
112 
     | 
    
         
             
                    uid = context.uid
         
     | 
| 
       272 
     | 
    
         
            -
                     
     | 
| 
       273 
     | 
    
         
            -
             
     | 
| 
      
 113 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 115 
     | 
    
         
            +
                        access = true
         
     | 
| 
      
 116 
     | 
    
         
            +
                        check_read = (mode & 4 != 0)
         
     | 
| 
      
 117 
     | 
    
         
            +
                        check_write = (mode & 2 != 0)
         
     | 
| 
      
 118 
     | 
    
         
            +
                        check_execute = (mode & 1 != 0)
         
     | 
| 
      
 119 
     | 
    
         
            +
                        access &&= File.readable?(hpath) if check_read
         
     | 
| 
      
 120 
     | 
    
         
            +
                        access &&= File.writable?(hpath) if check_write
         
     | 
| 
      
 121 
     | 
    
         
            +
                        access &&= File.executable?(hpath) if check_execute
         
     | 
| 
      
 122 
     | 
    
         
            +
                        access
         
     | 
| 
       274 
123 
     | 
    
         
             
                    end
         
     | 
| 
       275 
124 
     | 
    
         
             
                end
         
     | 
| 
       276 
125 
     | 
    
         | 
| 
         @@ -285,9 +134,11 @@ class HomeFS 
     | 
|
| 
       285 
134 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       286 
135 
     | 
    
         
             
                #   called directly
         
     | 
| 
       287 
136 
     | 
    
         
             
                def chmod(context, path, mode)
         
     | 
| 
       288 
     | 
    
         
            -
                     
     | 
| 
       289 
     | 
    
         
            -
                     
     | 
| 
       290 
     | 
    
         
            -
                     
     | 
| 
      
 137 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 138 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 140 
     | 
    
         
            +
                        File.chmod(mode, hpath)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    end
         
     | 
| 
       291 
142 
     | 
    
         
             
                end
         
     | 
| 
       292 
143 
     | 
    
         | 
| 
       293 
144 
     | 
    
         
             
                # Change file ownership
         
     | 
| 
         @@ -302,9 +153,11 @@ class HomeFS 
     | 
|
| 
       302 
153 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       303 
154 
     | 
    
         
             
                #   called directly
         
     | 
| 
       304 
155 
     | 
    
         
             
                def chown(context, path, uid, gid)
         
     | 
| 
       305 
     | 
    
         
            -
                     
     | 
| 
       306 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       307 
     | 
    
         
            -
                     
     | 
| 
      
 156 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 157 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 159 
     | 
    
         
            +
                        File.chown(mode, hpath)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
       308 
161 
     | 
    
         
             
                end
         
     | 
| 
       309 
162 
     | 
    
         | 
| 
       310 
163 
     | 
    
         
             
                # Create and open a file
         
     | 
| 
         @@ -321,23 +174,11 @@ class HomeFS 
     | 
|
| 
       321 
174 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       322 
175 
     | 
    
         
             
                #   called directly
         
     | 
| 
       323 
176 
     | 
    
         
             
                def create(context, path, mode, ffi)
         
     | 
| 
       324 
     | 
    
         
            -
                     
     | 
| 
       325 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       326 
     | 
    
         
            -
             
     | 
| 
       327 
     | 
    
         
            -
             
     | 
| 
       328 
     | 
    
         
            -
                     
     | 
| 
       329 
     | 
    
         
            -
                    # race-condition. To be more specific, if this filesystem is
         
     | 
| 
       330 
     | 
    
         
            -
                    # running as root (which it usually is), the file will be created
         
     | 
| 
       331 
     | 
    
         
            -
                    # owned by root. If the mode were set when the file is created,
         
     | 
| 
       332 
     | 
    
         
            -
                    # then an attacker could create a setuid file writable by anyone,
         
     | 
| 
       333 
     | 
    
         
            -
                    # and then, in the 'real' filesystem (not HomeFS), quickly write a
         
     | 
| 
       334 
     | 
    
         
            -
                    # small program that just executes /bin/bash. If the write
         
     | 
| 
       335 
     | 
    
         
            -
                    # completed before handle.chown was called, the attacker could have
         
     | 
| 
       336 
     | 
    
         
            -
                    # a setuid shell.
         
     | 
| 
       337 
     | 
    
         
            -
                    handle = File.new(hpath, File::CREAT | File::WRONLY, 0600)
         
     | 
| 
       338 
     | 
    
         
            -
                    handle.chown(context.uid, context.gid)
         
     | 
| 
       339 
     | 
    
         
            -
                    handle.chmod(mode)
         
     | 
| 
       340 
     | 
    
         
            -
                    ffi.fh = handle
         
     | 
| 
      
 177 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 178 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    ffi.fh = drop_priv(uid, context.gid) do
         
     | 
| 
      
 180 
     | 
    
         
            +
                        File.new(hpath, File::CREAT | File::WRONLY, mode)
         
     | 
| 
      
 181 
     | 
    
         
            +
                    end
         
     | 
| 
       341 
182 
     | 
    
         
             
                end
         
     | 
| 
       342 
183 
     | 
    
         | 
| 
       343 
184 
     | 
    
         
             
                # Get attributes of an open file
         
     | 
| 
         @@ -431,8 +272,11 @@ class HomeFS 
     | 
|
| 
       431 
272 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       432 
273 
     | 
    
         
             
                #   called directly
         
     | 
| 
       433 
274 
     | 
    
         
             
                def getattr(context, path)
         
     | 
| 
       434 
     | 
    
         
            -
                     
     | 
| 
       435 
     | 
    
         
            -
                     
     | 
| 
      
 275 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 276 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 277 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 278 
     | 
    
         
            +
                        File.lstat(hpath)
         
     | 
| 
      
 279 
     | 
    
         
            +
                    end
         
     | 
| 
       436 
280 
     | 
    
         
             
                end
         
     | 
| 
       437 
281 
     | 
    
         | 
| 
       438 
282 
     | 
    
         
             
                # Called when filesystem is initialised
         
     | 
| 
         @@ -457,20 +301,22 @@ class HomeFS 
     | 
|
| 
       457 
301 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       458 
302 
     | 
    
         
             
                #   called directly
         
     | 
| 
       459 
303 
     | 
    
         
             
                def link(context, from, to)
         
     | 
| 
       460 
     | 
    
         
            -
                     
     | 
| 
       461 
     | 
    
         
            -
                     
     | 
| 
       462 
     | 
    
         
            -
                     
     | 
| 
       463 
     | 
    
         
            -
             
     | 
| 
      
 304 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 305 
     | 
    
         
            +
                    hfrom = homepath(uid, from)
         
     | 
| 
      
 306 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 307 
     | 
    
         
            +
                        File.link(hfrom, to)
         
     | 
| 
      
 308 
     | 
    
         
            +
                    end
         
     | 
| 
       464 
309 
     | 
    
         
             
                end
         
     | 
| 
       465 
310 
     | 
    
         | 
| 
       466 
311 
     | 
    
         
             
                # (see RFuse::Fuse#mkdir)
         
     | 
| 
       467 
312 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       468 
313 
     | 
    
         
             
                #   called directly
         
     | 
| 
       469 
314 
     | 
    
         
             
                def mkdir(context, path, mode)
         
     | 
| 
       470 
     | 
    
         
            -
                     
     | 
| 
       471 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       472 
     | 
    
         
            -
                     
     | 
| 
       473 
     | 
    
         
            -
             
     | 
| 
      
 315 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 316 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 317 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 318 
     | 
    
         
            +
                        Dir.mkdir(hpath, mode)
         
     | 
| 
      
 319 
     | 
    
         
            +
                    end
         
     | 
| 
       474 
320 
     | 
    
         
             
                end
         
     | 
| 
       475 
321 
     | 
    
         | 
| 
       476 
322 
     | 
    
         
             
                # File open operation
         
     | 
| 
         @@ -488,24 +334,17 @@ class HomeFS 
     | 
|
| 
       488 
334 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       489 
335 
     | 
    
         
             
                #   called directly
         
     | 
| 
       490 
336 
     | 
    
         
             
                def open(context, path, ffi)
         
     | 
| 
       491 
     | 
    
         
            -
                     
     | 
| 
       492 
     | 
    
         
            -
             
     | 
| 
       493 
     | 
    
         
            -
                    elsif ffi.flags & File::WRONLY != 0
         
     | 
| 
       494 
     | 
    
         
            -
                        mask = 0222
         
     | 
| 
       495 
     | 
    
         
            -
                    else
         
     | 
| 
       496 
     | 
    
         
            -
                        mask = 0444
         
     | 
| 
       497 
     | 
    
         
            -
                    end
         
     | 
| 
       498 
     | 
    
         
            -
                    hpath = homepath(context.uid, path)
         
     | 
| 
       499 
     | 
    
         
            -
                    unless mode_mask(hpath, mask, context.uid)
         
     | 
| 
       500 
     | 
    
         
            -
                        fail Errno::EACCES, path
         
     | 
| 
       501 
     | 
    
         
            -
                    end
         
     | 
| 
      
 337 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 338 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
       502 
339 
     | 
    
         | 
| 
       503 
340 
     | 
    
         
             
                    # We pass the flags straight into File.open because the constants
         
     | 
| 
       504 
341 
     | 
    
         
             
                    # in RFuse::Fcntl and File::Constants have the same values, because
         
     | 
| 
       505 
342 
     | 
    
         
             
                    # they both have the same values as the open syscall. If this were
         
     | 
| 
       506 
343 
     | 
    
         
             
                    # not the case, we'd have the map the values of RFuse::Fcntl to
         
     | 
| 
       507 
344 
     | 
    
         
             
                    # their equivalents in File::Constants
         
     | 
| 
       508 
     | 
    
         
            -
                     
     | 
| 
      
 345 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 346 
     | 
    
         
            +
                        ffi.fh = File.open(hpath, ffi.flags)
         
     | 
| 
      
 347 
     | 
    
         
            +
                    end
         
     | 
| 
       509 
348 
     | 
    
         
             
                end
         
     | 
| 
       510 
349 
     | 
    
         | 
| 
       511 
350 
     | 
    
         
             
                # Open directory
         
     | 
| 
         @@ -524,8 +363,11 @@ class HomeFS 
     | 
|
| 
       524 
363 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       525 
364 
     | 
    
         
             
                #   called directly
         
     | 
| 
       526 
365 
     | 
    
         
             
                def opendir(context, path, ffi)
         
     | 
| 
       527 
     | 
    
         
            -
                     
     | 
| 
       528 
     | 
    
         
            -
                     
     | 
| 
      
 366 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 367 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 368 
     | 
    
         
            +
                    ffi.fh = drop_priv(uid, context.gid) do
         
     | 
| 
      
 369 
     | 
    
         
            +
                        Dir.new(hpath)
         
     | 
| 
      
 370 
     | 
    
         
            +
                    end
         
     | 
| 
       529 
371 
     | 
    
         
             
                end
         
     | 
| 
       530 
372 
     | 
    
         | 
| 
       531 
373 
     | 
    
         
             
                # Read data from an open file
         
     | 
| 
         @@ -547,7 +389,7 @@ class HomeFS 
     | 
|
| 
       547 
389 
     | 
    
         
             
                    else
         
     | 
| 
       548 
390 
     | 
    
         
             
                        ffi.fh.seek(offset, :SET)
         
     | 
| 
       549 
391 
     | 
    
         
             
                    end
         
     | 
| 
       550 
     | 
    
         
            -
                    ffi.fh.read(size)
         
     | 
| 
      
 392 
     | 
    
         
            +
                    ffi.fh.read(size) || ''
         
     | 
| 
       551 
393 
     | 
    
         
             
                end
         
     | 
| 
       552 
394 
     | 
    
         | 
| 
       553 
395 
     | 
    
         
             
                # (see RFuse::Fuse#readdir)
         
     | 
| 
         @@ -603,12 +445,12 @@ class HomeFS 
     | 
|
| 
       603 
445 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       604 
446 
     | 
    
         
             
                #   called directly
         
     | 
| 
       605 
447 
     | 
    
         
             
                def rename(context, from, to)
         
     | 
| 
       606 
     | 
    
         
            -
                     
     | 
| 
       607 
     | 
    
         
            -
                     
     | 
| 
       608 
     | 
    
         
            -
                     
     | 
| 
       609 
     | 
    
         
            -
                     
     | 
| 
       610 
     | 
    
         
            -
             
     | 
| 
       611 
     | 
    
         
            -
                     
     | 
| 
      
 448 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 449 
     | 
    
         
            +
                    hfrom = homepath(uid, from)
         
     | 
| 
      
 450 
     | 
    
         
            +
                    hto = homepath(uid, to)
         
     | 
| 
      
 451 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 452 
     | 
    
         
            +
                        FileUtils.mv(hfrom, hto, :force => true)
         
     | 
| 
      
 453 
     | 
    
         
            +
                    end
         
     | 
| 
       612 
454 
     | 
    
         
             
                end
         
     | 
| 
       613 
455 
     | 
    
         | 
| 
       614 
456 
     | 
    
         
             
                # Create a symbolic link
         
     | 
| 
         @@ -624,12 +466,10 @@ class HomeFS 
     | 
|
| 
       624 
466 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       625 
467 
     | 
    
         
             
                #   called directly
         
     | 
| 
       626 
468 
     | 
    
         
             
                def symlink(context, to, from)
         
     | 
| 
       627 
     | 
    
         
            -
                     
     | 
| 
       628 
     | 
    
         
            -
                    hfrom = homepath( 
     | 
| 
       629 
     | 
    
         
            -
                     
     | 
| 
       630 
     | 
    
         
            -
             
     | 
| 
       631 
     | 
    
         
            -
                        File.lchown(context.uid, context.gid, hfrom)
         
     | 
| 
       632 
     | 
    
         
            -
                    rescue
         
     | 
| 
      
 469 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 470 
     | 
    
         
            +
                    hfrom = homepath(uid, from)
         
     | 
| 
      
 471 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 472 
     | 
    
         
            +
                        File.symlink(to, hfrom)
         
     | 
| 
       633 
473 
     | 
    
         
             
                    end
         
     | 
| 
       634 
474 
     | 
    
         
             
                end
         
     | 
| 
       635 
475 
     | 
    
         | 
| 
         @@ -644,8 +484,11 @@ class HomeFS 
     | 
|
| 
       644 
484 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       645 
485 
     | 
    
         
             
                #   called directly
         
     | 
| 
       646 
486 
     | 
    
         
             
                def truncate(context, path, offset)
         
     | 
| 
       647 
     | 
    
         
            -
                     
     | 
| 
       648 
     | 
    
         
            -
                     
     | 
| 
      
 487 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 488 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 489 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 490 
     | 
    
         
            +
                        File.truncate(hpath, offset)
         
     | 
| 
      
 491 
     | 
    
         
            +
                    end
         
     | 
| 
       649 
492 
     | 
    
         
             
                end
         
     | 
| 
       650 
493 
     | 
    
         | 
| 
       651 
494 
     | 
    
         
             
                # Remove a file
         
     | 
| 
         @@ -658,8 +501,11 @@ class HomeFS 
     | 
|
| 
       658 
501 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       659 
502 
     | 
    
         
             
                #   called directly
         
     | 
| 
       660 
503 
     | 
    
         
             
                def unlink(context, path)
         
     | 
| 
       661 
     | 
    
         
            -
                     
     | 
| 
       662 
     | 
    
         
            -
                     
     | 
| 
      
 504 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 505 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 506 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 507 
     | 
    
         
            +
                        File.unlink(hpath)
         
     | 
| 
      
 508 
     | 
    
         
            +
                    end
         
     | 
| 
       663 
509 
     | 
    
         
             
                end
         
     | 
| 
       664 
510 
     | 
    
         | 
| 
       665 
511 
     | 
    
         
             
                # Change access/modification times of a file
         
     | 
| 
         @@ -674,8 +520,11 @@ class HomeFS 
     | 
|
| 
       674 
520 
     | 
    
         
             
                # @note This method should usually only be called by FUSE, and not
         
     | 
| 
       675 
521 
     | 
    
         
             
                #   called directly
         
     | 
| 
       676 
522 
     | 
    
         
             
                def utimens(context, path, actime, modtime)
         
     | 
| 
       677 
     | 
    
         
            -
                     
     | 
| 
       678 
     | 
    
         
            -
                     
     | 
| 
      
 523 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 524 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 525 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 526 
     | 
    
         
            +
                        File.utime(actime, modtime, hpath)
         
     | 
| 
      
 527 
     | 
    
         
            +
                    end
         
     | 
| 
       679 
528 
     | 
    
         
             
                end
         
     | 
| 
       680 
529 
     | 
    
         | 
| 
       681 
530 
     | 
    
         
             
                # Write data to an open file
         
     | 
| 
         @@ -717,9 +566,11 @@ begin 
     | 
|
| 
       717 
566 
     | 
    
         
             
                # @note This method is only defined if the gem 'ffi-xattr' is
         
     | 
| 
       718 
567 
     | 
    
         
             
                # available
         
     | 
| 
       719 
568 
     | 
    
         
             
                def getxattr(context, path, name)
         
     | 
| 
       720 
     | 
    
         
            -
                     
     | 
| 
       721 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       722 
     | 
    
         
            -
                     
     | 
| 
      
 569 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 570 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 571 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 572 
     | 
    
         
            +
                        check_nodata Xattr::Lib.get(hpath, false, name)
         
     | 
| 
      
 573 
     | 
    
         
            +
                    end
         
     | 
| 
       723 
574 
     | 
    
         
             
                end
         
     | 
| 
       724 
575 
     | 
    
         | 
| 
       725 
576 
     | 
    
         
             
                # Set extended attributes
         
     | 
| 
         @@ -737,9 +588,11 @@ begin 
     | 
|
| 
       737 
588 
     | 
    
         
             
                # @note This method is only defined if the gem 'ffi-xattr' is
         
     | 
| 
       738 
589 
     | 
    
         
             
                # available
         
     | 
| 
       739 
590 
     | 
    
         
             
                def setxattr(context, path, name, data, flags)
         
     | 
| 
       740 
     | 
    
         
            -
                     
     | 
| 
       741 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       742 
     | 
    
         
            -
                     
     | 
| 
      
 591 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 592 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 593 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 594 
     | 
    
         
            +
                        Xattr::Lib.set(hpath, false, name, data)
         
     | 
| 
      
 595 
     | 
    
         
            +
                    end
         
     | 
| 
       743 
596 
     | 
    
         
             
                end
         
     | 
| 
       744 
597 
     | 
    
         | 
| 
       745 
598 
     | 
    
         
             
                # List extended attributes
         
     | 
| 
         @@ -754,9 +607,11 @@ begin 
     | 
|
| 
       754 
607 
     | 
    
         
             
                # @note This method is only defined if the gem 'ffi-xattr' is
         
     | 
| 
       755 
608 
     | 
    
         
             
                # available
         
     | 
| 
       756 
609 
     | 
    
         
             
                def listxattr(context, path)
         
     | 
| 
       757 
     | 
    
         
            -
                     
     | 
| 
       758 
     | 
    
         
            -
                    hpath = homepath( 
     | 
| 
       759 
     | 
    
         
            -
                     
     | 
| 
      
 610 
     | 
    
         
            +
                    uid = context.uid
         
     | 
| 
      
 611 
     | 
    
         
            +
                    hpath = homepath(uid, path)
         
     | 
| 
      
 612 
     | 
    
         
            +
                    drop_priv(uid, context.gid) do
         
     | 
| 
      
 613 
     | 
    
         
            +
                        check_nodata Xattr::Lib.list(hpath, false)
         
     | 
| 
      
 614 
     | 
    
         
            +
                    end
         
     | 
| 
       760 
615 
     | 
    
         
             
                end
         
     | 
| 
       761 
616 
     | 
    
         | 
| 
       762 
617 
     | 
    
         
             
                # Remove extended attribute
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: homefs
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.3. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.3.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Dylan Frese
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2015-10- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-10-30 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: rfuse
         
     |