fstab 0.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.
- data/.document +5 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.md +71 -0
- data/Rakefile +43 -0
- data/lib/fstab.rb +288 -0
- data/spec/fstab_helper_spec.rb +309 -0
- data/spec/spec_helper.rb +11 -0
- metadata +106 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", ">= 0"
|
10
|
+
gem "jeweler", "~> 1.8.4"
|
11
|
+
gem "rdoc"
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 BVox World S.L.U.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# fstab
|
2
|
+
|
3
|
+
Linux fstab helper library
|
4
|
+
|
5
|
+
# Usage
|
6
|
+
|
7
|
+
Adding a new entry to fstab:
|
8
|
+
|
9
|
+
require 'fstab'
|
10
|
+
|
11
|
+
# Use default /etc/fstab
|
12
|
+
# With safe_mode only valid devices/filesystems will be added to fstab
|
13
|
+
# Trying to add non existant device or filesystem will raise an exception
|
14
|
+
#
|
15
|
+
# :backup => true will create a timestamped fstab backup before saving
|
16
|
+
# the changes
|
17
|
+
fstab = Fstab.new '/etc/fstab', :safe_mode => true,
|
18
|
+
:backup => true, :backup_dir = '/etc/'
|
19
|
+
|
20
|
+
# Asuming /dev/sda1 has a valid FS
|
21
|
+
# The library will use the FS UUID automatically by default even if the block device path
|
22
|
+
# was used as an argument. This is usually safer.
|
23
|
+
fstab.add_device '/dev/sda1', '/mnt', 'ext4', 'discard,errors=remount-ro', 0, 1
|
24
|
+
|
25
|
+
# You can use filesystem UUID also
|
26
|
+
fstab.add_device '15baeabd-b419-4a69-a306-bc550dc8355f', '/mnt', 'ext4', 'discard,errors=remount-ro', 0, 1
|
27
|
+
|
28
|
+
# List fstab entries
|
29
|
+
fstab.entries.each do |key, val|
|
30
|
+
puts val[:label] # FS label, may be nil
|
31
|
+
puts val[:uuid] # FS UUID, nil if entry marked as invalid
|
32
|
+
puts val[:mount_point]
|
33
|
+
puts val[:type]
|
34
|
+
puts val[:opts]
|
35
|
+
puts val[:dump]
|
36
|
+
puts val[:pass]
|
37
|
+
puts val[:special] # special FS, i.e. not a block device
|
38
|
+
puts val[:line_number] # line number for the FS in fstab
|
39
|
+
puts val[:invalid] # the parser marked the entry as invalid
|
40
|
+
end
|
41
|
+
|
42
|
+
Checking if a device is present in /etc/fstab:
|
43
|
+
|
44
|
+
fstab.has_device? '/dev/sda1' # => true
|
45
|
+
# Assuming /dev/sda1 UUID is 15baeabd-b419-4a69-a306-bc550dc8355f
|
46
|
+
fstab.has_device? '15baeabd-b419-4a69-a306-bc550dc8355f' # => true
|
47
|
+
|
48
|
+
Some other helper methods:
|
49
|
+
|
50
|
+
# Return a hash of 'invalid' entries, i.e. device does not exist or the
|
51
|
+
# entry is malformed
|
52
|
+
fstab.invalid_entries
|
53
|
+
# Automatically remove invalid entries creating a backup file
|
54
|
+
fstab.remove_invalid_entries
|
55
|
+
|
56
|
+
# Get FS UUID
|
57
|
+
Fstab.get_uuid '/dev/sda1'
|
58
|
+
|
59
|
+
# Get FS label, assuming it has one
|
60
|
+
# return nil otherwise
|
61
|
+
Fstab.get_label '/dev/sda1'
|
62
|
+
|
63
|
+
# Running the tests
|
64
|
+
|
65
|
+
rake spec
|
66
|
+
|
67
|
+
# Copyright
|
68
|
+
|
69
|
+
Copyright (c) 2012 BVox World S.L.U. See LICENSE.txt for
|
70
|
+
further details.
|
71
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require './lib/fstab.rb'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Bundler.setup(:default, :development)
|
9
|
+
rescue Bundler::BundlerError => e
|
10
|
+
$stderr.puts e.message
|
11
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
+
exit e.status_code
|
13
|
+
end
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.version = Fstab::VERSION
|
20
|
+
gem.name = "fstab"
|
21
|
+
gem.homepage = "http://github.com/bvox/fstab"
|
22
|
+
gem.license = "MIT"
|
23
|
+
gem.summary = %Q{Linux fstab helper library}
|
24
|
+
gem.description = %Q{Linux fstab helper library}
|
25
|
+
gem.email = "rubiojr@bvox.net"
|
26
|
+
gem.authors = ["Sergio Rubio"]
|
27
|
+
# dependencies defined in Gemfile
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
RSpec::Core::RakeTask.new(:spec)
|
33
|
+
task :default => :spec
|
34
|
+
|
35
|
+
require 'rdoc/task'
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
37
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
38
|
+
|
39
|
+
rdoc.rdoc_dir = 'rdoc'
|
40
|
+
rdoc.title = "fstab #{version}"
|
41
|
+
rdoc.rdoc_files.include('README*')
|
42
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
43
|
+
end
|
data/lib/fstab.rb
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
class Fstab
|
2
|
+
|
3
|
+
VERSION = "0.1"
|
4
|
+
|
5
|
+
# if safe_mode true, non existing devices won't be added to fstab.
|
6
|
+
# Adding a non existing device to fstab will raise an exception.
|
7
|
+
# Trying to add a device without a filesystem will also rise an exception
|
8
|
+
#
|
9
|
+
def initialize(file = '/etc/fstab', opts = {})
|
10
|
+
@file = file
|
11
|
+
@contents = File.read file
|
12
|
+
@backup = opts[:backup].nil? ? true : opts[:backup]
|
13
|
+
@safe_mode = opts[:safe_mode].nil? ? true : opts[:safe_mode]
|
14
|
+
@backup_dir = opts[:backup_dir] || '/etc/'
|
15
|
+
end
|
16
|
+
|
17
|
+
def entries
|
18
|
+
parse
|
19
|
+
end
|
20
|
+
|
21
|
+
# :label => label or :uuid => uuid or :dev => dev_path
|
22
|
+
# :mount_point => mp
|
23
|
+
# :type => type
|
24
|
+
# :opts => opts
|
25
|
+
# :dump => dump
|
26
|
+
# :pass => pass
|
27
|
+
def add_entry(opts = {})
|
28
|
+
raise ArgumentError.new(":dev key is required (fs_spec).") unless opts[:dev]
|
29
|
+
dev = opts[:dev].strip.chomp
|
30
|
+
uuid = nil
|
31
|
+
label = nil
|
32
|
+
case dev
|
33
|
+
when /\/dev\// # device path
|
34
|
+
pdev = dev
|
35
|
+
when /^\/\/\w+(\.\w+)*((\/)|\w+|\.)*/ #smbfs/cifs
|
36
|
+
when /^(tmpfs|proc|usbfs|devpts|none|sysfs)/ #special FS
|
37
|
+
when /^\w+:\/?\w*(\/\w+)*/ # NFS
|
38
|
+
when /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i # UUID
|
39
|
+
uuid = dev
|
40
|
+
else # Asume FS label, rise exception if FS label does not exist
|
41
|
+
if File.blockdev?("/dev/disk/by-label/#{dev}")
|
42
|
+
label = dev
|
43
|
+
else
|
44
|
+
raise Exception.new "Unsupported filesystem #{dev}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if opts[:mount_point].nil? or opts[:type].nil? or \
|
49
|
+
opts[:opts].nil? or opts[:dump].nil? or opts[:pass].nil?
|
50
|
+
raise ArgumentError.new("Missing :mount_point, :type, :opts, :dump or :pass options")
|
51
|
+
end
|
52
|
+
|
53
|
+
if @safe_mode
|
54
|
+
if label
|
55
|
+
raise ArgumentError.new("Invalid device label #{label}") unless \
|
56
|
+
File.blockdev?("/dev/disk/by-label/#{opts[:label]}")
|
57
|
+
opts[:uuid] = Fstab.get_uuid_from_label(label)
|
58
|
+
elsif uuid
|
59
|
+
raise ArgumentError.new("Invalid device UUID #{uuid}") unless \
|
60
|
+
File.blockdev?("/dev/disk/by-uuid/#{uuid}")
|
61
|
+
opts[:uuid] = uuid
|
62
|
+
elsif pdev
|
63
|
+
raise ArgumentError.new("Invalid device path #{pdev}") unless \
|
64
|
+
File.blockdev?("#{pdev}")
|
65
|
+
opts[:uuid] = Fstab.get_uuid(pdev)
|
66
|
+
else
|
67
|
+
# Asume special device
|
68
|
+
special = true
|
69
|
+
end
|
70
|
+
unless special
|
71
|
+
raise ArgumentError.new("Duplicated entry found (safe_mode=on)") if has_device?(dev)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
backup_fstab
|
76
|
+
File.open @file, 'w' do |f|
|
77
|
+
f.puts @contents
|
78
|
+
f.puts format_entry(dev, opts)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_fs(dev, mpoint, type, opts, dump = 0, pass = 0)
|
83
|
+
o = {}
|
84
|
+
o[:dev] = dev
|
85
|
+
o[:mount_point] = mpoint
|
86
|
+
o[:type] = type
|
87
|
+
o[:opts] = opts
|
88
|
+
o[:dump] = dump
|
89
|
+
o[:pass] = pass
|
90
|
+
add_entry o
|
91
|
+
end
|
92
|
+
|
93
|
+
def line_count
|
94
|
+
@lcount
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse(reload = true)
|
98
|
+
@contents = File.read @file if reload
|
99
|
+
raise Exception.new("/sbin/blkid not found") unless File.exist?('/sbin/blkid')
|
100
|
+
fslist = {}
|
101
|
+
ucount = 0
|
102
|
+
@lcount = 0
|
103
|
+
@contents.each_line do |l|
|
104
|
+
next if l.strip.chomp.empty?
|
105
|
+
@lcount += 1
|
106
|
+
next if l =~ /\s*#/
|
107
|
+
fs, mp, type, opts, dump, pass = l.split
|
108
|
+
|
109
|
+
# FSTAB(5) states that pass and dump are optional, defaults to 0
|
110
|
+
pass = "0" unless pass
|
111
|
+
dump = "0" unless dump
|
112
|
+
pdev = nil
|
113
|
+
label = nil
|
114
|
+
uuid = nil
|
115
|
+
special = false
|
116
|
+
if l =~ /^\s*LABEL=/
|
117
|
+
# by LABEL
|
118
|
+
label = fs.split("=").last.strip.chomp
|
119
|
+
pdev = "/dev/" + File.readlink("/dev/disk/by-label/#{label}").split("/").last rescue "unknown_#{ucount}"
|
120
|
+
uuid = Fstab.get_uuid pdev
|
121
|
+
elsif l =~ /^\s*UUID=/
|
122
|
+
# by UUID
|
123
|
+
uuid = fs.split("=").last.strip.chomp
|
124
|
+
pdev = "/dev/" + File.readlink("/dev/disk/by-uuid/#{uuid}").split("/").last rescue "unknown_#{ucount}"
|
125
|
+
label = Fstab.get_label pdev
|
126
|
+
elsif l =~ /^\s*\/dev/
|
127
|
+
# by dev path
|
128
|
+
pdev = fs
|
129
|
+
blkid = `/sbin/blkid #{pdev}`
|
130
|
+
label = blkid.match(/LABEL="(.*?)"/)[1] rescue nil
|
131
|
+
uuid = blkid.match(/UUID="(.*?)"/)[1] rescue nil
|
132
|
+
else
|
133
|
+
# FIXME: somewhat risky to assume that everything else
|
134
|
+
# can be considered a special device, but validating this
|
135
|
+
# is really tricky.
|
136
|
+
special = true
|
137
|
+
pdev = fs
|
138
|
+
end
|
139
|
+
# Fstab entries not matching real devices have pdev unknown
|
140
|
+
invalid = (l.split.count != 6) # invalid entry if < 6 columns
|
141
|
+
if (uuid.nil? and label.nil? and !special) or
|
142
|
+
pdev =~ /^unknown_/ or \
|
143
|
+
(!File.exist?(pdev) and !special)
|
144
|
+
invalid = true
|
145
|
+
ucount += 1
|
146
|
+
end
|
147
|
+
|
148
|
+
invalid = true unless (dump =~ /0|1|2/ and pass =~ /0|1|2/)
|
149
|
+
|
150
|
+
fslist[pdev] = {
|
151
|
+
:label => label,
|
152
|
+
:uuid => uuid,
|
153
|
+
:mount_point => mp,
|
154
|
+
:type => type,
|
155
|
+
:opts => opts,
|
156
|
+
:dump => dump,
|
157
|
+
:pass => pass,
|
158
|
+
:special => special,
|
159
|
+
:line_number => @lcount,
|
160
|
+
:invalid => invalid,
|
161
|
+
}
|
162
|
+
end
|
163
|
+
fslist
|
164
|
+
end
|
165
|
+
|
166
|
+
def valid_entries
|
167
|
+
Hash[parse.find_all { |k,v| !v[:invalid] }]
|
168
|
+
end
|
169
|
+
|
170
|
+
def auto_header
|
171
|
+
@header ||= "#\n" +
|
172
|
+
"# This file was autogenerated at #{Time.now.to_s}\n" +
|
173
|
+
"#\n"
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# May rise exception
|
178
|
+
#
|
179
|
+
def remove_invalid_entries
|
180
|
+
return false if invalid_entries.empty?
|
181
|
+
backup_fstab
|
182
|
+
File.open @file, 'w' do |f|
|
183
|
+
f.puts auto_header
|
184
|
+
valid_entries.each do |k,v|
|
185
|
+
f.puts format_entry(k, v)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
true
|
189
|
+
end
|
190
|
+
|
191
|
+
def invalid_entries
|
192
|
+
Hash[parse.find_all { |k,v| v[:invalid] }]
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Accepts UUID/LABEL/dev
|
197
|
+
#
|
198
|
+
def find_device(dev)
|
199
|
+
# get canonical device_name
|
200
|
+
begin
|
201
|
+
dev = Fstab.get_blockdev(dev)
|
202
|
+
parse.each do |k, v|
|
203
|
+
return { k => v } if k == dev
|
204
|
+
end
|
205
|
+
rescue
|
206
|
+
end
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
|
210
|
+
def has_device?(dev)
|
211
|
+
!find_device(dev).nil?
|
212
|
+
end
|
213
|
+
|
214
|
+
# returns
|
215
|
+
# {
|
216
|
+
# :uuid => UUID,
|
217
|
+
# :label => LABEL,
|
218
|
+
# :fstype => FSTYPE,
|
219
|
+
# :dev => DEVICE
|
220
|
+
# }
|
221
|
+
#
|
222
|
+
# All the attributes except dev may be nil at any given time since
|
223
|
+
# device may not have a valid filesystem or label.
|
224
|
+
def self.get_blkdev_fs_attrs(dev)
|
225
|
+
raise ArgumentError.new("Invalid device path") unless File.blockdev?(dev)
|
226
|
+
blkid = `/sbin/blkid #{dev}`
|
227
|
+
attrs = {}
|
228
|
+
attrs[:uuid] = blkid.match(/UUID="(.*?)"/)[1] rescue nil
|
229
|
+
attrs[:label] = blkid.match(/LABEL="(.*?)"/)[1] rescue nil
|
230
|
+
attrs[:fstype] = blkid.match(/TYPE="(.*?)"/)[1] rescue nil
|
231
|
+
attrs[:dev] = blkid.match(/\/dev\/(.*):/)[1] rescue nil
|
232
|
+
attrs
|
233
|
+
end
|
234
|
+
|
235
|
+
#
|
236
|
+
# Get block device from UUID/Label
|
237
|
+
def self.get_blockdev(id)
|
238
|
+
if File.blockdev?(id) and !File.symlink?(id)
|
239
|
+
return id
|
240
|
+
end
|
241
|
+
path = nil
|
242
|
+
# Try to get blockdev from UUID first, then label
|
243
|
+
begin
|
244
|
+
path = File.readlink("/dev/disk/by-uuid/#{id}")
|
245
|
+
rescue
|
246
|
+
path = File.readlink("/dev/disk/by-label/#{id}")
|
247
|
+
end
|
248
|
+
"/dev/#{path.split('/').last}"
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.get_uuid(dev)
|
252
|
+
#`/sbin/blkid #{dev}`.match(/UUID="(.*?)"/)[1] rescue nil
|
253
|
+
Fstab.get_blkdev_fs_attrs(dev)[:uuid]
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.get_uuid_from_label(label)
|
257
|
+
Fstab.get_blkdev_fs_attrs("/dev/disk/by-label/#{label}")[:uuid]
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.get_label(dev)
|
261
|
+
Fstab.get_blkdev_fs_attrs(dev)[:label]
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
def format_entry(dev, values)
|
266
|
+
if values[:special]
|
267
|
+
"#{dev} #{values[:mount_point]} #{values[:type]} " +
|
268
|
+
"#{values[:opts]} #{values[:dump]} #{values[:pass]}"
|
269
|
+
else
|
270
|
+
if values[:uuid]
|
271
|
+
"UUID=#{values[:uuid]} #{values[:mount_point]} #{values[:type]} " +
|
272
|
+
"#{values[:opts]} #{values[:dump]} #{values[:pass]}"
|
273
|
+
else
|
274
|
+
"#{dev} #{values[:mount_point]} #{values[:type]} " +
|
275
|
+
"#{values[:opts]} #{values[:dump]} #{values[:pass]}"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def backup_fstab
|
281
|
+
return unless @backup
|
282
|
+
File.open("#{@backup_dir}/fstab.#{Time.now.to_f}.bak", 'w') do |f|
|
283
|
+
f.puts @contents
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fstab do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@fake_fstab = "/tmp/fstab.#{Time.now.to_f}.fake"
|
7
|
+
File.open @fake_fstab, 'w' do |f|
|
8
|
+
f.puts ""
|
9
|
+
end
|
10
|
+
|
11
|
+
@real_fstab = "/tmp/fstab.#{Time.now.to_f}.real"
|
12
|
+
File.open @real_fstab, 'w' do |f|
|
13
|
+
f.puts File.read('/etc/fstab')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after :each do
|
18
|
+
File.delete @fake_fstab
|
19
|
+
File.delete @real_fstab
|
20
|
+
Dir["/tmp/fstab*.bak"].each do |f|
|
21
|
+
File.delete f
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#new" do
|
26
|
+
it "accepts two optional arguments" do
|
27
|
+
h = Fstab.new '/etc/fstab', {}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises exception unless opts is not Hash" do
|
31
|
+
lambda { Fstab.new(@real_fstab, false) }.should raise_error NoMethodError
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts no arguments" do
|
35
|
+
h = Fstab.new
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises exception if file does not exist" do
|
39
|
+
lambda { Fstab.new('/tmp/lkjsdfouwoiue') }.should raise_exception(Errno::ENOENT)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "reads custom #{@fake_fstab}" do
|
43
|
+
h = Fstab.new @fake_fstab
|
44
|
+
h.instance_variable_get(:@contents).strip.chomp.should be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
it "has backups enabled by default" do
|
48
|
+
h = Fstab.new @fake_fstab
|
49
|
+
h.instance_variable_get(:@backup).should be_true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#parse" do
|
55
|
+
it "should count lines" do
|
56
|
+
count = File.read('/etc/fstab').lines.count
|
57
|
+
f = Fstab.new(@real_fstab); f.parse
|
58
|
+
f.line_count.should be count
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should return empty Hash if empty" do
|
62
|
+
Fstab.new(@fake_fstab).parse.should be_empty
|
63
|
+
end
|
64
|
+
it "should have 1 special FS" do
|
65
|
+
File.open @fake_fstab, 'w+' do |f|
|
66
|
+
f.puts 'proc /proc proc nodev,noexec,nosuid 0 0'
|
67
|
+
f.flush
|
68
|
+
fstab = Fstab.new @fake_fstab
|
69
|
+
devs = fstab.parse
|
70
|
+
devs.count.should == 1
|
71
|
+
# d[:special] == true
|
72
|
+
devs.first.last[:special].should == true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should detect special filesystems as valid" do
|
77
|
+
File.open @fake_fstab, 'w+' do |f|
|
78
|
+
f.puts 'proc /proc proc nodev,noexec,nosuid 0 0'
|
79
|
+
f.flush
|
80
|
+
fstab = Fstab.new @fake_fstab
|
81
|
+
devs = fstab.parse
|
82
|
+
devs.count.should == 1
|
83
|
+
devs.first.last[:invalid].should == false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should detect invalid dump/pass columns" do
|
88
|
+
File.open @fake_fstab, 'w+' do |f|
|
89
|
+
f.puts 'proc /proc proc nodev,noexec,nosuid a b'
|
90
|
+
f.flush
|
91
|
+
fstab = Fstab.new @fake_fstab
|
92
|
+
devs = fstab.parse
|
93
|
+
devs.count.should == 1
|
94
|
+
devs.first.last[:invalid].should == true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should detect invalid filesystems" do
|
99
|
+
File.open @fake_fstab, 'w+' do |f|
|
100
|
+
f.puts '/dev/foobar /tmp/bar xfs defaults 0 0'
|
101
|
+
f.flush
|
102
|
+
fstab = Fstab.new @fake_fstab
|
103
|
+
devs = fstab.parse
|
104
|
+
devs.count.should == 1
|
105
|
+
devs.first.last[:invalid].should == true
|
106
|
+
f.puts 'weird_entry foo bar'
|
107
|
+
f.flush
|
108
|
+
devs = fstab.parse
|
109
|
+
devs.count.should == 2
|
110
|
+
devs.each do |k,v|
|
111
|
+
v[:invalid].should == true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#add_fs" do
|
118
|
+
it "should raise an exception adding a non existing device" do
|
119
|
+
f = Fstab.new(@real_fstab)
|
120
|
+
lambda {f.add_fs '/dev/fff', '/foo/bar', 'xfs', 'defaults'}.should \
|
121
|
+
raise_error /Invalid device path/
|
122
|
+
end
|
123
|
+
it "should raise an exception adding a non existing dev label" do
|
124
|
+
f = Fstab.new(@real_fstab)
|
125
|
+
lambda {f.add_fs 'my-foo-fs', '/foo/bar', 'xfs', 'defaults'}.should \
|
126
|
+
raise_error /Unsupported filesystem/
|
127
|
+
end
|
128
|
+
it "should raise an exception adding a non existing dev uuid" do
|
129
|
+
f = Fstab.new(@real_fstab)
|
130
|
+
lambda {f.add_fs 'ca803c3f-32e6-423e-994a-52f648a0321d', '/foo/bar', 'xfs', 'defaults'}.should \
|
131
|
+
raise_error /Invalid device UUID/
|
132
|
+
end
|
133
|
+
it "should raise an exception adding a non existing dev label" do
|
134
|
+
f = Fstab.new(@real_fstab)
|
135
|
+
lambda {f.add_fs 'my-foo-fs', '/foo/bar', 'xfs', 'defaults'}.should \
|
136
|
+
raise_error /Unsupported filesystem/
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should add invalid device when safe_mode=false" do
|
140
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup_dir => '/tmp')
|
141
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
142
|
+
f.invalid_entries.count.should == 1
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should create a backup when backup=true (default)" do
|
146
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup_dir => '/tmp')
|
147
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
148
|
+
Dir["/tmp/fstab*.bak"].count.should == 1
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should NOT create a backup when backup=false" do
|
152
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
153
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
154
|
+
Dir["/tmp/fstab*.bak"].count.should == 0
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should add a valid special FS entry" do
|
158
|
+
f = Fstab.new(@fake_fstab, :backup => false, :backup_dir => '/tmp')
|
159
|
+
prev_count = f.parse.count
|
160
|
+
f.add_fs 'tmpfs', '/foo/bar', 'tmpfs', 'size=1024', '0', '0'
|
161
|
+
f.parse.count.should == prev_count + 1
|
162
|
+
File.read(@fake_fstab).should match /^tmpfs/m
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should add a FS UUID" do
|
166
|
+
f = Fstab.new(@fake_fstab, :safe_mode => true, :backup => false, :backup_dir => '/tmp')
|
167
|
+
pdev, uuid, label, type = get_first_blkid
|
168
|
+
f.add_fs uuid, '/foo/bar', type, 'defaults', '0', '0'
|
169
|
+
File.read(@fake_fstab).should match /^UUID=#{uuid}/m
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should add a FS UUID when using a blockdev" do
|
173
|
+
f = Fstab.new(@fake_fstab, :safe_mode => true, :backup => false, :backup_dir => '/tmp')
|
174
|
+
pdev, uuid, label, type = get_first_blkid
|
175
|
+
f.add_fs pdev, '/foo/bar', type, 'defaults', '0', '0'
|
176
|
+
File.read(@fake_fstab).should match /^UUID=#{uuid}/m
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should raise error when adding duplicated entry and safe_mode=true" do
|
180
|
+
f = Fstab.new(@fake_fstab, :backup => false, :backup_dir => '/tmp')
|
181
|
+
pdev, uuid, label, type = get_first_blkid
|
182
|
+
f.add_fs uuid, '/foo/bar', type, 'defaults', '0', '0'
|
183
|
+
lambda {f.add_fs uuid, '/foo/bar', type, 'defaults', '0', '0'}.should raise_error /Duplicated entry found/
|
184
|
+
lambda {f.add_fs pdev, '/foo/bar', type, 'defaults', '0', '0'}.should raise_error /Duplicated entry found/
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "#add_entry" do
|
189
|
+
it "should raise an exception when missing mount_point/type/opts,dump,pass" do
|
190
|
+
f = Fstab.new(@real_fstab)
|
191
|
+
opts = {
|
192
|
+
:dev => '/dev/sda',
|
193
|
+
:mount_point => '/foo/bar',
|
194
|
+
:type => 'xfs',
|
195
|
+
:opts => 'defaults',
|
196
|
+
:dump => 0,
|
197
|
+
:pass => 0
|
198
|
+
}
|
199
|
+
[:mount_point, :type, :opts, :dump, :pass].each do |s|
|
200
|
+
dup_opts = opts.dup; dup_opts.delete s
|
201
|
+
lambda {f.add_entry dup_opts}.should \
|
202
|
+
raise_error /Missing :mount_point, :type, :opts/
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should raise and exception when dev/uuid/label nil" do
|
207
|
+
f = Fstab.new(@real_fstab)
|
208
|
+
opts = {
|
209
|
+
:mount_point => '/foo/bar',
|
210
|
+
:type => 'xfs',
|
211
|
+
:opts => 'defaults',
|
212
|
+
:dump => 0,
|
213
|
+
:pass => 0
|
214
|
+
}
|
215
|
+
lambda {f.add_entry opts}.should \
|
216
|
+
raise_error /:dev key is required/
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#invalid_entries" do
|
221
|
+
it "should return one entry" do
|
222
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
223
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
224
|
+
f.invalid_entries.count.should == 1
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "#valid_entries" do
|
229
|
+
it "should only return valid entries" do
|
230
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
231
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
232
|
+
lcount = File.read(@real_fstab).lines.find_all do |l|
|
233
|
+
(l.strip.chomp !~ /^#/) and !l.strip.chomp.empty?
|
234
|
+
end.count
|
235
|
+
f.valid_entries.count.should == lcount - 1
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should match the number of devices" do
|
239
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
240
|
+
f.valid_entries.count.should == f.parse.count
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "#remove_invalid_entries" do
|
245
|
+
it "should remove 1 entry" do
|
246
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
247
|
+
prev_count = f.parse.count
|
248
|
+
f.add_fs '/dev/foo_dev', '/foo/bar', 'xfs', 'defaults'
|
249
|
+
f.remove_invalid_entries.should == true
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "#find_device" do
|
254
|
+
it "should return nil if device not found" do
|
255
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
256
|
+
f.find_device('/alksjdflkjsd').should be_nil
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should return a valid device if device is defined" do
|
260
|
+
pdev, uuid, label, type = get_first_blkid
|
261
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
262
|
+
f.add_fs pdev, '/foo/bar', type, 'defaults'
|
263
|
+
f.find_device(pdev).should be_a Hash
|
264
|
+
k, dev = f.find_device(pdev).first
|
265
|
+
dev.should_not be_nil
|
266
|
+
k.should match pdev
|
267
|
+
dev[:mount_point].should match '/foo/bar'
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe "#has_device?" do
|
272
|
+
it "should return true if device is defined" do
|
273
|
+
pdev, uuid, label, type = get_first_blkid
|
274
|
+
f = Fstab.new(@fake_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
275
|
+
f.add_fs pdev, '/foo/bar', type, 'defaults'
|
276
|
+
f.has_device?(pdev).should == true
|
277
|
+
f.has_device?(uuid).should == true
|
278
|
+
end
|
279
|
+
it "should return true if device is defined" do
|
280
|
+
pdev, uuid, label, type = get_first_blkid
|
281
|
+
f = Fstab.new(@real_fstab, :safe_mode => false, :backup => false, :backup_dir => '/tmp')
|
282
|
+
f.add_fs pdev, '/foo/bar', type, 'defaults'
|
283
|
+
f.has_device?(pdev).should == true
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "get_label" do
|
288
|
+
it "should raise exception for an invalid device" do
|
289
|
+
lambda {Fstab.get_label('/akjsdfljaslkdj')}.should raise_error ArgumentError
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should return a valid LABEL for device with a labeled FS" do
|
293
|
+
pdev, uuid, label, type = get_first_blkid
|
294
|
+
Fstab.get_label(pdev).should be_nil
|
295
|
+
Fstab.get_uuid(pdev).should match uuid
|
296
|
+
if label.nil?
|
297
|
+
Fstab.get_label(pdev).should be_nil
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "get_uuid" do
|
303
|
+
it "should return a valid UUID for device with a FS" do
|
304
|
+
pdev, uuid, label, type = get_first_blkid
|
305
|
+
Fstab.get_uuid(pdev).should match uuid
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../lib')
|
2
|
+
require 'fstab'
|
3
|
+
|
4
|
+
def get_first_blkid
|
5
|
+
blkid = `/sbin/blkid`.lines.first
|
6
|
+
uuid = blkid.match(/UUID="(.*?)"\s+/)[1]
|
7
|
+
label = blkid.match(/LABEL="(.*?)"\s+/)[1] rescue nil
|
8
|
+
type = blkid.match(/TYPE="(.*?)"\s+/)[1]
|
9
|
+
pdev = blkid.match(/(\/dev\/.*?):/)[1]
|
10
|
+
return pdev, uuid, label, type
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fstab
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sergio Rubio
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: jeweler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.8.4
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.8.4
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Linux fstab helper library
|
63
|
+
email: rubiojr@bvox.net
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files:
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
files:
|
70
|
+
- .document
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- lib/fstab.rb
|
76
|
+
- spec/fstab_helper_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
homepage: http://github.com/bvox/fstab
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
hash: 866801040596206319
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.8.24
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Linux fstab helper library
|
106
|
+
test_files: []
|