From: Trevor Vaughan <[email protected]>

Signed-off-by: Trevor Vaughan - Onyx Point <[email protected]>
---
 lib/puppet/type/file.rb      |    2 +-
 lib/puppet/type/file/mode.rb |  141 +++++++++++++++++++++++++++++++++---------
 spec/unit/type/file/mode.rb  |   61 ++++++++++++++++++
 3 files changed, 173 insertions(+), 31 deletions(-)
 create mode 100755 spec/unit/type/file/mode.rb

diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb
index 2f5b5df..ede4b1a 100644
--- a/lib/puppet/type/file.rb
+++ b/lib/puppet/type/file.rb
@@ -725,7 +725,7 @@ module Puppet
                 path = self[:path]
               end
 
-            mode = self.should(:mode) # might be nil
+            mode = self.should(:mode) ? 
self.property(:mode).sym2oct(self.stat,self.should(:mode)) : nil # might be nil
             umask = mode ? 000 : 022
 
             Puppet::Util.withumask(umask) do
diff --git a/lib/puppet/type/file/mode.rb b/lib/puppet/type/file/mode.rb
index 83034cb..da0d09d 100755
--- a/lib/puppet/type/file/mode.rb
+++ b/lib/puppet/type/file/mode.rb
@@ -4,9 +4,8 @@
 module Puppet
     Puppet::Type.type(:file).newproperty(:mode) do
         require 'etc'
-        desc "Mode the file should be.  Currently relatively limited:
-            you must specify the exact mode the file should be.
- 
+        desc "Mode the file should be.  You may specify either the precise 
octal mode or the POSIX symbolic mode per GNU coreutils chmod.
+
             Note that when you set the mode of a directory, Puppet always
             sets the search/traverse (1) bit anywhere the read (4) bit is set. 
             This is almost always what you want: read allows you to list the
@@ -22,16 +21,90 @@ module Puppet
 
             In this case all of the files underneath ``/some/dir`` will have 
             mode 644, and all of the directories will have mode 755."
-
         @event = :file_changed
 
