nfs-rb 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +12 -0
- data/LICENSE +23 -0
- data/README.md +73 -0
- data/Rakefile +14 -0
- data/bin/nfs-rb +52 -0
- data/lib/nfs.rb +17 -0
- data/lib/nfs/default_logger.rb +13 -0
- data/lib/nfs/file_proxy.rb +117 -0
- data/lib/nfs/filehandle.rb +24 -0
- data/lib/nfs/handler.rb +570 -0
- data/lib/nfs/mount.rb +94 -0
- data/lib/nfs/nfs.rb +259 -0
- data/lib/nfs/server.rb +39 -0
- data/lib/nfs/sunrpc.rb +365 -0
- data/lib/nfs/sunrpc/client.rb +77 -0
- data/lib/nfs/sunrpc/procedure.rb +56 -0
- data/lib/nfs/sunrpc/program.rb +58 -0
- data/lib/nfs/sunrpc/server.rb +119 -0
- data/lib/nfs/sunrpc/tcp_server.rb +41 -0
- data/lib/nfs/sunrpc/udp_server.rb +42 -0
- data/lib/nfs/sunrpc/version.rb +55 -0
- data/lib/nfs/version.rb +3 -0
- data/lib/nfs/xdr.rb +300 -0
- data/nfs-rb.gemspec +17 -0
- data/spec/nfs_spec.rb +29 -0
- data/spec/orig_dir/file1.txt +1 -0
- data/spec/orig_dir/file2.txt +3 -0
- data/spec/run_server.rb +10 -0
- data/spec/spec_helper.rb +44 -0
- metadata +74 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module NFS
|
2
|
+
module SUNRPC
|
3
|
+
module Client
|
4
|
+
@@xid = 0
|
5
|
+
@@xid_mutex = Mutex.new
|
6
|
+
|
7
|
+
def method_missing(name, *args)
|
8
|
+
procedure = @version.get_procedure(name)
|
9
|
+
|
10
|
+
if procedure.nil?
|
11
|
+
raise NoMethodError, name.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
if args.size == 0
|
15
|
+
args = [nil]
|
16
|
+
end
|
17
|
+
|
18
|
+
if args.size != 1
|
19
|
+
raise ArgumentError
|
20
|
+
end
|
21
|
+
|
22
|
+
xid = nil
|
23
|
+
|
24
|
+
@@xid_mutex.synchronize do
|
25
|
+
xid = @@xid
|
26
|
+
@@xid += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
message = RpcMsg.encode({
|
30
|
+
xid: xid,
|
31
|
+
body: {
|
32
|
+
_discriminant: :CALL,
|
33
|
+
cbody: {
|
34
|
+
rpcvers: 2,
|
35
|
+
prog: @program.number,
|
36
|
+
vers: @version.number,
|
37
|
+
proc: procedure.number,
|
38
|
+
cred: {
|
39
|
+
flavor: :AUTH_NULL,
|
40
|
+
body: ''
|
41
|
+
},
|
42
|
+
verf: {
|
43
|
+
flavor: :AUTH_NULL,
|
44
|
+
body: ''
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}) + procedure.encode(args[0])
|
49
|
+
|
50
|
+
# This will return the result object or raise an exception that
|
51
|
+
# contains the cause of the error.
|
52
|
+
sendrecv(message) do |result|
|
53
|
+
envelope = RpcMsg.decode(result)
|
54
|
+
|
55
|
+
if envelope[:xid] == xid
|
56
|
+
if envelope[:body][:_discriminant] != :REPLY
|
57
|
+
raise envelope.inspect
|
58
|
+
end
|
59
|
+
|
60
|
+
if envelope[:body][:rbody][:_discriminant] != :MSG_ACCEPTED
|
61
|
+
raise envelope[:body][:rbody].inspect
|
62
|
+
end
|
63
|
+
|
64
|
+
if envelope[:body][:rbody][:areply][:reply_data][:_discriminant] != :SUCCESS
|
65
|
+
|
66
|
+
raise envelope[:body][:rbody][:areply][:reply_data].inspect
|
67
|
+
end
|
68
|
+
|
69
|
+
procedure.decode(result)
|
70
|
+
else
|
71
|
+
false # false means keep giving us received messages to inspect
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module NFS
|
2
|
+
module SUNRPC
|
3
|
+
class Procedure
|
4
|
+
attr_reader :number
|
5
|
+
|
6
|
+
def initialize(number, returntype, argtype, &block)
|
7
|
+
@number = number
|
8
|
+
@returntype, @argtype = returntype, argtype
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def dup
|
13
|
+
Procedure.new(@number, @returntype, @argtype, &@block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_call(&block)
|
17
|
+
@block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def encode(arg)
|
21
|
+
@argtype.encode(arg)
|
22
|
+
end
|
23
|
+
|
24
|
+
def decode(value)
|
25
|
+
@returntype.decode(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(arg, cred, verf)
|
29
|
+
begin
|
30
|
+
arg_object = @argtype.decode(arg)
|
31
|
+
rescue
|
32
|
+
raise GarbageArguments
|
33
|
+
end
|
34
|
+
|
35
|
+
# Undefined procedures are also unavailable, even if the XDR says it's
|
36
|
+
# there. Define your procedures and this won't happen.
|
37
|
+
if @block.nil?
|
38
|
+
raise ProcedureUnavailable
|
39
|
+
end
|
40
|
+
|
41
|
+
result_object = @block.call(arg_object, cred, verf)
|
42
|
+
result = nil
|
43
|
+
|
44
|
+
begin
|
45
|
+
result = @returntype.encode(result_object)
|
46
|
+
rescue => e
|
47
|
+
NFS.logger.error(e.message)
|
48
|
+
NFS.logger.error(e.backtrace.join("\n"))
|
49
|
+
raise IgnoreRequest
|
50
|
+
end
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module NFS
|
2
|
+
module SUNRPC
|
3
|
+
class Program
|
4
|
+
attr_reader :number, :low, :high
|
5
|
+
|
6
|
+
def initialize(number, &block)
|
7
|
+
@number = number
|
8
|
+
@versions = {}
|
9
|
+
@low = @high = nil
|
10
|
+
instance_eval(&block) if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
def dup
|
14
|
+
self.class.new(@number).tap do |p|
|
15
|
+
@versions.each_pair do |number, version|
|
16
|
+
p.add_version(number, version.dup)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_version(number, ver)
|
22
|
+
if @low.nil? or number < @low
|
23
|
+
@low = number
|
24
|
+
end
|
25
|
+
|
26
|
+
if @high.nil? or number > @high
|
27
|
+
@high = number
|
28
|
+
end
|
29
|
+
|
30
|
+
@versions[number] = ver
|
31
|
+
end
|
32
|
+
|
33
|
+
def version(ver, &block)
|
34
|
+
add_version(ver, Version.new(ver, &block))
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_version(ver)
|
38
|
+
@versions[ver]
|
39
|
+
end
|
40
|
+
|
41
|
+
def each_version(&block)
|
42
|
+
@versions.each_value(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_call(ver, procedure_name, &block)
|
46
|
+
@versions[ver].on_call(procedure_name, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(ver, procedure, arg, cred, verf)
|
50
|
+
if !@versions.include?(ver)
|
51
|
+
raise ProgramMismatch.new(2, 2)
|
52
|
+
end
|
53
|
+
|
54
|
+
@versions[ver].call(procedure, arg, cred, verf)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module NFS
|
2
|
+
module SUNRPC
|
3
|
+
class Server
|
4
|
+
def host
|
5
|
+
@server.addr[2]
|
6
|
+
end
|
7
|
+
|
8
|
+
def port
|
9
|
+
@server.addr[1]
|
10
|
+
end
|
11
|
+
|
12
|
+
def join
|
13
|
+
if !@thread.nil?
|
14
|
+
@thread.join
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
if @thread.nil?
|
20
|
+
@thread.start
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def shutdown
|
25
|
+
Thread.kill(@thread)
|
26
|
+
@thread = nil
|
27
|
+
@server.shutdown
|
28
|
+
rescue Errno::ENOTCONN
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def run_procedure(data)
|
34
|
+
begin
|
35
|
+
xid, program_num, version_num, procedure_num, cred,
|
36
|
+
verf = decode_envelope(data)
|
37
|
+
|
38
|
+
program = @programs[program_num]
|
39
|
+
|
40
|
+
if program.nil?
|
41
|
+
raise ProgramUnavailable
|
42
|
+
else
|
43
|
+
result = program.call(
|
44
|
+
version_num, procedure_num, data, cred, verf
|
45
|
+
)
|
46
|
+
|
47
|
+
create_success_envelope(xid, result)
|
48
|
+
end
|
49
|
+
rescue IgnoreRequest, RequestDenied, AcceptedError => e
|
50
|
+
return e.encode(xid)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decode_envelope(data)
|
55
|
+
envelope = nil
|
56
|
+
|
57
|
+
begin
|
58
|
+
envelope = RpcMsg.decode(data)
|
59
|
+
rescue
|
60
|
+
raise IgnoreRequest
|
61
|
+
end
|
62
|
+
|
63
|
+
if envelope[:body][:_discriminant] != :CALL
|
64
|
+
raise IgnoreRequest
|
65
|
+
end
|
66
|
+
|
67
|
+
if envelope[:body][:cbody][:rpcvers] != 2
|
68
|
+
raise RpcMismatch.new(2, 2, envelope[:xid])
|
69
|
+
end
|
70
|
+
|
71
|
+
cbody = envelope[:body][:cbody]
|
72
|
+
|
73
|
+
[
|
74
|
+
envelope[:xid],
|
75
|
+
cbody[:prog],
|
76
|
+
cbody[:vers],
|
77
|
+
cbody[:proc],
|
78
|
+
cbody[:cred],
|
79
|
+
cbody[:verf]
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_success_envelope(xid, result)
|
84
|
+
RpcMsg.encode({
|
85
|
+
xid: xid,
|
86
|
+
body: {
|
87
|
+
_discriminant: :REPLY,
|
88
|
+
rbody: {
|
89
|
+
_discriminant: :MSG_ACCEPTED,
|
90
|
+
areply: {
|
91
|
+
verf: {
|
92
|
+
flavor: :AUTH_NULL,
|
93
|
+
body: ''
|
94
|
+
},
|
95
|
+
reply_data: {
|
96
|
+
_discriminant: :SUCCESS,
|
97
|
+
results: ''
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}) + result
|
103
|
+
end
|
104
|
+
|
105
|
+
def hash_programs(programs)
|
106
|
+
case programs
|
107
|
+
when Hash
|
108
|
+
programs
|
109
|
+
when Array
|
110
|
+
programs.each_with_object({}) do |program, result|
|
111
|
+
result[program.number] = program
|
112
|
+
end
|
113
|
+
else
|
114
|
+
{ programs.number => programs }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module NFS
|
5
|
+
module SUNRPC
|
6
|
+
class TCPServer < Server
|
7
|
+
def initialize(programs, port = nil, host = '127.0.0.1')
|
8
|
+
@server = ::TCPServer.new(host, port)
|
9
|
+
@programs = hash_programs(programs)
|
10
|
+
|
11
|
+
@thread = Thread.new do
|
12
|
+
loop do
|
13
|
+
Thread.new(@server.accept) do |socket|
|
14
|
+
loop do
|
15
|
+
frame = socket.recv(4).unpack1('N')
|
16
|
+
len = frame & ~0x80000000
|
17
|
+
break unless len
|
18
|
+
|
19
|
+
data = socket.recv(len)
|
20
|
+
result = run_procedure(data)
|
21
|
+
|
22
|
+
if !result.nil?
|
23
|
+
result = [result.length | (128 << 24)].pack('N') + result
|
24
|
+
socket.send(result, 0)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if block_given?
|
32
|
+
begin
|
33
|
+
yield(self)
|
34
|
+
ensure
|
35
|
+
shutdown
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module NFS
|
5
|
+
module SUNRPC
|
6
|
+
class UDPServer < Server
|
7
|
+
def initialize(programs, port = nil, host = '127.0.0.1')
|
8
|
+
@socket = UDPSocket.open
|
9
|
+
@socket.bind(host, port)
|
10
|
+
socketmutex = Mutex.new
|
11
|
+
@programs = hash_programs(programs)
|
12
|
+
|
13
|
+
@thread = Thread.new do
|
14
|
+
loop do
|
15
|
+
request = @socket.recvfrom(UDPClient::UDPRecvMTU)
|
16
|
+
data = request[0]
|
17
|
+
port = request[1][1]
|
18
|
+
host = request[1][3]
|
19
|
+
|
20
|
+
Thread.new do
|
21
|
+
result = run_procedure(data)
|
22
|
+
|
23
|
+
if !result.nil?
|
24
|
+
socketmutex.synchronize do
|
25
|
+
@socket.send(result, 0, host, port)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if block_given?
|
33
|
+
begin
|
34
|
+
yield(self)
|
35
|
+
ensure
|
36
|
+
shutdown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module NFS
|
2
|
+
module SUNRPC
|
3
|
+
class Version
|
4
|
+
attr_reader :number
|
5
|
+
|
6
|
+
def initialize(number, &block)
|
7
|
+
@number = number
|
8
|
+
@procedures = {}
|
9
|
+
@procedure_names = {}
|
10
|
+
|
11
|
+
# Add the customary null procedure by default.
|
12
|
+
procedure XDR::Void.new, :NULL, 0, XDR::Void.new do
|
13
|
+
# do nothing
|
14
|
+
end
|
15
|
+
|
16
|
+
instance_eval(&block) if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
def dup
|
20
|
+
Version.new(@number).tap do |v|
|
21
|
+
@procedure_names.each_pair do |name, procedure|
|
22
|
+
v.add_procedure(name, procedure.number, procedure.dup)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_procedure(name, number, newproc)
|
28
|
+
@procedures[number] = newproc
|
29
|
+
@procedure_names[name] = newproc
|
30
|
+
end
|
31
|
+
|
32
|
+
# The name is required, but just for documentation.
|
33
|
+
def procedure(returntype, name, number, argtype, &block)
|
34
|
+
newproc = Procedure.new(number, returntype, argtype, &block)
|
35
|
+
add_procedure(name, number, newproc)
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_procedure(procedure_name)
|
39
|
+
@procedure_names[procedure_name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_call(procedure_name, &block)
|
43
|
+
@procedure_names[procedure_name].on_call(&block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def call(p, arg, cred, verf)
|
47
|
+
unless @procedures.include?(p)
|
48
|
+
raise ProcedureUnavailable
|
49
|
+
end
|
50
|
+
|
51
|
+
@procedures[p].call(arg, cred, verf)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|