homefs 0.1.0

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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/bin/homefs +53 -0
  3. data/lib/homefs/homefs.rb +172 -0
  4. data/lib/homefs.rb +1 -0
  5. metadata +51 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bd9b532279081ee10b0b4d63e5bf21e93f7316bf
4
+ data.tar.gz: 9bf006b0347c6e0a67f3b5e22de6bc2e3d16e916
5
+ SHA512:
6
+ metadata.gz: f011c0e7dd3ce87b40d9acdda973c3f1f931680ba6f57da40129b8cbe159c9fba6811d07dc93cd3cf5b08ec8c4b1de970c2c8ca635b0089df48d40b47c99c3c5
7
+ data.tar.gz: 5737dba4cc80076d2c99c8fea79ba9b4fdd0ef33b1f78bbf9230b30b2d5069885b829481d4e66b91e9013d0b180b6706e2d9dca8ba9e531fef4f0653bf15f3b7
data/bin/homefs ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'homefs'
4
+ require 'rb-inotify'
5
+ require 'fileutils'
6
+
7
+ if ARGV.size < 2
8
+ warn "Usage:\n\t#{File.basename($0)} relative_directory mountpoint [options...]"
9
+ exit 1
10
+ end
11
+
12
+ # Return immediately
13
+ if fork
14
+ exit 0
15
+ end
16
+
17
+ reldir = ARGV.shift
18
+
19
+ fs = HomeFS.new(reldir)
20
+
21
+ # Shut up
22
+ $stdout = File.open("/dev/null", "w")
23
+ $stderr = File.open("/dev/null", "w")
24
+
25
+ pid = fork
26
+ if pid
27
+ # The child process will be the one actually handling the FuseFS,
28
+ # so we want to exit as soon as it dies
29
+ trap 'CLD' do
30
+ exit 0
31
+ end
32
+
33
+ # Set a watch for /etc/passwd, and update our hash of UIDs => homedirs
34
+ # when it changes
35
+ notifier = INotify::Notifier.new
36
+ notifier.watch('/etc/passwd', :modify) do
37
+ # Send a signal to the child process; it will catch USR1 and update
38
+ # the homedirs hash
39
+ Process::kill('USR1', pid)
40
+ end
41
+ notifier.run
42
+ else
43
+ # Here we trap the signal to be sent to us by our parent
44
+ # when /etc/passwd changes and update our hash accordingly
45
+ trap 'USR1' do
46
+ fs.read_passwd
47
+ end
48
+ end
49
+
50
+ # We set allow_other, because this filesystem doesn't really make sense
51
+ # without it (why direct different users to different places if only one
52
+ # user can access the filesystem?).
53
+ FuseFS.main(ARGV + ["-o", "allow_other"]) { fs }
@@ -0,0 +1,172 @@
1
+ require 'rfusefs'
2
+
3
+ class HomeFS
4
+ def initialize(path)
5
+ @relpath = path
6
+ @dirlinks = Hash.new
7
+ read_passwd
8
+ end
9
+
10
+ def read_passwd
11
+ # Here we map each line in /etc/passwd to an array,
12
+ # which Hash interprets as a key, value pair.
13
+ @homedirs = Hash[
14
+ File.readlines('/etc/passwd').map do |line|
15
+ next if line.strip.empty?
16
+ values = line.split(':')
17
+ # UID, home directory
18
+ [Integer(values[2]), values[5]]
19
+ end
20
+ ]
21
+ end
22
+
23
+ def homepath(path = nil)
24
+ uid = FuseFS.reader_uid
25
+ basepath = @homedirs[uid]
26
+ # basepath shouldn't ever be nil, but fail gracefully just
27
+ # in case.
28
+ raise Errno::ENOENT if basepath == nil
29
+ if path
30
+ "#{basepath}/#{@relpath}/#{path}"
31
+ else
32
+ "#{basepath}/#{@relpath}"
33
+ end
34
+ end
35
+
36
+ def mode_mask(file, mask, check_ids = true)
37
+ stat = File.stat(file)
38
+ # First digit: setuid (4), setgid (2), sticky bit (1)
39
+ # Second, third, and fourth digits: user, group, and
40
+ # world permissions for read (4), write (2), execute (1)
41
+ # (see `man chmod` for more)
42
+ fmode = stat.mode
43
+ if check_ids
44
+ fuid, fgid = stat.uid, stat.gid
45
+ uid, gid = FuseFS.reader_uid, FuseFS.reader_gid
46
+ # Zero out the third digit (in octal).
47
+ # We could use a constant here, but this works
48
+ # for a mask of any length
49
+ if uid != fuid
50
+ mask &= ~(mask & 0700)
51
+ end
52
+ if gid != fgid
53
+ mask &= ~(mask & 0070)
54
+ end
55
+ end
56
+ fmode & mask != 0
57
+ end
58
+
59
+ def rename(from_path, to_path)
60
+ FileUtils.mv(homepath(from_path), homepath(to_path))
61
+ end
62
+
63
+ def touch(path, modtime)
64
+ File.utime(modtime, modtime, homepath(path))
65
+ end
66
+
67
+ def rmdir(path)
68
+ FileUtils.rmdir(homepath(path))
69
+ end
70
+
71
+ def can_rmdir?(path)
72
+ File.writable?(homepath(path))
73
+ end
74
+
75
+ def mkdir(path)
76
+ FileUtils.mkdir(homepath(path))
77
+ end
78
+
79
+ def can_mkdir?(path)
80
+ writable?(File.dirname(homepath(path)))
81
+ end
82
+
83
+ def delete(path)
84
+ FileUtils.rm(homepath(path))
85
+ end
86
+
87
+ def can_delete?(path)
88
+ writable?(File.dirname(homepath(path)))
89
+ end
90
+
91
+ def write_to(path, str)
92
+ File.open(homepath(path), "wb") {|file| file.write(str) }
93
+ end
94
+
95
+ def writable?(path)
96
+ mode_mask(path, 0222)
97
+ end
98
+
99
+ def can_write?(path)
100
+ hpath = homepath(path)
101
+ if File.exist?(hpath)
102
+ writable?(hpath)
103
+ else
104
+ # FIXME: We don't support sticky bits
105
+ writable?(File.dirname(hpath))
106
+ end
107
+ end
108
+
109
+ def times(path)
110
+ atime = File.atime(homepath(path))
111
+ mtime = File.mtime(homepath(path))
112
+ ctime = File.ctime(homepath(path))
113
+ [atime, mtime, ctime]
114
+ end
115
+
116
+ def executable?(path)
117
+ mode_mask(homepath(path), 0111)
118
+ end
119
+
120
+ def read_file(path)
121
+ File.open(homepath(path), "rb") do |file|
122
+ file.read
123
+ end
124
+ end
125
+
126
+ def contents(path)
127
+ Dir.new(homepath(path)).to_a
128
+ end
129
+
130
+ def size(path)
131
+ File.size(homepath(path))
132
+ end
133
+
134
+ def file?(path)
135
+ File.file?(homepath(path))
136
+ end
137
+
138
+ def directory?(path)
139
+ File.directory?(homepath(path))
140
+ end
141
+
142
+ def raw_open(path, mode, rfusefs = nil)
143
+ mode = case mode
144
+ when "rw" then File::RDWR | File::CREAT | File::BINARY
145
+ when "r" then File::RDONLY | File::BINARY
146
+ when "w" then File::WRONLY | File::CREAT | File::BINARY
147
+ end
148
+ File.open(homepath(path), mode)
149
+ end
150
+
151
+ def raw_read(path, offset, size, raw = nil)
152
+ raw ||= raw_open(path, "r")
153
+ raw.seek(offset, :SET)
154
+ raw.read(size)
155
+ end
156
+
157
+ def raw_sync(path, datasync, raw = nil)
158
+ return if raw.nil? # Should we sync anyway?
159
+ raw.fdatasync
160
+ end
161
+
162
+ def raw_close(path, raw = nil)
163
+ return if raw.nil? # ???
164
+ raw.close
165
+ end
166
+
167
+ def raw_write(path, off, sz, buf, raw = nil)
168
+ raw ||= File.open(path, "w")
169
+ raw.seek(off, :SET)
170
+ raw.write(buf[0...sz])
171
+ end
172
+ end
data/lib/homefs.rb ADDED
@@ -0,0 +1 @@
1
+ require 'homefs/homefs'
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: homefs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dylan Frese
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ FuseFS currently written in Ruby that directs filesystem
15
+ calls to a directory relative to the calling user's home
16
+ directory.
17
+ email: dmfrese@gmail.com
18
+ executables:
19
+ - homefs
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - bin/homefs
24
+ - lib/homefs.rb
25
+ - lib/homefs/homefs.rb
26
+ homepage:
27
+ licenses:
28
+ - GPLv3
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.2.2
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: FUSE Filesystem
50
+ test_files: []
51
+ has_rdoc: