summaryrefslogtreecommitdiffstats
path: root/lib/puppet/type/file/mode.rb
blob: 31209f42ec714afab327cbff838c2e1ea882305b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# Manage file modes.  This state should support different formats
# for specification (e.g., u+rwx, or -0011), but for now only supports
# specifying the full mode.
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.

            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
            entries in a directory, and search/traverse allows you to access
            (read/write/execute) those entries.)  Because of this feature, you
            can recursively make a directory and all of the files in it
            world-readable by setting e.g.::

                file { '/some/dir':
                    mode => 644,
                    recurse => true,
                }

            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

        # 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
                return "%o" % currentvalue
            when Symbol
                return currentvalue
            else
                raise Puppet::DevError, "Invalid current value for mode: #{currentvalue.inspect}"
            end
        end

        def should_to_s(newvalue = @should)
            case newvalue
            when Integer
                return "%o" % newvalue
            when Symbol
                return newvalue
            else
                raise Puppet::DevError, "Invalid 'should' value for mode: #{newvalue.inspect}"
            end
        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
            value = should
            if value.is_a?(String)
                unless value =~ /^\d+$/
                    raise Puppet::Error, "File modes can only be numbers, not #{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 #{old.inspect} to integer"
                end
            end

            return value
        end

        # If we're a directory, we need to be executable for all cases
        # that are readable.  This should probably be selectable, but eh.
        def dirmask(value)
            if FileTest.directory?(@resource[:path])
                value |= 0100 if value & 0400 != 0
                value |= 010 if value & 040 != 0
                value |= 01 if value & 04 != 0
            end

            value
        end

        def insync?(currentvalue)
            if stat = @resource.stat and stat.ftype == "link" and @resource[:links] != :follow
                self.debug "Not managing symlink mode"
                return true
            else
                return super(currentvalue)
            end
        end

        def retrieve
            # If we're not following links and we're a link, then we just turn
            # off mode management entirely.

            if stat = @resource.stat(false)
                unless defined?(@fixed)
                    @should &&= @should.collect { |s| self.dirmask(s) }
                end
                return stat.mode & 007777
            else
                return :absent
            end
        end

        def sync
            mode = self.should

            begin
                File.chmod(mode, @resource[:path])
            rescue => detail
                error = Puppet::Error.new("failed to chmod #{@resource[:path]}: #{detail.message}")
                error.set_backtrace detail.backtrace
                raise error
            end
            :file_changed
        end
    end
end