file_mode 0.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.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/README.rdoc +87 -0
- data/Rakefile +34 -0
- data/lib/file_mode.rb +150 -0
- data/test/test_aix_listing.rb +4114 -0
- data/test/test_file_mode.rb +43 -0
- data/test/test_linux_listing.rb +12309 -0
- data/test/test_sunos_listing.rb +4114 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e7111de3e1b1269c364ec2b68c7337f75686cc17
|
4
|
+
data.tar.gz: 2287af1a240cce77f7b370a4987271259f72e057
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6516cac2615f49fb57edd9964a5b9cc1cf128e3a1bebcfc98dafb76feb14343b7351e1bb786302d92cb41b25de39ad4a9ba60f871810c6cdd77f5d7b537f7a15
|
7
|
+
data.tar.gz: f593a5c60f92342ed114aacd8edda386f9d60231bf237cb5cc946b80a9b4b71d6e93df91e3b2db8112af59e17d80a3dfd581eedebaf70d16caf27f2fe1dc8b76
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright 2014 Martin Carpenter. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are
|
4
|
+
permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of
|
7
|
+
conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY Martin Carpenter ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
14
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Martin Carpenter OR
|
16
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
17
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
18
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
19
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
20
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
21
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
|
+
|
23
|
+
The views and conclusions contained in the software and documentation are those of the
|
24
|
+
authors and should not be interpreted as representing official policies, either expressed
|
25
|
+
or implied, of Martin Carpenter.
|
26
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
|
2
|
+
= file_mode
|
3
|
+
|
4
|
+
Author:: Martin Carpenter
|
5
|
+
Email:: mcarpenter@free.fr
|
6
|
+
Copyright:: Copyright (c) Martin Carpenter 2014
|
7
|
+
|
8
|
+
== About
|
9
|
+
|
10
|
+
The file_mode gem assists with manipulation of four-octal-digit UNIX
|
11
|
+
file modes by providing readable method names rather than error-prone
|
12
|
+
bit twiddles. It also provides methods to convert to and from standard
|
13
|
+
ls(1)-style long listing formats. The class methods can be used standalone
|
14
|
+
or this module can be mixed in to a class that has an integer :mode
|
15
|
+
attribute.
|
16
|
+
|
17
|
+
== Examples
|
18
|
+
|
19
|
+
=== Class method use
|
20
|
+
|
21
|
+
require 'file_mode'
|
22
|
+
|
23
|
+
FileMode.user_readable?(0o644)
|
24
|
+
=> true
|
25
|
+
FileMode.setuid?(0o4755)
|
26
|
+
=> true
|
27
|
+
FileMode.int_to_listing(0o644)
|
28
|
+
=> "rw-r--r--"
|
29
|
+
FileMode.listing_to_str('rwsr-xr-x')
|
30
|
+
=> "4755"
|
31
|
+
|
32
|
+
=== Mixin use
|
33
|
+
|
34
|
+
require 'file_mode'
|
35
|
+
|
36
|
+
class FileLikeThing
|
37
|
+
include FileMode
|
38
|
+
attr_accessor :mode
|
39
|
+
end
|
40
|
+
|
41
|
+
flt = FileLikeThing.new
|
42
|
+
flt.mode = 0o0644
|
43
|
+
flt.user_readable?
|
44
|
+
=> true
|
45
|
+
flt.setuid?
|
46
|
+
=> false
|
47
|
+
|
48
|
+
== Mode formats
|
49
|
+
|
50
|
+
The module offers three forms of mode representation:
|
51
|
+
|
52
|
+
* integer (eg 0o0644 or 420 decimal)
|
53
|
+
* string ('0644')
|
54
|
+
* listing ('rw-r--r--')
|
55
|
+
|
56
|
+
Listing formats are not necessarily identical across UNIX-like operating
|
57
|
+
systems, or even across applications on the same operating system. A
|
58
|
+
case in point is Solaris which shows mandatory file lock permission as
|
59
|
+
'l' with standard ls(1) and 'L' with XPG-compatible ls(1).
|
60
|
+
|
61
|
+
This module aims to take any standard format as input
|
62
|
+
(listing_to_... methods) and targets Linux/GNU coreutils for
|
63
|
+
output(..._to_listing methods).
|
64
|
+
|
65
|
+
=== Some conversion gotchas
|
66
|
+
|
67
|
+
Beware the following standard ruby gotchas when making conversions using
|
68
|
+
#to_i and a solitary leading zero to specify octal mode:
|
69
|
+
|
70
|
+
644.to_i
|
71
|
+
=> 644 # ok
|
72
|
+
'644'.to_i
|
73
|
+
=> 644 # ok
|
74
|
+
0644.to_i
|
75
|
+
=> 420 # ok
|
76
|
+
'0644'.to_i
|
77
|
+
=> 644 # ! leading zero is ignored
|
78
|
+
'0o644'.to_i
|
79
|
+
=> 0 # ! parsing stops at 'o'
|
80
|
+
|
81
|
+
== Unit tests
|
82
|
+
|
83
|
+
Unit tests are provided particularly for the conversion between the
|
84
|
+
ls(1)-style long-listing format and integer representations. Since
|
85
|
+
the long-listing format can vary between systems a script
|
86
|
+
bin/create_listing_test.rb is provided to generate unit test files.
|
87
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'rubygems/package_task'
|
6
|
+
|
7
|
+
desc 'Default task (package)'
|
8
|
+
task :default => [:package]
|
9
|
+
|
10
|
+
desc 'Create listing unit test'
|
11
|
+
task :create_listing_test do
|
12
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
13
|
+
load File.join('bin', 'create_listing_test.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
Rake::TestTask.new( 'test' )
|
17
|
+
|
18
|
+
SPECFILE = 'file_mode.gemspec'
|
19
|
+
if File.exist?(SPECFILE)
|
20
|
+
spec = eval(File.read(SPECFILE))
|
21
|
+
Gem::PackageTask.new(spec).define
|
22
|
+
end
|
23
|
+
|
24
|
+
RDoc::Task.new do |rdoc|
|
25
|
+
rdoc.rdoc_dir = 'rdoc'
|
26
|
+
rdoc.title = 'file_mode'
|
27
|
+
rdoc.options << '--charset' << 'utf-8'
|
28
|
+
rdoc.options << '--main' << 'README.rdoc'
|
29
|
+
rdoc.options << '--all'
|
30
|
+
rdoc.rdoc_files.include('README.rdoc' )
|
31
|
+
rdoc.rdoc_files.include(FileList['lib/**/*.rb'])
|
32
|
+
rdoc.rdoc_files.include(FileList['test/*.rb' ])
|
33
|
+
end
|
34
|
+
|
data/lib/file_mode.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
|
2
|
+
# Module for UNIX octal file modes. Classes that mixin this module
|
3
|
+
# must have a mode attribute that is the integer representation
|
4
|
+
# of the mode.
|
5
|
+
module FileMode
|
6
|
+
|
7
|
+
# Class methods. These are both extended into the FileMode
|
8
|
+
# class and mixed-in as instance methods on classes that
|
9
|
+
# "include FileMode" when the class Fixnum attribute :mode
|
10
|
+
# is used in place of the argument of the same name.
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Return true if the mode is readable by user, false otherwise.
|
14
|
+
def user_readable?(mode)
|
15
|
+
mode & 00400 == 00400
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return true if the mode is readable by group, false otherwise.
|
19
|
+
def group_readable?(mode)
|
20
|
+
mode & 00040 == 00040
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return true if the mode is readable by other, false otherwise.
|
24
|
+
def other_readable?(mode)
|
25
|
+
mode & 00004 == 00004
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return true if the mode is writable by user, false otherwise.
|
29
|
+
def user_writable?(mode)
|
30
|
+
mode & 00200 == 00200
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return true if the mode is writable by group, false otherwise.
|
34
|
+
def group_writable?(mode)
|
35
|
+
mode & 00020 == 00020
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return true if the mode is writable by other, false otherwise.
|
39
|
+
def other_writable?(mode)
|
40
|
+
mode & 00002 == 00002
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return true if the mode is executable by user, false otherwise.
|
44
|
+
def user_executable?(mode)
|
45
|
+
mode & 00100 == 00100
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return true if the mode is executable by group, false otherwise.
|
49
|
+
def group_executable?(mode)
|
50
|
+
mode & 00010 == 00010
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return true if the mode is executable by other, false otherwise.
|
54
|
+
def other_executable?(mode)
|
55
|
+
mode & 00001 == 00001
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return true if mode has the setuid bit set, false otherwise.
|
59
|
+
def setuid?(mode)
|
60
|
+
mode & 04000 == 04000
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return true if mode has the setgid bit set, false otherwise.
|
64
|
+
def setgid?(mode)
|
65
|
+
mode & 02000 == 02000
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return true if mode has the sticky bit set, false otherwise.
|
69
|
+
def sticky?(mode)
|
70
|
+
mode & 01000 == 01000
|
71
|
+
end
|
72
|
+
|
73
|
+
end # ClassMethods
|
74
|
+
|
75
|
+
extend ClassMethods
|
76
|
+
|
77
|
+
# Convert an integer mode to a 4 digit octal string.
|
78
|
+
def self.int_to_str(int)
|
79
|
+
int.to_s(8).rjust(4, '0')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Convert an octal string to an integer.
|
83
|
+
def self.str_to_int(str)
|
84
|
+
str.to_i(8)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Convert a permissions listing string to its integer equivalent.
|
88
|
+
def self.listing_to_int(listing)
|
89
|
+
int = listing.chars.map do |c|
|
90
|
+
# l/L are for mandatory file locking on Solaris.
|
91
|
+
%w{ - S T l L }.include?(c) ? 0 : 1
|
92
|
+
end.join.to_i(2)
|
93
|
+
int += 04000 if %w{ s S }.include?(listing[2]) # setuid
|
94
|
+
int += 02000 if %w{ s S l L }.include?(listing[5]) # setgid
|
95
|
+
int += 01000 if %w{ t T }.include?(listing[8]) # sticky
|
96
|
+
int
|
97
|
+
end
|
98
|
+
|
99
|
+
# Convert permissions listing string to a string of its octal representation.
|
100
|
+
def self.listing_to_str(listing)
|
101
|
+
int_to_str(listing_to_int(listing))
|
102
|
+
end
|
103
|
+
|
104
|
+
# Mapping between a single octal digit and its permissions listing.
|
105
|
+
OCTAL_DIGIT_TO_LISTING = {
|
106
|
+
0 => '---',
|
107
|
+
1 => '--x',
|
108
|
+
2 => '-w-',
|
109
|
+
3 => '-wx',
|
110
|
+
4 => 'r--',
|
111
|
+
5 => 'r-x',
|
112
|
+
6 => 'rw-',
|
113
|
+
7 => 'rwx'
|
114
|
+
}
|
115
|
+
|
116
|
+
# Convert an integer into its permissions listing string. Listing strings
|
117
|
+
# may vary between operating systems (and even between applications on the
|
118
|
+
# same operating system). The listing output here matches the ouput from
|
119
|
+
# Linux/GNU coreutils ls(1).
|
120
|
+
def self.int_to_listing(int)
|
121
|
+
perms = [ [6, 00700], [3, 00070], [0, 00007] ].map do |bitshift, mask|
|
122
|
+
OCTAL_DIGIT_TO_LISTING[(int & mask) >> bitshift]
|
123
|
+
end.join
|
124
|
+
if 0 != int & 04000 # setuid
|
125
|
+
perms[2] = perms[2] == 'x' ? 's' : 'S'
|
126
|
+
end
|
127
|
+
if 0 != int & 02000 # setgid
|
128
|
+
perms[5] = perms[5] == 'x' ? 's' : 'S'
|
129
|
+
end
|
130
|
+
if 0 != int & 01000 # sticky
|
131
|
+
perms[8] = perms[8] == 'x' ? 't' : 'T'
|
132
|
+
end
|
133
|
+
perms
|
134
|
+
end
|
135
|
+
|
136
|
+
# Convert an octal string into its permissions listing string.
|
137
|
+
def self.str_to_listing(str)
|
138
|
+
int_to_listing(str.to_i(8))
|
139
|
+
end
|
140
|
+
|
141
|
+
# Create instance methods on FileMode that forward to the
|
142
|
+
# corresponding class method with an implicit self.mode argument.
|
143
|
+
ClassMethods.instance_methods(false).each do |mixin_method|
|
144
|
+
define_method(mixin_method) do |*args|
|
145
|
+
FileMode.send(mixin_method, *(args.dup.unshift(self.mode)))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end # FileMode
|
150
|
+
|