ftpd 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +31 -3
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.md +0 -8
- data/VERSION +1 -1
- data/doc/references.md +7 -0
- data/examples/example.rb +7 -0
- data/features/example/eplf.feature +14 -0
- data/features/example/step_definitions/example_server.rb +9 -1
- data/features/ftp_server/list.feature +8 -1
- data/features/ftp_server/name_list.feature +1 -1
- data/features/step_definitions/list.rb +10 -0
- data/features/support/example_server.rb +3 -2
- data/features/support/test_server.rb +0 -8
- data/ftpd.gemspec +12 -2
- data/lib/ftpd.rb +5 -0
- data/lib/ftpd/command_sequence_checker.rb +3 -2
- data/lib/ftpd/disk_file_system.rb +62 -74
- data/lib/ftpd/file_info.rb +115 -0
- data/lib/ftpd/ftp_server.rb +8 -0
- data/lib/ftpd/list_format/eplf.rb +74 -0
- data/lib/ftpd/list_format/ls.rb +154 -0
- data/lib/ftpd/session.rb +31 -7
- data/spec/disk_file_system_spec.rb +69 -70
- data/spec/file_info_spec.rb +59 -0
- data/spec/list_format/eplf_spec.rb +62 -0
- data/spec/list_format/ls_spec.rb +270 -0
- data/spec/spec_helper.rb +3 -0
- metadata +26 -3
data/Changelog.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
### 0.3.1
|
2
|
+
|
3
|
+
API changes
|
4
|
+
|
5
|
+
The file system interface for directory listing was completely
|
6
|
+
rewritten. It no longer shells out to ls, which removes potential
|
7
|
+
command injection security holes, and improves prospects for
|
8
|
+
portability.
|
9
|
+
|
10
|
+
* Removed {Ftpd::DiskFileSystem::Ls}
|
11
|
+
* Removed {Ftpd::DiskFileSystem::NameList}. NLIST now uses the
|
12
|
+
functions in {Ftpd::DiskFileSystem::List}.
|
13
|
+
* Removed {Ftpd::DiskFileSystem::List#list}. The formatting of
|
14
|
+
directory output is now done by ftpd, not by the file system driver.
|
15
|
+
* Added {Ftpd::DiskFileSystem::List#file_info}, used by LIST.
|
16
|
+
* Added {Ftpd::DiskFileSystem::List#dir}, used by LIST and NLST.
|
17
|
+
|
18
|
+
Bug fixes
|
19
|
+
|
20
|
+
* LIST and NLST support globs again.
|
21
|
+
* STOU (store unique) works in Ruby 1.8.7
|
22
|
+
|
23
|
+
Enhancements
|
24
|
+
|
25
|
+
* The output of the "LIST" command can be customized (see
|
26
|
+
{Ftpd::FtpServer#list_formatter})
|
27
|
+
|
1
28
|
### 0.2.2
|
2
29
|
|
3
30
|
Bug fixes
|
@@ -8,9 +35,10 @@ Bug fixes
|
|
8
35
|
PASS
|
9
36
|
* Open PASV mode data connection on same local IP as control connection.
|
10
37
|
This is required by RFC 1123.
|
11
|
-
* Disabled globbing in LIST (for now) due to
|
12
|
-
vulnerability. This patch also disables globbing in NLST,
|
13
|
-
probably shouldn't do globbing.
|
38
|
+
* Disabled globbing in LIST (for now) due to a command (shell)
|
39
|
+
injection vulnerability. This patch also disables globbing in NLST,
|
40
|
+
but NLST probably shouldn't do globbing. Thanks to Larry Cashdollar
|
41
|
+
for the report.
|
14
42
|
|
15
43
|
Enhancements
|
16
44
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -92,10 +92,6 @@ Ftpd is not yet RFC compliant. It does most of RFC969, and enough TLS
|
|
92
92
|
to get by. {file:doc/rfc.md Here} is a list of RFCs, indicating how
|
93
93
|
much of each Ftpd complies with.
|
94
94
|
|
95
|
-
The DiskFileSystem class only works in Linux. This is because it
|
96
|
-
shells out to the "ls" command. This affects the example, which uses
|
97
|
-
the DiskFileSystem.
|
98
|
-
|
99
95
|
To bind the server to an external interface, the interface must be set
|
100
96
|
to the public IP of that interface (e.g. "1.2.3.4"), not to "0.0.0.0".
|
101
97
|
That's because the interface IP is used both for binding server ports,
|
@@ -103,10 +99,6 @@ _and_ for advertising to the client which IP to connect to. Binding
|
|
103
99
|
to 0.0.0.0 will work fine, but when the client tries to connect to
|
104
100
|
0.0.0.0, it won't get to the server.
|
105
101
|
|
106
|
-
LIST doesn't accept globs. It has other problems (it accepts
|
107
|
-
arbitrary ls arguments!) and needs to be rewritten to not shell out to
|
108
|
-
"ls".
|
109
|
-
|
110
102
|
## RUBY COMPATABILITY
|
111
103
|
|
112
104
|
The tests pass with these Rubies:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
data/doc/references.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# REFERENCES
|
2
2
|
|
3
|
+
## RFCs
|
4
|
+
|
3
5
|
_This list of references comes from the README of the em-ftpd gem,
|
4
6
|
which is licensed under the same MIT license as this gem, and is
|
5
7
|
Copyright (c) 2008 James Healy_
|
@@ -30,3 +32,8 @@ including the ye old RFC114 from 1971, "A File Transfer Protocol"
|
|
30
32
|
There is a {http://secureftp-test.com public test server} which is
|
31
33
|
very handy for checking out clients, and seeing how at least one
|
32
34
|
server behaves.
|
35
|
+
|
36
|
+
## LIST output format
|
37
|
+
|
38
|
+
* {http://www.gnu.org/software/coreutils/manual/html_node/What-information-is-listed.html#What-information-is-listed GNU docs for ls}
|
39
|
+
* {http://cr.yp.to/ftp/list/eplf.html Easily Parsed LIST format (EPLF)}
|
data/examples/example.rb
CHANGED
@@ -10,6 +10,7 @@ require 'optparse'
|
|
10
10
|
module Example
|
11
11
|
class Arguments
|
12
12
|
|
13
|
+
attr_reader :eplf
|
13
14
|
attr_reader :interface
|
14
15
|
attr_reader :port
|
15
16
|
attr_reader :tls
|
@@ -39,6 +40,9 @@ module Example
|
|
39
40
|
'Select TLS support (off, explicit, implicit)') do |t|
|
40
41
|
@tls = t
|
41
42
|
end
|
43
|
+
op.on('--eplf', 'LIST uses EPLF format') do |t|
|
44
|
+
@eplf = t
|
45
|
+
end
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
@@ -94,6 +98,9 @@ module Example
|
|
94
98
|
@server.port = @args.port
|
95
99
|
@server.tls = @args.tls
|
96
100
|
@server.certfile_path = insecure_certfile_path
|
101
|
+
if @args.eplf
|
102
|
+
@server.list_formatter = Ftpd::ListFormat::Eplf
|
103
|
+
end
|
97
104
|
@server.start
|
98
105
|
display_connection_info
|
99
106
|
create_connection_script
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Example
|
2
|
+
|
3
|
+
As a programmer
|
4
|
+
I want to enable EPLF list format
|
5
|
+
So that I can test this library with an EPLF client
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given the example has argument "--eplf"
|
9
|
+
And the example server is started
|
10
|
+
|
11
|
+
Scenario: List directory
|
12
|
+
Given a successful login
|
13
|
+
When the client successfully lists the directory
|
14
|
+
Then the list should be in EPLF format
|
@@ -1,3 +1,11 @@
|
|
1
|
+
def example_args
|
2
|
+
@example_args ||= []
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^the example has argument "(.*?)"$/ do |arg|
|
6
|
+
example_args << arg
|
7
|
+
end
|
8
|
+
|
1
9
|
Given /^the example server is started$/ do
|
2
|
-
@server = ExampleServer.new
|
10
|
+
@server = ExampleServer.new(example_args)
|
3
11
|
end
|
@@ -41,8 +41,15 @@ Feature: List
|
|
41
41
|
Then the file list should be in long form
|
42
42
|
And the file list should contain "foo"
|
43
43
|
|
44
|
+
Scenario: After CWD
|
45
|
+
Given a successful login
|
46
|
+
And the server has file "subdir/foo"
|
47
|
+
And the client successfully cd's to "subdir"
|
48
|
+
When the client successfully lists the directory
|
49
|
+
Then the file list should be in long form
|
50
|
+
And the file list should contain "foo"
|
51
|
+
|
44
52
|
Scenario: Glob
|
45
|
-
Given PENDING "Disabled (for now) due to code injection vulnerability"
|
46
53
|
Given a successful login
|
47
54
|
And the server has file "foo"
|
48
55
|
And the server has file "bar"
|
@@ -62,7 +62,7 @@ Feature: Name List
|
|
62
62
|
Then the server returns a not logged in error
|
63
63
|
|
64
64
|
Scenario: List not enabled
|
65
|
-
Given the test server is started without
|
65
|
+
Given the test server is started without list
|
66
66
|
And a successful login
|
67
67
|
When the client name-lists the directory
|
68
68
|
Then the server returns an unimplemented command error
|
@@ -18,6 +18,12 @@ class FileList
|
|
18
18
|
!long_form?
|
19
19
|
end
|
20
20
|
|
21
|
+
def eplf_format?
|
22
|
+
@lines.all? do |line|
|
23
|
+
line =~ /^\+.*\t.*$/
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
21
27
|
def empty?
|
22
28
|
@lines.empty?
|
23
29
|
end
|
@@ -61,3 +67,7 @@ end
|
|
61
67
|
Then /^the file list should be empty$/ do
|
62
68
|
@list.should be_empty
|
63
69
|
end
|
70
|
+
|
71
|
+
Then /^the list should be in EPLF format$/ do
|
72
|
+
@list.should be_eplf_format
|
73
|
+
end
|
@@ -9,10 +9,11 @@ class ExampleServer
|
|
9
9
|
include FileUtils
|
10
10
|
include TestServerFiles
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(args = nil)
|
13
13
|
command = [
|
14
14
|
File.expand_path('../../examples/example.rb',
|
15
|
-
File.dirname(__FILE__))
|
15
|
+
File.dirname(__FILE__)),
|
16
|
+
args,
|
16
17
|
].join(' ')
|
17
18
|
@io = IO.popen(command, 'r+')
|
18
19
|
@output = read_output
|
@@ -16,7 +16,6 @@ class TestServer
|
|
16
16
|
attr_accessor :delete
|
17
17
|
attr_accessor :list
|
18
18
|
attr_accessor :mkdir
|
19
|
-
attr_accessor :name_list
|
20
19
|
attr_accessor :read
|
21
20
|
attr_accessor :rename
|
22
21
|
attr_accessor :rmdir
|
@@ -27,7 +26,6 @@ class TestServer
|
|
27
26
|
@delete = true
|
28
27
|
@list = true
|
29
28
|
@mkdir = true
|
30
|
-
@name_list = true
|
31
29
|
@read = true
|
32
30
|
@rename = true
|
33
31
|
@rmdir = true
|
@@ -43,7 +41,6 @@ class TestServer
|
|
43
41
|
:delete => @delete,
|
44
42
|
:list => @list,
|
45
43
|
:mkdir => @mkdir,
|
46
|
-
:name_list => @name_list,
|
47
44
|
:read => @read,
|
48
45
|
:rename => @rename,
|
49
46
|
:rmdir => @rmdir,
|
@@ -147,10 +144,6 @@ class TestServer
|
|
147
144
|
include Ftpd::DiskFileSystem::List
|
148
145
|
end
|
149
146
|
|
150
|
-
if opts[:name_list]
|
151
|
-
include Ftpd::DiskFileSystem::NameList
|
152
|
-
end
|
153
|
-
|
154
147
|
if opts[:mkdir]
|
155
148
|
include Ftpd::DiskFileSystem::Mkdir
|
156
149
|
end
|
@@ -218,7 +211,6 @@ class TestServer
|
|
218
211
|
def_delegator :@driver, :'delete='
|
219
212
|
def_delegator :@driver, :'list='
|
220
213
|
def_delegator :@driver, :'mkdir='
|
221
|
-
def_delegator :@driver, :'name_list='
|
222
214
|
def_delegator :@driver, :'rmdir='
|
223
215
|
def_delegator :@driver, :'read='
|
224
216
|
def_delegator :@driver, :'rename='
|
data/ftpd.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "ftpd"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wayne Conrad"]
|
12
|
-
s.date = "2013-03-
|
12
|
+
s.date = "2013-03-04"
|
13
13
|
s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, passive and active mode, and most of the commands specified in RFC 969. It an be used as part of a test fixture or embedded in a program."
|
14
14
|
s.email = "wconrad@yagni.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
"doc/rfc-compliance.md",
|
29
29
|
"examples/example.rb",
|
30
30
|
"examples/hello_world.rb",
|
31
|
+
"features/example/eplf.feature",
|
31
32
|
"features/example/example.feature",
|
32
33
|
"features/example/step_definitions/example_server.rb",
|
33
34
|
"features/ftp_server/allo.feature",
|
@@ -110,9 +111,12 @@ Gem::Specification.new do |s|
|
|
110
111
|
"lib/ftpd/error.rb",
|
111
112
|
"lib/ftpd/exception_translator.rb",
|
112
113
|
"lib/ftpd/exceptions.rb",
|
114
|
+
"lib/ftpd/file_info.rb",
|
113
115
|
"lib/ftpd/file_system_error_translator.rb",
|
114
116
|
"lib/ftpd/ftp_server.rb",
|
115
117
|
"lib/ftpd/insecure_certificate.rb",
|
118
|
+
"lib/ftpd/list_format/eplf.rb",
|
119
|
+
"lib/ftpd/list_format/ls.rb",
|
116
120
|
"lib/ftpd/server.rb",
|
117
121
|
"lib/ftpd/session.rb",
|
118
122
|
"lib/ftpd/temp_dir.rb",
|
@@ -127,7 +131,10 @@ Gem::Specification.new do |s|
|
|
127
131
|
"spec/command_sequence_checker_spec.rb",
|
128
132
|
"spec/disk_file_system_spec.rb",
|
129
133
|
"spec/exception_translator_spec.rb",
|
134
|
+
"spec/file_info_spec.rb",
|
130
135
|
"spec/file_system_error_translator_spec.rb",
|
136
|
+
"spec/list_format/eplf_spec.rb",
|
137
|
+
"spec/list_format/ls_spec.rb",
|
131
138
|
"spec/spec_helper.rb",
|
132
139
|
"spec/translate_exceptions_spec.rb"
|
133
140
|
]
|
@@ -148,6 +155,7 @@ Gem::Specification.new do |s|
|
|
148
155
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
149
156
|
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
150
157
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
158
|
+
s.add_development_dependency(%q<timecop>, [">= 0"])
|
151
159
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
152
160
|
else
|
153
161
|
s.add_dependency(%q<memoizer>, ["~> 1.0.1"])
|
@@ -157,6 +165,7 @@ Gem::Specification.new do |s|
|
|
157
165
|
s.add_dependency(%q<rake>, [">= 0"])
|
158
166
|
s.add_dependency(%q<redcarpet>, [">= 0"])
|
159
167
|
s.add_dependency(%q<rspec>, [">= 0"])
|
168
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
160
169
|
s.add_dependency(%q<yard>, [">= 0"])
|
161
170
|
end
|
162
171
|
else
|
@@ -167,6 +176,7 @@ Gem::Specification.new do |s|
|
|
167
176
|
s.add_dependency(%q<rake>, [">= 0"])
|
168
177
|
s.add_dependency(%q<redcarpet>, [">= 0"])
|
169
178
|
s.add_dependency(%q<rspec>, [">= 0"])
|
179
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
170
180
|
s.add_dependency(%q<yard>, [">= 0"])
|
171
181
|
end
|
172
182
|
end
|
data/lib/ftpd.rb
CHANGED
@@ -7,10 +7,15 @@ require 'socket'
|
|
7
7
|
require 'tmpdir'
|
8
8
|
|
9
9
|
module Ftpd
|
10
|
+
module ListFormat
|
11
|
+
autoload :Eplf, 'ftpd/list_format/eplf'
|
12
|
+
autoload :Ls, 'ftpd/list_format/ls'
|
13
|
+
end
|
10
14
|
autoload :CommandSequenceChecker, 'ftpd/command_sequence_checker'
|
11
15
|
autoload :DiskFileSystem, 'ftpd/disk_file_system'
|
12
16
|
autoload :Error, 'ftpd/error'
|
13
17
|
autoload :ExceptionTranslator, 'ftpd/exception_translator'
|
18
|
+
autoload :FileInfo, 'ftpd/file_info'
|
14
19
|
autoload :FileSystemErrorTranslator, 'ftpd/file_system_error_translator'
|
15
20
|
autoload :FileSystemMethodMissing, 'ftpd/file_system_method_missing'
|
16
21
|
autoload :FtpServer, 'ftpd/ftp_server'
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# Some commands are supposed to occur in sequence. For example, USER
|
2
2
|
# must be immediately followed by PASS. This class keeps track of
|
3
|
-
# when a specific command
|
4
|
-
# error when
|
3
|
+
# when a specific command either must arrive or must not arrive, and
|
4
|
+
# raises a "bad sequence" error when commands arrive in the wrong
|
5
|
+
# sequence.
|
5
6
|
|
6
7
|
module Ftpd
|
7
8
|
class CommandSequenceChecker
|
@@ -200,38 +200,6 @@ module Ftpd
|
|
200
200
|
|
201
201
|
end
|
202
202
|
|
203
|
-
class DiskFileSystem
|
204
|
-
|
205
|
-
# Ls interface used by List and NameList
|
206
|
-
|
207
|
-
module Ls
|
208
|
-
|
209
|
-
include Shellwords
|
210
|
-
|
211
|
-
def ls(ftp_path, option)
|
212
|
-
path = expand_ftp_path(ftp_path)
|
213
|
-
dirname = File.dirname(path)
|
214
|
-
filename = File.basename(path)
|
215
|
-
command = [
|
216
|
-
'ls',
|
217
|
-
option,
|
218
|
-
filename,
|
219
|
-
].compact
|
220
|
-
if File.exists?(dirname)
|
221
|
-
list = Dir.chdir(dirname) do
|
222
|
-
`#{shelljoin(command)} 2>&1`
|
223
|
-
end
|
224
|
-
else
|
225
|
-
list = ''
|
226
|
-
end
|
227
|
-
list = "" if $? != 0
|
228
|
-
list = list.gsub(/^total \d+\n/, '')
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
|
233
|
-
end
|
234
|
-
|
235
203
|
class DiskFileSystem
|
236
204
|
|
237
205
|
# DiskFileSystem mixin providing directory listing
|
@@ -240,65 +208,86 @@ module Ftpd
|
|
240
208
|
|
241
209
|
include TranslateExceptions
|
242
210
|
|
243
|
-
# Get a file
|
244
|
-
# directory listing. The FTP standard does not specify the
|
245
|
-
# format of the listing, but many systems emit a *nix style
|
246
|
-
# directory listing:
|
211
|
+
# Get information about a single file or directory.
|
247
212
|
#
|
248
|
-
#
|
249
|
-
#
|
213
|
+
# Should follow symlinks (per
|
214
|
+
# {http://cr.yp.to/ftp/list/eplf.html}, "lstat() is not a good
|
215
|
+
# idea for FTP directory listings").
|
216
|
+
#
|
217
|
+
# @return [FileInfo]
|
218
|
+
#
|
219
|
+
# Called for:
|
220
|
+
# * LIST
|
221
|
+
#
|
222
|
+
# If missing, then these commands are not supported.
|
223
|
+
|
224
|
+
def file_info(path)
|
225
|
+
stat = File.stat(expand_ftp_path(path))
|
226
|
+
FileInfo.new(:ftype => stat.ftype,
|
227
|
+
:group => gid_name(stat.gid),
|
228
|
+
:identifier => identifier(stat),
|
229
|
+
:mode => stat.mode,
|
230
|
+
:mtime => stat.mtime,
|
231
|
+
:nlink => stat.nlink,
|
232
|
+
:owner => uid_name(stat.uid),
|
233
|
+
:path => path,
|
234
|
+
:size => stat.size)
|
235
|
+
end
|
236
|
+
translate_exceptions :file_info
|
237
|
+
|
238
|
+
# Expand a path that may contain globs into a list of paths of
|
239
|
+
# matching files and directories.
|
250
240
|
#
|
251
|
-
#
|
252
|
-
# Parsed List Format):
|
241
|
+
# * If the path matches no files, returns an empty list.
|
253
242
|
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
# +i8388621.48598,m824253270,r,s612, 514.html
|
243
|
+
# * If the path has no glob and matches a directory, then the
|
244
|
+
# list contains only that directory.
|
257
245
|
#
|
258
|
-
#
|
246
|
+
# * If the patch has a glob, it may return multiple entries.
|
259
247
|
#
|
260
|
-
#
|
248
|
+
# The paths returned are fully qualified, relative to the root
|
249
|
+
# of the virtual file system.
|
250
|
+
#
|
251
|
+
# For example, suppose these files exist on the physical file
|
252
|
+
# system:
|
261
253
|
#
|
262
|
-
#
|
263
|
-
#
|
264
|
-
#
|
265
|
-
# EPLF are that it's easier for clients to parse, and the client
|
266
|
-
# can display the LIST output in any format it likes.
|
254
|
+
# /var/lib/ftp/foo/foo
|
255
|
+
# /var/lib/ftp/foo/subdir/bar
|
256
|
+
# /var/lib/ftp/foo/subdir/baz
|
267
257
|
#
|
268
|
-
#
|
269
|
-
#
|
258
|
+
# and that the directory /var/lib/ftp is the root of the virtual
|
259
|
+
# file system. Then:
|
260
|
+
#
|
261
|
+
# dir('foo') # => ['/foo']
|
262
|
+
# dir('subdir') # => ['/subdir']
|
263
|
+
# dir('subdir/*') # => ['/subdir/bar', '/subdir/baz']
|
264
|
+
# dir('*') # => ['/foo', '/subdir']
|
270
265
|
#
|
271
266
|
# Called for:
|
272
267
|
# * LIST
|
268
|
+
# * NLST
|
273
269
|
#
|
274
270
|
# If missing, then these commands are not supported.
|
275
271
|
|
276
|
-
def
|
277
|
-
|
272
|
+
def dir(path)
|
273
|
+
Dir[expand_ftp_path(path)].map do |path|
|
274
|
+
path.sub(/^#{@data_dir}/, '')
|
275
|
+
end
|
278
276
|
end
|
277
|
+
translate_exceptions :dir
|
279
278
|
|
280
|
-
|
281
|
-
end
|
282
|
-
|
283
|
-
class DiskFileSystem
|
284
|
-
|
285
|
-
# DiskFileSystem mixin providing directory name listing
|
286
|
-
|
287
|
-
module NameList
|
279
|
+
private
|
288
280
|
|
289
|
-
|
281
|
+
def uid_name(uid)
|
282
|
+
Etc.getpwuid(uid).name
|
283
|
+
end
|
290
284
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
#
|
295
|
-
# Called for:
|
296
|
-
# * NLST
|
297
|
-
#
|
298
|
-
# If missing, then these commands are not supported.
|
285
|
+
def gid_name(gid)
|
286
|
+
Etc.getgrgid(gid).name
|
287
|
+
end
|
299
288
|
|
300
|
-
def
|
301
|
-
|
289
|
+
def identifier(stat)
|
290
|
+
[stat.dev, stat.ino].join('.')
|
302
291
|
end
|
303
292
|
|
304
293
|
end
|
@@ -367,7 +356,6 @@ module Ftpd
|
|
367
356
|
include DiskFileSystem::Delete
|
368
357
|
include DiskFileSystem::List
|
369
358
|
include DiskFileSystem::Mkdir
|
370
|
-
include DiskFileSystem::NameList
|
371
359
|
include DiskFileSystem::Read
|
372
360
|
include DiskFileSystem::Rename
|
373
361
|
include DiskFileSystem::Rmdir
|