file_mode 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|