package fileutils

  1. Overview
  2. Docs

Source file FileUtilMode.ml

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
(******************************************************************************)
(*  ocaml-fileutils: files and filenames common operations                    *)
(*                                                                            *)
(*  Copyright (C) 2003-2014, Sylvain Le Gall                                  *)
(*                                                                            *)
(*  This library is free software; you can redistribute it and/or modify it   *)
(*  under the terms of the GNU Lesser General Public License as published by  *)
(*  the Free Software Foundation; either version 2.1 of the License, or (at   *)
(*  your option) any later version, with the OCaml static compilation         *)
(*  exception.                                                                *)
(*                                                                            *)
(*  This library is distributed in the hope that it will be useful, but       *)
(*  WITHOUT ANY WARRANTY; without even the implied warranty of                *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file         *)
(*  COPYING for more details.                                                 *)
(*                                                                            *)
(*  You should have received a copy of the GNU Lesser General Public License  *)
(*  along with this library; if not, write to the Free Software Foundation,   *)
(*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA             *)
(******************************************************************************)

type who = [`User | `Group | `Other | `All]
type wholist = [ who | `List of who list ]
type permcopy = [`User | `Group | `Other]
type perm = [ `Read | `Write | `Exec | `ExecX | `Sticky | `StickyO ]
type permlist = [ perm | `List of perm list ]
type actionarg = [ permlist | permcopy ]
type action = [ `Set of actionarg | `Add of actionarg | `Remove of actionarg]
type actionlist = [ action | `List of action list ]
type clause = [ `User of actionlist | `Group of actionlist
              | `Other of actionlist | `All of actionlist
              | `None of actionlist ]

type t = clause list


let all_masks =
  [
    `User,  `Sticky,  0o4000;
    `User,  `Exec,    0o0100;
    `User,  `Write,   0o0200;
    `User,  `Read,    0o0400;
    `Group, `Sticky,  0o2000;
    `Group, `Exec,    0o0010;
    `Group, `Write,   0o0020;
    `Group, `Read,    0o0040;
    `Other, `StickyO, 0o1000;
    `Other, `Exec,    0o0001;
    `Other, `Write,   0o0002;
    `Other, `Read,    0o0004;
  ]


let mask =
  let module M =
    Map.Make
      (struct
         type t = who * perm
         let compare = Stdlib.compare
       end)
  in
  let m =
    List.fold_left
      (fun m (who, prm, msk) -> M.add (who, prm) msk m)
      M.empty all_masks
  in
    fun who prm ->
      try
        M.find (who, prm) m
      with Not_found ->
        0


let of_int i =
  let user, group, other =
    List.fold_left
      (fun (user, group, other) (who, perm, mask) ->
         if (i land mask) <> 0 then begin
           match who with
           | `User -> perm :: user, group, other
           | `Group -> user, perm :: group, other
           | `Other -> user, group, perm :: other
         end else begin
           (user, group, other)
         end)
      ([], [], [])
      all_masks
  in
    [`User (`Set (`List user));
     `Group (`Set (`List group));
     `Other (`Set (`List other))]


let to_string =
  let perm =
    function
    | `Read -> "r"
    | `Write -> "w"
    | `Exec -> "x"
    | `Sticky -> "s"
    | `ExecX -> "X"
    | `StickyO -> "t"
  in
  let permlist =
    function
    | `List lst -> String.concat "" (List.map perm lst)
    | #perm as prm -> perm prm
  in
  let permcopy =
    function
    | `User -> "u"
    | `Group -> "g"
    | `Other -> "o"
  in
  let action act =
    let sact, arg =
      match act with
      | `Set arg -> "=", arg
      | `Add arg -> "+", arg
      | `Remove arg -> "-", arg
    in
    let sarg =
      match arg with
      | #permlist as lst -> permlist lst
      | #permcopy as prm -> permcopy prm
    in
      sact^sarg
  in
  let actionlist =
    function
    | `List lst -> String.concat "" (List.map action lst)
    | #action as act -> action act
  in
  let clause cls =
    let swho, lst =
      match cls with
      | `User lst -> "u", lst
      | `Group lst -> "g", lst
      | `Other lst -> "o", lst
      | `All lst -> "a", lst
      | `None lst -> "", lst
    in
      swho^(actionlist lst)
  in
    fun t -> String.concat "," (List.map clause t)


let apply ~is_dir ~umask i (t: t) =
  let set who prm b i =
    let m = mask who prm in
      if b then i lor m else i land (lnot m)
  in
  let get who prm i =
    let m = mask who prm in
      (i land m) <> 0
  in
  let permlist _who i lst =
    List.fold_left
      (fun acc ->
         function
         | `Exec | `Read | `Write | `Sticky | `StickyO as a -> a :: acc
         | `ExecX ->
             if is_dir ||
                List.exists (fun who -> get who `Exec i)
                  [`User; `Group; `Other] then
               `Exec :: acc
             else
               acc)
      []
      (match lst with
       | `List lst -> lst
       | #perm as prm -> [prm])
  in
  let permcopy _who i =
    List.fold_left
      (fun acc (who, prm, _) ->
         if get who prm i then
           prm :: acc
         else
           acc)
      [] all_masks
  in
  let args who i =
    function
    | #permlist as lst -> permlist who i lst
    | #permcopy as who -> permcopy who i
  in
  let rec action who i act =
    match act with
    | `Set arg ->
        action who
          (action who i (`Remove (`List (permcopy who i))))
          (`Add arg)
    | `Add arg ->
        List.fold_left (fun i prm -> set who prm true i) i (args who i arg)
    | `Remove arg ->
        List.fold_left (fun i prm -> set who prm false i) i (args who i arg) 
  in
  let actionlist who i lst =
    match lst with
    | `List lst -> List.fold_left (action who) i lst
    | #action as act -> action who i act
  in
  let actionlist_none i lst =
    let numask = lnot umask in
    let arg_set_if_mask who i arg b =
      List.fold_left
        (fun i prm ->
           if get who prm numask then
             set who prm b i
           else
             i)
        i (args who i arg)
    in
      List.fold_left
        (fun i who ->
           List.fold_left
             (fun i ->
                function
                | `Set _ -> i
                | `Add arg -> arg_set_if_mask who i arg true
                | `Remove arg -> arg_set_if_mask who i arg false)
             i
             (match lst with
              | `List lst -> lst
              | #action as act -> [act]))
        i [`User; `Group; `Other]
  in

  let rec clause i cls =
    match cls with
    | `User lst -> actionlist `User i lst
    | `Group lst -> actionlist `Group i lst
    | `Other lst -> actionlist `Other i lst
    | `All lst -> 
        List.fold_left clause i [`User lst; `Group lst; `Other lst]
    | `None lst -> actionlist_none i lst
  in
    List.fold_left clause i t