+        # The bitwise position of the UGO fields.
+        SYMBASE = {
+                    "u" => 6,
+                    "g" => 3,
+                    "o" => 0
+                  }
+
+        # The general mask for activating the appropriate sections of 'how'.
+        SYMLEFT = {
+                    "u" => 05700,
+                    "g" => 03070,
+                    "o" => 01007,
+                    "a" => 07777
+                  }
+       # The regular expression for matching a valid symbolic mode.
+       SYMREG = /^(([ugoa]+)([+-=])([rwxst]+|[ugo]),?)+$/
+
+        # This is a helper that takes the current mode and the new mode and
+        # returns the adjusted decimal representation octal file mode (0700,
+        # etc...)
+        #
+        # The current mode (curmode) can be represented either as an integer
+        # string or as a File::Stat object.
+        def sym2oct(curmode,newmode)
+            if !newmode.nil? and newmode.to_s =~ /^\d+$/ then
+                value = newmode
+            else
+                # Set this to 0600 so that we can actually read and write the
+                # file as a normal user.
+                if curmode.nil? then
+                    value = 00600
+                elsif curmode.is_a?(File::Stat) then
+                    value = curmode.mode & 07777
+                else
+                    value = Integer("0#{curmode}")
+                end
+
+                # This needs to remain variable.
+                right = {
+                        "r" => 00444, 
+                        "w" => 00222, 
+                        "x" => 00111, 
+                        "s" => 06000, 
+                        "t" => 01000, 
+                        "u" => 00700, 
+                        "g" => 00070, 
+                        "o" => 00007 
+                        }
+
+                newmode.split(",").each do |cmd|
+                    match = cmd.match(SYMREG) or return value
+                    # The following vars are directly dependent on the
+                    # structure of SYMREG above
+                    who = match[2]
+                    what = match[3]
+                    how = match[4].split(//).uniq.to_s
+                    if how =~ /^[ugo]$/ then
+                      who.split(//).uniq.each do |lhv|
+                        right[how] = ( ((value << (SYMBASE[lhv] - 
SYMBASE[how])) & right[lhv]) | ( value & ~right[lhv] ) ) & 0777
+                      end
+                    end
+                    who = who.split(//).inject(num=0) {|num,b| num |= 
SYMLEFT[b]; num }
+                    how = how.split(//).inject(num=0) {|num,b| num |= 
right[b]; num }
+                    mask = who & how
+                    case what
+                        when "+": value = value | mask
+                        when "-": value = value & ~mask
+                        when "=": value = ( mask & who ) | ( value & ~(who & 
0777) )
+                    end
+                end
+                value = "0%o" % value
+            end
+            Integer(value)
+        end
+
+
         # Our modes are octal, so make sure they print correctly.  Other
         # valid values are symbols, basically
         def is_to_s(currentvalue)
-            case currentvalue
-            when Integer
+            if currentvalue.is_a?(Integer) then
                 return "%o" % currentvalue
-            when Symbol
+            elsif ( currentvalue.is_a?(Symbol) or ( currentvalue.is_a?(String) 
and currentvalue.match(SYMREG))) then
                 return currentvalue
             else
                 raise Puppet::DevError, "Invalid current value for mode: %s" %
@@ -40,10 +113,9 @@ module Puppet
         end
 
         def should_to_s(newvalue = @should)
-            case newvalue
-            when Integer
+            if newvalue.is_a?(Integer) then
                 return "%o" % newvalue
-            when Symbol
+            elsif ( newvalue.is_a?(Symbol) or ( newvalue.is_a?(String) and 
newvalue.match(SYMREG))) then
                 return newvalue
             else
                 raise Puppet::DevError, "Invalid 'should' value for mode: %s" %
@@ -52,29 +124,32 @@ module Puppet
         end
 
         munge do |should|
-            # this is pretty hackish, but i need to make sure the number is in
-            # octal, yet the number can only be specified as a string right now
+            # This handles both numbers and symbolic modes matching SYMREG
+            #
+            # Note: This now returns a string and the accepting function must
+            # know how to handle it!
+
             value = should
             if value.is_a?(String)
-                unless value =~ /^\d+$/
-                    raise Puppet::Error, "File modes can only be numbers, not 
%s" %
+                if value =~ /^\d+$/ then
+                    unless value =~ /^0/
+                        value = "0#{value}"
+                    end
+
+                    old = value
+                    begin
+                        value = Integer(value)
+                    rescue ArgumentError => detail
+                        raise Puppet::DevError, "Could not convert %s to 
integer" %
+                            old.inspect
+                    end
+                elsif value.match(SYMREG).nil? then
+                    raise Puppet::DevError, "Symbolic mode %s does not match 
#{SYMREG}" %
                         value.inspect
                 end
-                # Make sure our number looks like octal.
-                unless value =~ /^0/
-                    value = "0" + value
-                end
-                old = value
-                begin
-                    value = Integer(value)
-                rescue ArgumentError => detail
-                    raise Puppet::DevError, "Could not convert %s to integer" %
-                        old.inspect
-                end
             end
-
             return value
-        end
+       end
 
         # If we're a directory, we need to be executable for all cases
         # that are readable.  This should probably be selectable, but eh.
@@ -99,7 +174,14 @@ module Puppet
                 self.debug "Not managing symlink mode"
                 return true
             else
-                return super(currentvalue)
+                retval = super(currentvalue)
+                if !retval then
+                    if currentvalue == sym2oct(resource.stat,self.should) then
+                        retval = true
+                    end
+                end
+
+                return retval
             end
         end
 
@@ -120,8 +202,7 @@ module Puppet
         end
 
         def sync
-            mode = self.should
-
+            mode = sym2oct(resource.stat,self.should)
             begin
                 File.chmod(mode, @resource[:path])
             rescue => detail
diff --git a/spec/unit/type/file/mode.rb b/spec/unit/type/file/mode.rb
new file mode 100755
index 0000000..4cdbc36
--- /dev/null
+++ b/spec/unit/type/file/mode.rb
@@ -0,0 +1,61 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? 
require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+property = Puppet::Type.type(:file).attrclass(:mode)
+
+describe property do
+    before do
+        @resource = stub 'resource', :line => "foo", :file => "bar"
+        @mode = property.new :resource => @resource
+    end
+
+    it "should have a method for converting symbolic modes to octal modes" do
+        @mode.must respond_to(:sym2oct)
+    end
+    it "should be able to apply three digit numeric octal modes" do
+        @mode.sym2oct("777",Integer("0640")).should == Integer("0640")
+    end
+    it "should be able to apply additive symbolic user modes" do
+        @mode.sym2oct("640","u+x").should == Integer("0740")
+    end
+    it "should be able to apply subtractive symbolic user modes" do
+        @mode.sym2oct("640","u-w").should == Integer("0440")
+    end
+    it "should be able to apply equality symbolic user modes" do
+        @mode.sym2oct("640","u=r").should == Integer("0440")
+    end
+    it "should be able to apply referential equality symbolic user modes" do
+        @mode.sym2oct("640","u=g").should == Integer("0440")
+    end
+    it "should be able to apply additive symbolic group modes" do
+        @mode.sym2oct("640","g+x").should == Integer("0650")
+    end
+    it "should be able to apply subtractive symbolic group modes" do
+        @mode.sym2oct("640","g-r").should == Integer("0600")
+    end
+    it "should be able to apply equality symbolic group modes" do
+        @mode.sym2oct("640","g=rwx").should == Integer("0670")
+    end
+    it "should be able to apply referential equality symbolic group modes" do
+        @mode.sym2oct("640","g=o").should == Integer("0600")
+    end
+    it "should be able to apply additive symbolic other modes" do
+        @mode.sym2oct("640","o+rx").should == Integer("0645")
+    end
+    it "should be able to apply subtractive symbolic other modes" do
+        @mode.sym2oct("647","o-rx").should == Integer("0642")
+    end
+    it "should be able to apply equality symbolic other modes" do
+        @mode.sym2oct("647","o-rx").should == Integer("0642")
+    end
+    it "should be able to apply referential equality symbolic other modes" do
+        @mode.sym2oct("647","o=u").should == Integer("0646")
+    end
+    it "should be able to apply multi-part modes" do
+        @mode.sym2oct("640","o=u,g=o,u-w").should == Integer("0466")
+    end
+    it "should not be able to apply invalid modes" do
+        @mode.sym2oct("640","go-write-a-letter").should == Integer("0640")
+    end
+end
-- 
1.6.2.5

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to